Sorry

This feed does not validate.

In addition, interoperability with the widest range of feed readers could be improved by implementing the following recommendations.

Source: https://planet.gnome.org//rss20.xml

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/"><channel><title>Planet GNOME</title><link>https://planet.gnome.org/</link><description>Planet GNOME - https://planet.gnome.org/</description><language>en</language><generator>rfeed v1.1.1</generator><docs>https://github.com/svpino/rfeed/blob/master/README.md</docs><item><title>Pablo Correa Gomez: Retrospective of my term as a Director for the GNOME Foundation</title><link>https://blogs.gnome.org/pabloyoyoista/2025/08/25/retrospective-of-my-term-as-a-director-for-the-gnome-foundation/</link><description>&lt;p&gt;On the spring 2024 I presented my &lt;a class="external" href="https://discourse.gnome.org/t/2024-board-candidate-pablo-correa-gomez/21081/5"&gt;candidature&lt;/a&gt;, and eventually got elected for the Board of Directors at the GNOME Foundation. Directors are elected democratically by the GNOME Foundation members, and positions are usually up for election every 2 years. However, when I started my term there was one position open for a single year (due to some changes in composition of the Board). And I volunteered for it, thus my term expired more or less a month ago. When directors get elected, the information that voters have to decide on their vote is:&lt;/p&gt;
  3. &lt;ul&gt;
  4. &lt;li&gt;Their personal connection to the candidates&lt;/li&gt;
  5. &lt;li&gt;The candidatures &lt;a class="external" href="https://discourse.gnome.org/tag/elections2024"&gt;themselves&lt;/a&gt;&lt;/li&gt;
  6. &lt;li&gt;And further questions the voters might ask about specific topics&lt;/li&gt;
  7. &lt;/ul&gt;
  8. &lt;p&gt;For the shake of transparency and accountability, I am going to discuss my own personal candidature against the work that I contributed to the Board for the last year. Although pretty basic and a self-assessment, I believe this is important for any organization. Without retrospectives, feedback, and public discussion it is very hard to learn lesson, make improvements, and ensure knowledge transfer. Not to mention that public scrutiny and discussion are crucial for any kind of functional democratic governance. Though there are reasons to sometimes not discuss things in public, most of my work for the last year can indeed be discussed in public.&lt;/p&gt;
  9. &lt;h2&gt;Candidature goals&lt;/h2&gt;
  10. &lt;p&gt;I joined the Board to contribute mostly from the economic perspective. I wanted to become part of the Finance Committee to contribute to improve:&lt;/p&gt;
  11. &lt;ul&gt;
  12. &lt;li&gt;The communication about economic and funds-related topics. My goal was to ensure that funds were handled more transparently, and build trust by making sure the information reported was detailed and correct. Avoiding issues like those of which I spoke in a previous &lt;a href="https://blogs.gnome.org/pabloyoyoista/2024/05/19/analysis-of-gnome-foundation-public-economic-concerns-and-thoughts/"&gt;blog post&lt;/a&gt;.&lt;/li&gt;
  13. &lt;li&gt;The way funds were assigned, to make sure those were spent on fulfilling the goals we set to the Foundation.&lt;/li&gt;
  14. &lt;/ul&gt;
  15. &lt;p&gt;Unfortunately, when I joined the Board there were some considerable governance issues still unresolved that we got handed over from the previous year. Given my status as a developer and recent community member based in Europe, I had not had time to build relationships with many people in the community. As such, I also set it as one of my goals to represent those people that I knew, which had been under-represented in the previous board.&lt;/p&gt;
  16. &lt;h2&gt;Goals into actions&lt;/h2&gt;
  17. &lt;p&gt;Let it be said that being part of a governance body for a non-profit in a foreign country with a different history and legal background can be challenging. A non-trivial amount of my time was dedicated to understanding and getting used to such a body: its internal rules, ways of working, and expectations. I wish the governance was closer to home (has anybody in the American continent worked in organizations with a General Assembly?), but that would be a discussion for another time. Now I will stick to discussing actions related to the goals above.&lt;/p&gt;
  18. &lt;h4&gt;Becoming part of the Finance Committee&lt;/h4&gt;
  19. &lt;p&gt;I presented my candidature to become the Treasurer. The Board did a &lt;a class="external" href="https://gitlab.gnome.org/Teams/Board/-/blob/main/board-minutes/2024-07-17.md?ref_type=heads#new-business"&gt;vote&lt;/a&gt;, and instead decided to appoint me as Vice-treasurer. As such, I had no powers, more than the right to attend the Finance Committee meetings, which was enough for me to work on my other self-stated goals. Unfortunately, despite multiple requests from different Board members, and assurances in &lt;a class="external" href="https://gitlab.gnome.org/Teams/Board/-/blob/main/board-minutes/2024-08-09.md#2024-25-regular-meeting-kick-off"&gt;different&lt;/a&gt; &lt;a class="external" href="https://gitlab.gnome.org/Teams/Board/-/blob/main/board-minutes/2024-09-10.md#committee-check-in"&gt;meetings&lt;/a&gt;, I was never called to a Finance Committee meeting. With the &lt;a class="external" href="https://discourse.gnome.org/t/change-to-board-of-directors-roster-and-thanks-to-michael-downey/26352"&gt;resignation&lt;/a&gt; of Michael Downey as a directory during the change of the year, I was then offered the Treasurer position. I declined the offer, since at that point I was not willing to volunteer more of my time for the Foundation.&lt;/p&gt;
  20. &lt;h4&gt;Economic-related communication and funds&lt;/h4&gt;
  21. &lt;p&gt;During the first months of my term I got heavily involved in this aspect. In addition to taking part in discussions and decision-making, I was one of the the main authors of a &lt;a class="external" href="https://discourse.gnome.org/t/update-from-the-board-2024-10/24346"&gt;general economic update&lt;/a&gt; and one focused specifically on the &lt;a class="external" href="https://discourse.gnome.org/t/foundation-2024-2025-budget-and-economic-review/24436"&gt;budget&lt;/a&gt;. It of course took input and effort from multiple directors, but I am confident that my contribution was considerable. Happily, those updates did have an impact on how people saw the Foundation and the project, with some hints that such work could help regain good will and trust from further people in the membership and the wider FOSS community. Those two were concentrated in the first half of my term, and although there are usually more economic news around budget planning, I did not contribute further work on this for the second half of my term.&lt;/p&gt;
  22. &lt;p&gt;Regarding the usage of funds, I was quite involved in the drafting and approval of the budget for 2024/2025. Even if Richard (then Interim Executive Director) took the lion&amp;#8217;s share of the work. Such budget managed to retain spending essential for the fulfilling of the goals (conferences, infra), while at the same time making sure available restricted funds were put to use (parental controls and well-being). Unfortunately, some of the fears I expressed in my candidature were right, and expenses had to be cut considerably, leading to us not being able to support all the previous staffers. In general, those executive decisions were not made by me, but by the Executive Director. However, I am happy my input was taken into consideration, and we managed to have a budget that brought us to 2025.&lt;/p&gt;
  23. &lt;h4&gt;Governance and community engagement&lt;/h4&gt;
  24. &lt;p&gt;Although the governance issues were carried over from a previous board, I tried to apply the best of my judgement in every situation. I did communicate, when the situation allowed, with the community, and sit in countless meetings to both mediate and express my opinion. At some point, when I was not sure I was doing a good job, presented my resignation a considerable amount of people I knew had voted for me, and all considered it would be best for me to stay.&lt;/p&gt;
  25. &lt;h2&gt;Evaluation&lt;/h2&gt;
  26. &lt;p&gt;I put a lot of effort and time moving forward those things I promised in the first half of my term. Unfortunately, I was unable to continue with such engagement through the second half. Part of that had to be with bad management of my time and energy, where I surely burned too much too early. Another part had to be with the realization that many of the problems that I was hoping to tackle at the Foundation are symptoms of deeper structural issues at the project level. For example, no team or committee has official and effective communication channels. So why would the people running the project change their behavior just because being part ofΒ  the Board? I believe I do not have the leverage, skills, or experience to tackle those issues at their core, which strongly reduced my motivation. I am happy, though, that senior members in the community are really &lt;a class="external" href="https://www.bassi.io/articles/2025/08/03/governance-in-gnome/"&gt;trying&lt;/a&gt;.&lt;/p&gt;
  27. &lt;p&gt;I am specially sorry I did not take the Treasurer position when I was offered it. This was less than I expected from myself. I offered my resignation to community members that I knew had voted me, but nobody took it. I also presented my resignation to another Board member which took most of the burden for my decision of not taking the role. It was also politely declined. In hindsight I maybe should have resigned anyway.&lt;/p&gt;
  28. &lt;p&gt;On the positive side, I learned a lot of lessons just by being on the Board, specially related to governance. I take most of those lessons with me to &lt;a class="external" href="https://postmarketos.org/"&gt;postmarketOS&lt;/a&gt;, and I hope they serve as an useful inter-FOSS lesson sharing experiment. They have surely already helped!&lt;/p&gt;
  29. &lt;p&gt;Overall, I am a bit disappointed with myself, as I would have expected to keep doing a lot of the things I did at the beginning for the whole of my term. At the same time, considering the complete year, I think my overall level of work and engagement was probably on average compared to the rest of the Board. In general, the whole experience was considerably draining, specially emotionally, as we were going through uncommon circumstances, e.g: governance issues, staff reduction, etc.&lt;/p&gt;
  30. &lt;h2&gt;Moving forward&lt;/h2&gt;
  31. &lt;p&gt;I do not plan to come back to do governance for the GNOME project for a while. I am very happy with what we are building in postmarketOS: the community and the full-stack and upstreaming commitment we bring with us. I hope the GNOME project and Foundation can transition to more open and structured models of governance, where tracking decisions, communicating regularly, and taking responsibility over good and bad decisions becomes the norm. I hope this posts motivates other directors to write their own retrospectives and hold themselves accountable in public in the future.&lt;/p&gt;
  32. &lt;p&gt;It is also encouraging to see that we have a new Executive Director, Steven, which rapidly understood many of the issues and needs of the project and organization in a similar way than I do. I wish him the best of successes, and thank him for taking over such an endeavor.&lt;/p&gt;
  33. &lt;p&gt;Thanks also to every Board member: Cassidy, Erik, Federico, Julian, Karen, Michael, Philip, Robert, as well as the staff I interacted Rosanna, Kristi, Bart, and Richard. I&amp;#8217;ve seen the passion and effort, and it is truly remarkable. With this, I also want to give a special mention to Allan Day. He&amp;#8217;s worked non-stop during months to decisively contribute to the Foundation staying afloat. Thank you very much for your work, it is unmatched.&lt;/p&gt;
  34. &lt;p&gt;If you have any feedback you want to share, thoughts about my term, improvements to suggest, or just some thanks, feel free to comment or &lt;a href="mailto:pabloyoyoista@postmarketos.org"&gt;email&lt;/a&gt; me. I am always happy to hear other people&amp;#8217;s thoughts, and there&amp;#8217;s no improvement without feedback!&lt;/p&gt;</description><author>Pablo Correa Gomez</author><dc:creator>Pablo Correa Gomez</dc:creator><pubDate>Mon, 25 Aug 2025 12:52:56 GMT</pubDate><guid isPermaLink="true">https://blogs.gnome.org/pabloyoyoista/2025/08/25/retrospective-of-my-term-as-a-director-for-the-gnome-foundation/</guid></item><item><title>Steven Deobald: 2025-08-22 Foundation Update</title><link>https://blogs.gnome.org/steven/2025/08/23/2025-08-22-foundation-update/</link><description>&lt;h2&gt;## Bureaucracy&lt;/h2&gt;
  35. &lt;p&gt;You may have noted that there wasn&amp;#8217;t a Foundation Update last week. This is because almost everything that has been happening at the Foundation lately falls under the banner of &amp;#8220;bureaucracy&amp;#8221; and/or &amp;#8220;administration&amp;#8221; and there isn&amp;#8217;t much to say about either of those topics publicly.&lt;/p&gt;
  36. &lt;p&gt;Also, last Friday was GNOME&amp;#8217;s 28th birthday and no one wants to hear about paper-shuffling on their birthday. &lt;img alt="πŸ™‚" class="wp-smiley" src="https://s.w.org/images/core/emoji/16.0.1/72x72/1f642.png" style="height: 1em;" /&gt;&lt;/p&gt;
  37. &lt;p&gt;I&amp;#8217;m sorry to say there isn&amp;#8217;t much to add this week, either. But I hope you did all take a moment to reflect on the nearly-three-decades-of-work that&amp;#8217;s gone into GNOME last week.&lt;/p&gt;
  38. &lt;p&gt;Happy Belated Birthday, Computer. &lt;img alt="πŸŽ‚" class="wp-smiley" src="https://s.w.org/images/core/emoji/16.0.1/72x72/1f382.png" style="height: 1em;" /&gt;&lt;/p&gt;</description><author>Steven Deobald</author><dc:creator>Steven Deobald</dc:creator><pubDate>Sat, 23 Aug 2025 03:48:40 GMT</pubDate><guid isPermaLink="true">https://blogs.gnome.org/steven/2025/08/23/2025-08-22-foundation-update/</guid></item><item><title>This Week in GNOME: #213 Fixed Rules</title><link>https://thisweek.gnome.org/posts/2025/08/twig-213/</link><description>&lt;p&gt;Update on what happened across the GNOME project in the week from August 15 to August 22.&lt;!--more--&gt;&lt;/p&gt;
  39. &lt;h2 id="gnome-core-apps-and-libraries"&gt;GNOME Core Apps and Libraries&lt;/h2&gt;
  40. &lt;h3 id="glycin"&gt;Glycin &lt;a href="https://gitlab.gnome.org/GNOME/glycin"&gt;β†—&lt;/a&gt;&lt;/h3&gt;
  41. &lt;p&gt;Sandboxed and extendable image loading and editing.&lt;/p&gt;
  42. &lt;p&gt;&lt;a href="https://thisweek.gnome.org/reporters/78a55022fd487dd708fbc8b46a274879ddd95a53c11137fde5bd519ebf5699ab"&gt;Sophie (she/her)&lt;/a&gt; announces&lt;/p&gt;
  43. &lt;blockquote&gt;
  44. &lt;p&gt;Glycin 2.0.beta.3 has been released. Among the important changes are fixes for thumbnailers not working in certain configurations, loading speed for JPEG XL having been dramatically improved, fixed sandbox rules that broke image loading on some systems, and fixed editing for some JPEG images saved in progressive mode.&lt;/p&gt;
  45. &lt;/blockquote&gt;
  46. &lt;h2 id="gnome-circle-apps-and-libraries"&gt;GNOME Circle Apps and Libraries&lt;/h2&gt;
  47. &lt;h3 id="dΓ©jΓ -dup-backups"&gt;DΓ©jΓ  Dup Backups &lt;a href="https://apps.gnome.org/DejaDup/"&gt;β†—&lt;/a&gt;&lt;/h3&gt;
  48. &lt;p&gt;A simple backup tool.&lt;/p&gt;
  49. &lt;p&gt;&lt;a href="https://thisweek.gnome.org/reporters/3c28343ad45b86bbdc6c71d329672cea0fe60f3462f88407527a2822d4d304ba"&gt;Michael Terry&lt;/a&gt; announces&lt;/p&gt;
  50. &lt;blockquote&gt;
  51. &lt;p&gt;DΓ©jΓ  Dup 49.beta was released! It just fixes a few small bugs and uses the new libadwaita shortcuts dialog.&lt;/p&gt;
  52. &lt;p&gt;But if you haven’t tried the 49.x branch yet, it has a big UI refactor and adds file-manager-based restore for Restic backups.&lt;/p&gt;
  53. &lt;p&gt;Read more details and install instructions in the &lt;a href="https://discourse.gnome.org/t/deja-dup-backups-49-alpha-call-for-testing/29710"&gt;previous 49.alpha announcement&lt;/a&gt;. Thanks for any testing you can do before this reaches the masses!&lt;/p&gt;
  54. &lt;/blockquote&gt;
  55. &lt;h2 id="third-party-projects"&gt;Third Party Projects&lt;/h2&gt;
  56. &lt;p&gt;&lt;a href="https://thisweek.gnome.org/reporters/f632d3632503d479a4ea08f942f5a5e513d69f8fa619d4b2748dc5cf163801c3"&gt;Mir Sobhan&lt;/a&gt; announces&lt;/p&gt;
  57. &lt;blockquote&gt;
  58. &lt;p&gt;We forked the TWIG website and forged it into a β€œgood first issue” tracker. It catches all GNOME-related projects on GitHub and GNOME GitLab to show issues labeled β€œgood first issue” or β€œNewcomers.” This can help newcomers find places to contribute including myself.&lt;/p&gt;
  59. &lt;p&gt;Website: &lt;a href="https://ggfi.mirsobhan.ir"&gt;https://ggfi.mirsobhan.ir&lt;/a&gt;
  60. Repo: &lt;a href="https://gitlab.gnome.org/misano/goodfirstissue"&gt;https://gitlab.gnome.org/misano/goodfirstissue&lt;/a&gt;&lt;/p&gt;
  61. &lt;/blockquote&gt;
  62. &lt;p&gt;&lt;a href="https://thisweek.gnome.org/reporters/09eb29c19eec5691c4e8389947fc22a6314a0f3b15c5c492f83a552d8b001106"&gt;Džeremi&lt;/a&gt; says&lt;/p&gt;
  63. &lt;blockquote&gt;
  64. &lt;h2 id="chronograph-gets-a-big-new-40-update"&gt;Chronograph gets a BIG new 4.0 update!&lt;/h2&gt;
  65. &lt;h2 id="what-is-chronograph"&gt;What is Chronograph?&lt;/h2&gt;
  66. &lt;p&gt;&lt;a href="https://flathub.org/apps/io.github.dzheremi2.lrcmake-gtk"&gt;Chronograph&lt;/a&gt; is an app for syncing lyrics, making them display like karaoke in supported players. It comes with a beautiful GTK4 + LibAdwaita interface and includes a built-in metadata editor, so you can manage your music library along with syncing lyrics.
  67. Default LRC files can be published to the large lyrics database &lt;a href="https://lrclib.net"&gt;LRClib.net&lt;/a&gt;, which is widely used by many open-source players to fetch lyrics.
  68. Until now, Chronograph supported only line-by-line lyrics, which was enough for most cases since standard LRC is the most common format.
  69. &lt;em&gt;&lt;strong&gt;But times change…&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;
  70. &lt;h2 id="word-by-word-support"&gt;Word-by-Word support!&lt;/h2&gt;
  71. &lt;p&gt;Starting &lt;strong&gt;August 24th&lt;/strong&gt;, Chronograph will gain support for Word-by-Word syncing. This feature uses the &lt;strong&gt;eLRC&lt;/strong&gt; format (also known as LRC A2 or Enchanted LRC). In eLRC, each word has its own timestamp, allowing players that support it to &lt;em&gt;animate lyrics word-by-word&lt;/em&gt;, giving you a true karaoke experience.
  72. And this is just the beginning: future updates will also bring support for &lt;strong&gt;TTML (Timed Text Markup Language)&lt;/strong&gt;.&lt;/p&gt;
  73. &lt;h3 id="final-notes"&gt;Final notes&lt;/h3&gt;
  74. &lt;p&gt;I hope you’ll enjoy using the latest version of Chronograph, and together we can spread awareness of eLRC to the wider community. Sync lyrics of your loved songs! β™₯️&lt;/p&gt;
  75. &lt;p&gt;&lt;img height="812" src="https://thisweek.gnome.org/_astro/chronograph_library.CMcl3eq5_Z1HOd9M.webp" width="1026" /&gt;&lt;/p&gt;
  76. &lt;p&gt;&lt;img height="807" src="https://thisweek.gnome.org/_astro/chronograph_wbw_edit.L9yNvdA__ZNm1MJ.webp" width="1026" /&gt;&lt;/p&gt;
  77. &lt;p&gt;&lt;img height="807" src="https://thisweek.gnome.org/_astro/chronograph_wbw_sync.ggN6JXua_Z1BsdIp.webp" width="1026" /&gt;&lt;/p&gt;
  78. &lt;/blockquote&gt;
  79. &lt;p&gt;&lt;a href="https://thisweek.gnome.org/reporters/7eb6937cd9d14a3ed2be5dd83e1f7410a353e83d3791a28d6379100c73b34755"&gt;Nathan Perlman&lt;/a&gt; announces&lt;/p&gt;
  80. &lt;blockquote&gt;
  81. &lt;h2 id="rewaita--give-adwaita-some-flavour"&gt;Rewaita β€” Give Adwaita some flavour&lt;/h2&gt;
  82. &lt;p&gt;Hi there, a few weeks ago I released &lt;strong&gt;Rewaita&lt;/strong&gt;, a spiritual successor to &lt;a href="https://github.com/GradienceTeam/Gradience"&gt;Gradience&lt;/a&gt;. With it, you can recolour GTK4/Adwaita apps to popular colour schemes. That’s where the name comes from ~ Re(colour Ad)waita.&lt;/p&gt;
  83. &lt;p&gt;As of v1.0.4, released this week, you can create your own custom colour palettes if the ones we provide don’t suit you, and you can also change the window controls to be either coloured or MacOS-styled.&lt;/p&gt;
  84. &lt;p&gt;You can find it on &lt;a href="https://flathub.org/apps/io.github.swordpuffin.rewaita"&gt;Flathub&lt;/a&gt;, but also in the &lt;a href="https://aur.archlinux.org/packages/rewaita"&gt;AUR&lt;/a&gt; and &lt;a href="https://github.com/NixOS/nixpkgs/pull/435343"&gt;NIXPKGS&lt;/a&gt; (the Nix Package is still under review).&lt;/p&gt;
  85. &lt;p&gt;Rewaita is also going through rapid development, &lt;a href="https://github.com/SwordPuffin/Rewaita"&gt;so any help would be appreciated&lt;/a&gt;, or just leave us a star :). In particular, &lt;a href="https://github.com/SwordPuffin/Rewaita/issues/11"&gt;GTK3 and Cinnamon support&lt;/a&gt; are next up on the chopping block.&lt;/p&gt;
  86. &lt;p&gt;&lt;a href="https://thisweek.gnome.org/screenshot-dark.png"&gt;&lt;/a&gt;&lt;/p&gt;
  87. &lt;p&gt;&lt;a href="https://thisweek.gnome.org/screenshot-light.png"&gt;&lt;/a&gt;&lt;/p&gt;
  88. &lt;/blockquote&gt;
  89. &lt;h2 id="miscellaneous"&gt;Miscellaneous&lt;/h2&gt;
  90. &lt;p&gt;&lt;a href="https://thisweek.gnome.org/reporters/579a4f3527aa9f46798ea4476441fef008f4a13ffad33b4822bae5de56028aa7"&gt;JumpLink&lt;/a&gt; says&lt;/p&gt;
  91. &lt;blockquote&gt;
  92. &lt;p&gt;&lt;strong&gt;ts-for-gir&lt;/strong&gt; - TypeScript bindings for GObject Introspection&lt;/p&gt;
  93. &lt;p&gt;This week we’ve released a major improvement for GObject interface implementation: &lt;strong&gt;Virtual Interface Generation&lt;/strong&gt;.&lt;/p&gt;
  94. &lt;p&gt;Instead of having to implement all methods of a GObject interface, developers can now only implement the virtual methods (&lt;code&gt;vfunc_*&lt;/code&gt;). This matches the actual GObject-Introspection pattern and makes interface implementation much cleaner.&lt;/p&gt;
  95. &lt;p&gt;&lt;strong&gt;Before&lt;/strong&gt; (implement all methods):&lt;/p&gt;
  96. &lt;pre class="astro-code github-dark" style="background-color: #24292e; color: #e1e4e8;" tabindex="0"&gt;&lt;code&gt;&lt;span class="line"&gt;&lt;span style="color: #F97583;"&gt;class&lt;/span&gt;&lt;span style="color: #B392F0;"&gt; CustomPaintable&lt;/span&gt;&lt;span style="color: #F97583;"&gt; implements&lt;/span&gt;&lt;span style="color: #B392F0;"&gt; Gdk&lt;/span&gt;&lt;span style="color: #E1E4E8;"&gt;.&lt;/span&gt;&lt;span style="color: #B392F0;"&gt;Paintable&lt;/span&gt;&lt;span style="color: #E1E4E8;"&gt; {&lt;/span&gt;&lt;/span&gt;
  97. &lt;span class="line"&gt;&lt;span style="color: #6A737D;"&gt;  // Implement all methods manually&lt;/span&gt;&lt;/span&gt;
  98. &lt;span class="line"&gt;&lt;span style="color: #B392F0;"&gt;  get_current_image&lt;/span&gt;&lt;span style="color: #E1E4E8;"&gt;()&lt;/span&gt;&lt;span style="color: #F97583;"&gt;:&lt;/span&gt;&lt;span style="color: #B392F0;"&gt; Gdk&lt;/span&gt;&lt;span style="color: #E1E4E8;"&gt;.&lt;/span&gt;&lt;span style="color: #B392F0;"&gt;Paintable&lt;/span&gt;&lt;span style="color: #E1E4E8;"&gt; { &lt;/span&gt;&lt;span style="color: #F97583;"&gt;...&lt;/span&gt;&lt;span style="color: #E1E4E8;"&gt; }&lt;/span&gt;&lt;/span&gt;
  99. &lt;span class="line"&gt;&lt;span style="color: #B392F0;"&gt;  get_flags&lt;/span&gt;&lt;span style="color: #E1E4E8;"&gt;()&lt;/span&gt;&lt;span style="color: #F97583;"&gt;:&lt;/span&gt;&lt;span style="color: #B392F0;"&gt; Gdk&lt;/span&gt;&lt;span style="color: #E1E4E8;"&gt;.&lt;/span&gt;&lt;span style="color: #B392F0;"&gt;PaintableFlags&lt;/span&gt;&lt;span style="color: #E1E4E8;"&gt; { &lt;/span&gt;&lt;span style="color: #F97583;"&gt;...&lt;/span&gt;&lt;span style="color: #E1E4E8;"&gt; }&lt;/span&gt;&lt;/span&gt;
  100. &lt;span class="line"&gt;&lt;span style="color: #B392F0;"&gt;  get_intrinsic_width&lt;/span&gt;&lt;span style="color: #E1E4E8;"&gt;()&lt;/span&gt;&lt;span style="color: #F97583;"&gt;:&lt;/span&gt;&lt;span style="color: #79B8FF;"&gt; number&lt;/span&gt;&lt;span style="color: #E1E4E8;"&gt; { &lt;/span&gt;&lt;span style="color: #F97583;"&gt;...&lt;/span&gt;&lt;span style="color: #E1E4E8;"&gt; }&lt;/span&gt;&lt;/span&gt;
  101. &lt;span class="line"&gt;&lt;span style="color: #6A737D;"&gt;  // ... and many more&lt;/span&gt;&lt;/span&gt;
  102. &lt;span class="line"&gt;&lt;span style="color: #E1E4E8;"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
  103. &lt;p&gt;&lt;strong&gt;After&lt;/strong&gt; (only virtual methods):&lt;/p&gt;
  104. &lt;pre class="astro-code github-dark" style="background-color: #24292e; color: #e1e4e8;" tabindex="0"&gt;&lt;code&gt;&lt;span class="line"&gt;&lt;span style="color: #F97583;"&gt;class&lt;/span&gt;&lt;span style="color: #B392F0;"&gt; CustomPaintable&lt;/span&gt;&lt;span style="color: #F97583;"&gt; implements&lt;/span&gt;&lt;span style="color: #B392F0;"&gt; Gdk&lt;/span&gt;&lt;span style="color: #E1E4E8;"&gt;.&lt;/span&gt;&lt;span style="color: #B392F0;"&gt;Paintable&lt;/span&gt;&lt;span style="color: #E1E4E8;"&gt;.&lt;/span&gt;&lt;span style="color: #B392F0;"&gt;Interface&lt;/span&gt;&lt;span style="color: #E1E4E8;"&gt; {&lt;/span&gt;&lt;/span&gt;
  105. &lt;span class="line"&gt;&lt;span style="color: #6A737D;"&gt;  // Declare for TypeScript compatibility&lt;/span&gt;&lt;/span&gt;
  106. &lt;span class="line"&gt;&lt;span style="color: #F97583;"&gt;  declare&lt;/span&gt;&lt;span style="color: #FFAB70;"&gt; get_current_image&lt;/span&gt;&lt;span style="color: #F97583;"&gt;:&lt;/span&gt;&lt;span style="color: #B392F0;"&gt; Gdk&lt;/span&gt;&lt;span style="color: #E1E4E8;"&gt;.&lt;/span&gt;&lt;span style="color: #B392F0;"&gt;Paintable&lt;/span&gt;&lt;span style="color: #E1E4E8;"&gt;[&lt;/span&gt;&lt;span style="color: #9ECBFF;"&gt;"get_current_image"&lt;/span&gt;&lt;span style="color: #E1E4E8;"&gt;];&lt;/span&gt;&lt;/span&gt;
  107. &lt;span class="line"&gt;&lt;span style="color: #F97583;"&gt;  declare&lt;/span&gt;&lt;span style="color: #FFAB70;"&gt; get_flags&lt;/span&gt;&lt;span style="color: #F97583;"&gt;:&lt;/span&gt;&lt;span style="color: #B392F0;"&gt; Gdk&lt;/span&gt;&lt;span style="color: #E1E4E8;"&gt;.&lt;/span&gt;&lt;span style="color: #B392F0;"&gt;Paintable&lt;/span&gt;&lt;span style="color: #E1E4E8;"&gt;[&lt;/span&gt;&lt;span style="color: #9ECBFF;"&gt;"get_flags"&lt;/span&gt;&lt;span style="color: #E1E4E8;"&gt;];&lt;/span&gt;&lt;/span&gt;
  108. &lt;span class="line"&gt;&lt;span style="color: #E1E4E8;"&gt;  &lt;/span&gt;&lt;/span&gt;
  109. &lt;span class="line"&gt;&lt;span style="color: #6A737D;"&gt;  // Only implement virtual methods&lt;/span&gt;&lt;/span&gt;
  110. &lt;span class="line"&gt;&lt;span style="color: #B392F0;"&gt;  vfunc_get_current_image&lt;/span&gt;&lt;span style="color: #E1E4E8;"&gt;()&lt;/span&gt;&lt;span style="color: #F97583;"&gt;:&lt;/span&gt;&lt;span style="color: #B392F0;"&gt; Gdk&lt;/span&gt;&lt;span style="color: #E1E4E8;"&gt;.&lt;/span&gt;&lt;span style="color: #B392F0;"&gt;Paintable&lt;/span&gt;&lt;span style="color: #E1E4E8;"&gt; { &lt;/span&gt;&lt;span style="color: #F97583;"&gt;...&lt;/span&gt;&lt;span style="color: #E1E4E8;"&gt; }&lt;/span&gt;&lt;/span&gt;
  111. &lt;span class="line"&gt;&lt;span style="color: #B392F0;"&gt;  vfunc_get_flags&lt;/span&gt;&lt;span style="color: #E1E4E8;"&gt;()&lt;/span&gt;&lt;span style="color: #F97583;"&gt;:&lt;/span&gt;&lt;span style="color: #B392F0;"&gt; Gdk&lt;/span&gt;&lt;span style="color: #E1E4E8;"&gt;.&lt;/span&gt;&lt;span style="color: #B392F0;"&gt;PaintableFlags&lt;/span&gt;&lt;span style="color: #E1E4E8;"&gt; { &lt;/span&gt;&lt;span style="color: #F97583;"&gt;...&lt;/span&gt;&lt;span style="color: #E1E4E8;"&gt; }&lt;/span&gt;&lt;/span&gt;
  112. &lt;span class="line"&gt;&lt;span style="color: #E1E4E8;"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
  113. &lt;p&gt;We’ve created a comprehensive example: &lt;a href="https://github.com/gjsify/ts-for-gir/tree/main/examples/virtual-interface-test"&gt;https://github.com/gjsify/ts-for-gir/tree/main/examples/virtual-interface-test&lt;/a&gt;&lt;/p&gt;
  114. &lt;p&gt;This shows both &lt;code&gt;Gio.ListModel&lt;/code&gt; and &lt;code&gt;Gdk.Paintable&lt;/code&gt; implementations using the new pattern.&lt;/p&gt;
  115. &lt;p&gt;&lt;strong&gt;Release&lt;/strong&gt;: &lt;a href="https://github.com/gjsify/ts-for-gir/releases/tag/4.0.0-beta.35"&gt;v4.0.0-beta.35&lt;/a&gt; and &lt;a href="https://github.com/gjsify/ts-for-gir/releases/tag/4.0.0-beta.36"&gt;v4.0.0-beta.36&lt;/a&gt;&lt;/p&gt;
  116. &lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: Last week we also released &lt;a href="https://github.com/gjsify/ts-for-gir/releases/tag/v4.0.0-beta.34"&gt;v4.0.0-beta.34&lt;/a&gt; which introduced &lt;strong&gt;Advanced Variant Types&lt;/strong&gt; by default, completing the gi.ts integration with enhanced TypeScript support for &lt;code&gt;GLib.Variant.deepUnpack()&lt;/code&gt; and better type inference for GObject patterns.&lt;/p&gt;
  117. &lt;/blockquote&gt;
  118. &lt;h2 id="thats-all-for-this-week"&gt;That’s all for this week!&lt;/h2&gt;
  119. &lt;p&gt;See you next week, and be sure to stop by &lt;a href="https://matrix.to/#/#thisweek:gnome.org"&gt;#thisweek:gnome.org&lt;/a&gt; with updates on your own projects!&lt;/p&gt;</description><author>This Week in GNOME</author><dc:creator>This Week in GNOME</dc:creator><pubDate>Fri, 22 Aug 2025 00:00:00 GMT</pubDate><guid isPermaLink="true">https://thisweek.gnome.org/posts/2025/08/twig-213/</guid></item><item><title>Sebastian Wick: Testing with Portals</title><link>https://blog.sebastianwick.net/posts/testing-with-portals/</link><description>&lt;p&gt;At the Linux App Summit (LAS) in Albania three months ago, I gave a talk about testing in the &lt;a href="https://github.com/flatpak/xdg-desktop-portal"&gt;xdg-desktop-portal&lt;/a&gt; project. There is a &lt;a href="https://www.youtube.com/watch?v=vZIAGmHMcMI"&gt;recording&lt;/a&gt; of the presentation, and the &lt;a href="https://conf.linuxappsummit.org/event/7/contributions/218/attachments/47/81/las-testing-with-portals.pdf"&gt;slides&lt;/a&gt; are available as well.&lt;/p&gt;
  120. &lt;p&gt;To give a quick summary of the work I did:&lt;/p&gt;
  121. &lt;ul&gt;
  122. &lt;li&gt;Revamped the CI&lt;/li&gt;
  123. &lt;li&gt;Reworked and improved the pytest based integration test harness&lt;/li&gt;
  124. &lt;li&gt;Added integration tests for new portals&lt;/li&gt;
  125. &lt;li&gt;Ported over all the existing GLib/C based integration tests&lt;/li&gt;
  126. &lt;li&gt;Support ASAN for detecting memory leaks in the tests&lt;/li&gt;
  127. &lt;li&gt;Made tests pretend to be either a host, Flatpak or Snap app&lt;/li&gt;
  128. &lt;/ul&gt;
  129. &lt;p&gt;The hope I had is that this will result in:&lt;/p&gt;
  130. &lt;ul&gt;
  131. &lt;li&gt;Fewer regressions&lt;/li&gt;
  132. &lt;li&gt;Tests for new features and bug fixes&lt;/li&gt;
  133. &lt;li&gt;More confidence in refactoring&lt;/li&gt;
  134. &lt;li&gt;More activity in the project&lt;/li&gt;
  135. &lt;/ul&gt;
  136. &lt;p&gt;While it’s hard to get definite data on those points, at least some of it seems to have become reality. I have seen an increase in activity (there are other factors to this for sure), and a lot of PRs already come with tests without me even having to ask for it. Canonical is involved again, taking care of the Snap side of things. So far it seems like we didn’t introduce any new regressions, but this usually shows after a new release. The experience of refactoring portals also became a lot better because there is a baseline level of confidence when the tests pass, as well as the possibility to easily bisect issues. Overall I’m already quite happy with the results.&lt;/p&gt;
  137. &lt;p&gt;Two weeks ago, Georges &lt;a href="https://github.com/flatpak/xdg-desktop-portal/pull/1627"&gt;merged&lt;/a&gt; the last piece of what I talked about in the LAS presentation, so we’re finally testing the code paths that are specific to host, Flatpak and Snap applications! I also continued a bit with improving the tests, and now they &lt;a href="https://github.com/flatpak/xdg-desktop-portal/pull/1770"&gt;can be run with Valgrind&lt;/a&gt;, which is super slow and that’s why we’re not doing it in the CI, but it tends to find memory leaks which ASAN does not. With the existing tests, it found &lt;a href="https://github.com/flatpak/xdg-desktop-portal/pull/1770/commits/3d6b2ccbc90e02640d709d0a19c5a634ad62164a"&gt;9 small memory leaks&lt;/a&gt;.&lt;/p&gt;
  138. &lt;p&gt;If you want to improve the Flatpak story, come and contribute to xdg-desktop-portal. It&amp;rsquo;s now easier than ever!&lt;/p&gt;</description><author>Sebastian Wick</author><dc:creator>Sebastian Wick</dc:creator><pubDate>Thu, 21 Aug 2025 23:00:55 GMT</pubDate><guid isPermaLink="true">https://blog.sebastianwick.net/posts/testing-with-portals/</guid></item><item><title>Michael Meeks: 2025-08-20 Wednesday</title><link>https://meeksfamily.uk/~michael/blog/2025-08-20.html</link><description>&lt;ul&gt; &lt;!-- --&gt;
  139. &lt;li&gt;
  140. Up extremely early, worked for a few hours, out for a
  141. run with J. painful left hip.
  142. &lt;/li&gt;
  143. &lt;li&gt;
  144. Merger/finance call, sync with Dave, Gokay &amp;amp; Szymon,
  145. Lunch.
  146. &lt;/li&gt;
  147. &lt;li&gt;
  148. Published the next strip: on Fixed Price projects
  149. &lt;center&gt;
  150. &lt;a href="https://www.collaboraonline.com/torf/torf31"&gt;&lt;img alt="The Open Road to Freedom - strip#31 - Fixed Price" src="https://meeksfamily.uk/~michael/images/torf31-thumb.png" /&gt;&lt;/a&gt;
  151. &lt;/center&gt;
  152. &lt;/li&gt;
  153. &lt;li&gt;
  154. Productivity All Hands meeting.
  155. &lt;/li&gt;
  156. &lt;/ul&gt;</description><author>Michael Meeks</author><dc:creator>Michael Meeks</dc:creator><pubDate>Wed, 20 Aug 2025 13:22:18 GMT</pubDate><guid isPermaLink="true">https://meeksfamily.uk/~michael/blog/2025-08-20.html</guid></item><item><title>Thibault Martin: Cloud tech makes sense on-prem too</title><link>https://ergaster.org/posts/2025/08/20-cloud-tech-on-prem/</link><description>&lt;p&gt;In &lt;a href="https://ergaster.org/posts/2025/08/04-overegineering-homelab/"&gt;the previous post&lt;/a&gt;, we talked about the importance to have a flexible homelab with Proxmox, and set it up. Long story short, I only have a single physical server but I like to experiment with new setups regularly. Proxmox is a baremetal hypervisor: a piece of software that lets me spin up Virtual Machines on top of my server, to act as mini servers.&lt;/p&gt;
  157. &lt;p&gt;Thanks to this set-up I can have a long-lived VM for my (single node) production k3s cluster, and I can spin up disposable VMs to experiment with, without impacting my production.&lt;/p&gt;
  158. &lt;p&gt;But it's more complex to install Proxmox, spin up a VM, and install k3s on it, as compared to just installing Debian and k3s on my baremetal server. We have already automated the Proxmox install process. &lt;strong&gt;Let's now automate the VM provisioning and deploy k3s on it, to make it simple and easy to re-provision a fully functional Virtual Machine on top of Proxmox!&lt;/strong&gt;&lt;/p&gt;
  159. &lt;p&gt;&lt;/p&gt;
  160. &lt;p&gt;In this post we will configure opentofu so it can ask Proxmox to spin up a new VM, use cloud-init to do the basic pre-configuration of the VM, and use ansible to deploy k3s on it.&lt;/p&gt;
  161. &lt;h2&gt;Provisioning and pre-configuring a VM&lt;/h2&gt;
  162. &lt;p&gt;OpenTofu is software I execute on my laptop. It reads file describing what I want to provision, and performs the actual provisioning. I can use it to say "I want a VM with 4 vCPUs and 8GB of RAM on this Proxmox cluster," or "I want to add this A record to my DNS managed by Cloudflare." I need to write this down in &lt;code&gt;.tf&lt;/code&gt; files, and invoke the &lt;code&gt;tofu&lt;/code&gt; CLI to read those files and apply the changes.&lt;/p&gt;
  163. &lt;p&gt;Opentofu is quite flexible. It can connect to many different providers (e.g. Proxmox, AWS, Scaleway, Hetzner...) to spin up a variety of resources (e.g. a VM on Proxmox, an EC2 or EKS instance or AWS, etc). Proxmox, Amazon and other providers publish &lt;em&gt;Provider&lt;/em&gt; plugins for opentofu, available in the &lt;a href="https://search.opentofu.org/"&gt;OpenTofu Registry&lt;/a&gt; (and in the &lt;a href="https://registry.terraform.io/"&gt;Terraform Registry&lt;/a&gt; since opentofu is backward compatible for now).&lt;/p&gt;
  164. &lt;p&gt;&lt;/p&gt;
  165. &lt;h3&gt;Configuring Opentofu for Proxmox&lt;/h3&gt;
  166. &lt;p&gt;To use Opentofu with Proxmox, you need to pick and configure an Opentofu Provider for Proxmox. There seem to be two active implementations:&lt;/p&gt;
  167. &lt;ul&gt;
  168. &lt;li&gt;&lt;a href="https://search.opentofu.org/provider/bpg/proxmox/latest"&gt;bpg/proxmox&lt;/a&gt; is maintained by an individual&lt;/li&gt;
  169. &lt;li&gt;&lt;a href="https://search.opentofu.org/provider/telmate/proxmox/latest"&gt;telmate/proxmox&lt;/a&gt; is maintained by the Telmate organization&lt;/li&gt;
  170. &lt;/ul&gt;
  171. &lt;p&gt;The former seems to have better test coverage, and friends have used it for months without a problem. I am taking a leap of faith and picking it.&lt;/p&gt;
  172. &lt;p&gt;The plugin needs to be configured so opentofu on my laptop can talk to Proxmox and spin up new VMs. To do so, I need to create a Proxmox service account that opentofu will use, so opentofu has sufficient privileges to create the VMs I ask it to create.&lt;/p&gt;
  173. &lt;p&gt;I will rely on the &lt;code&gt;pveum&lt;/code&gt; (Proxmox Virtual Environment User Management) utility to create a role with the right privileges, create a new user/service account, and assign the role to the service account.&lt;/p&gt;
  174. &lt;p&gt;Once ssh'd into the Proxmox host, I can create the terraform user that opentofu will use&lt;/p&gt;
  175. &lt;pre&gt;&lt;code&gt;# pveum user add terraform@pve
  176. &lt;/code&gt;&lt;/pre&gt;
  177. &lt;blockquote&gt;
  178. &lt;p&gt;[!info] I don't have to add a password&lt;/p&gt;
  179. &lt;p&gt;I will issue an API Key for opentofu to authenticate as this user. Not having a password reduces the attack surface by ensuring nobody can use this service account to log into the web UI.&lt;/p&gt;
  180. &lt;/blockquote&gt;
  181. &lt;p&gt;Then let's create the role&lt;/p&gt;
  182. &lt;pre&gt;&lt;code&gt;# pveum role add Terraform -privs "Datastore.Allocate \
  183.    Datastore.AllocateSpace \
  184.    Datastore.AllocateTemplate \
  185.    Datastore.Audit \
  186.    Pool.Allocate \
  187.    Sys.Audit \
  188.    Sys.Console \
  189.    Sys.Modify \
  190.    VM.Allocate \
  191.    VM.Audit \
  192.    VM.Clone \
  193.    VM.Config.CDROM \
  194.    VM.Config.Cloudinit \
  195.    VM.Config.CPU \
  196.    VM.Config.Disk \
  197.    VM.Config.HWType \
  198.    VM.Config.Memory \
  199.    VM.Config.Network \
  200.    VM.Config.Options \
  201.    VM.Console \
  202.    VM.Migrate \
  203.    VM.PowerMgmt \
  204.    SDN.Use"
  205. &lt;/code&gt;&lt;/pre&gt;
  206. &lt;p&gt;And now let's assign the role to the user&lt;/p&gt;
  207. &lt;pre&gt;&lt;code&gt;# pveum aclmod / -user terraform@pve -role Terraform
  208. &lt;/code&gt;&lt;/pre&gt;
  209. &lt;p&gt;Finally I can create an API token&lt;/p&gt;
  210. &lt;pre&gt;&lt;code&gt;# pveum user token add terraform@pve provider --privsep=0
  211. β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  212. β”‚ key          β”‚ value                                β”‚
  213. β•žβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•ͺ══════════════════════════════════════║
  214. β”‚ full-tokenid β”‚ terraform@pve!provider               β”‚
  215. β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
  216. β”‚ info         β”‚ {"privsep":"0"}                      β”‚
  217. β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
  218. β”‚ value        β”‚ REDACTED                             β”‚
  219. β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
  220. &lt;/code&gt;&lt;/pre&gt;
  221. &lt;p&gt;I now have a service account up and ready. Let's create a &lt;code&gt;~/Projects/infra/tofu&lt;/code&gt; folder that will contain my whole infrastructure's opentofu file. In that folder, I will create a &lt;code&gt;providers.tf&lt;/code&gt; file to declare and configure the various providers I need. For now, this will only be Proxmox. I can configure my Proxmox provider so it knows where the API endpoint is, and what API key to use.&lt;/p&gt;
  222. &lt;pre&gt;&lt;code&gt;terraform {
  223.  required_providers {
  224.    proxmox = {
  225.      source = "bpg/proxmox"
  226.      version = "0.80.0"
  227.    }
  228.  }
  229. }
  230.  
  231. provider "proxmox" {
  232.  endpoint = "https://192.168.1.220:8006/"
  233.  api_token = "terraform@pve!provider=REDACTED"
  234. }
  235. &lt;/code&gt;&lt;/pre&gt;
  236. &lt;p&gt;For some operations, including VM provisioning, the API is not enough and the Proxmox provider needs to ssh into the Proxmox host to issue commands. I can configure the Proxmox provider to use my ssh agent.&lt;/p&gt;
  237. &lt;p&gt;This way, when I call the &lt;code&gt;tofu&lt;/code&gt; command on my laptop to provision VMs on the Proxmox host, the provider will use the ssh-agent of my laptop to authenticate against the Proxmox host. This will make opentofu use my ssh keypair to authenticate. Since my ssh key is already trusted by the Proxmox host, opentofu will be able to log in seamlessly.&lt;/p&gt;
  238. &lt;pre&gt;&lt;code&gt;provider "proxmox" {
  239.  endpoint  = "https://192.168.1.220:8006/"
  240.  api_token = "terraform@pve!provider=REDACTED"
  241.  insecure  = true
  242.  
  243.  ssh {
  244.    agent    = true
  245.    username = "root"
  246.  }
  247. }
  248. &lt;/code&gt;&lt;/pre&gt;
  249. &lt;blockquote&gt;
  250. &lt;p&gt;[!warning] Insecure but still somewhat secure&lt;/p&gt;
  251. &lt;p&gt;We add an &lt;code&gt;insecure&lt;/code&gt; line to our configuration. It instructs opentofu to skip the TLS verification of the certificate presented by the Proxmox host. We do this because Proxmox generates a self-signed certificate our computer doesn't trust. We will understand what this means and fix that in a further blog post.&lt;/p&gt;
  252. &lt;p&gt;The main risk we're facing by doing so is to let another machine impersonate our Proxmox host. Since we're working on a homelab, in a home network, the chances of it happening are extraordinarily low, and this it can be considered a temporarily acceptable.&lt;/p&gt;
  253. &lt;/blockquote&gt;
  254. &lt;p&gt;After moving to the &lt;code&gt;tofu&lt;/code&gt; directory, running &lt;code&gt;tofu init&lt;/code&gt; will install the provider&lt;/p&gt;
  255. &lt;pre&gt;&lt;code&gt;$ tofu init
  256.  
  257. Initializing the backend...
  258.  
  259. Initializing provider plugins...
  260. - Finding bpg/proxmox versions matching "0.80.0"...
  261. - Installing bpg/proxmox v0.80.0...
  262. - Installed bpg/proxmox v0.80.0 (signed, key ID F0582AD6AE97C188)
  263. &lt;/code&gt;&lt;/pre&gt;
  264. &lt;p&gt;And a &lt;code&gt;tofu plan&lt;/code&gt; shouldn't return an error&lt;/p&gt;
  265. &lt;pre&gt;&lt;code&gt;$ tofu plan
  266.  
  267. No changes. Your infrastructure matches the configuration.
  268.  
  269. OpenTofu has compared your real infrastructure against your configuration and found no
  270. differences, so no changes are needed.
  271. &lt;/code&gt;&lt;/pre&gt;
  272. &lt;h3&gt;Removing sensitive information&lt;/h3&gt;
  273. &lt;p&gt;If you made it so far, you probably think I am completely reckless for storing credentials in plain text files, and you would be correct to think so. Credentials should never be stored in plain text. Fortunately opentofu can grab sensitive credentials from environment variables.&lt;/p&gt;
  274. &lt;p&gt;I use Bitwarden to store my production credentials and pass them to opentofu when I step into my work directory. You can find all the details on how to do it on &lt;a href="https://ergaster.org/posts/2025/07/28-direnv-bitwarden-integration/"&gt;this previous blog post&lt;/a&gt;. Bear in mind that this works well for a homelab but I wouldn't recommend it for a production setup.&lt;/p&gt;
  275. &lt;p&gt;We need to create a new credential in the &lt;code&gt;Infra&lt;/code&gt; folder of our vault, and call it &lt;code&gt;PROXMOX_VE_API_TOKEN&lt;/code&gt;. Its content is the following&lt;/p&gt;
  276. &lt;pre&gt;&lt;code&gt;terraform@pve!provider=yourApiKeyGoesHere
  277. &lt;/code&gt;&lt;/pre&gt;
  278. &lt;p&gt;Then we need to sync the vault managed by the bitwarden CLI, to ensure it has the credential we just added.&lt;/p&gt;
  279. &lt;pre&gt;&lt;code&gt;$ bw sync
  280. Syncing complete.
  281. &lt;/code&gt;&lt;/pre&gt;
  282. &lt;p&gt;Let's update our &lt;code&gt;~/Projects/infra/.direnv&lt;/code&gt; to make it retrieve the &lt;code&gt;PROXMOX_VE_API_TOKEN&lt;/code&gt; environment variable when we step into our work directory.&lt;/p&gt;
  283. &lt;pre&gt;&lt;code&gt;bitwarden_password_to_env Infra PROXMOX_VE_API_TOKEN
  284. &lt;/code&gt;&lt;/pre&gt;
  285. &lt;p&gt;And let's make direnv allow it&lt;/p&gt;
  286. &lt;pre&gt;&lt;code&gt;$ direnv allow ~/Projects/infra/
  287. &lt;/code&gt;&lt;/pre&gt;
  288. &lt;p&gt;We can now remove the credential from &lt;code&gt;tofu/providers.tf&lt;/code&gt;&lt;/p&gt;
  289. &lt;pre&gt;&lt;code&gt;provider "proxmox" {
  290.  endpoint  = "https://192.168.1.220:8006/"
  291.  api_token = "terraform@pve!provider=REDACTED"
  292.  insecure  = true
  293.  
  294.  ssh {
  295.    agent    = true
  296.    username = "root"
  297.  }
  298. }
  299. &lt;/code&gt;&lt;/pre&gt;
  300. &lt;h3&gt;Spinning up a new VM&lt;/h3&gt;
  301. &lt;p&gt;Now I have a working proxmox provider for opentofu, it's time to spin up a first VM! I already use Debian for my Proxmox host, I'm familiar with Debian, it's very stable, and it has a reactive security team. I want to keep track of as few operating systems (OS) as possible, so whenever possible I will use it as the base OS for my VMs.&lt;/p&gt;
  302. &lt;p&gt;When I spin up a new VM, I can also pre-configure a few settings with &lt;a href="https://cloud-init.io/"&gt;cloud-init&lt;/a&gt;. Cloud-init defines standard files that my VM will read on first boot. Those files contain various instructions: I can use them to give a static IP to my VM, create a user, add it to the sudoers without a password, and add a ssh key to let me perform key-based authentication with ssh.&lt;/p&gt;
  303. &lt;p&gt;I need to use a "cloud image" of Debian for it to support cloud-init file. I can grab the link on &lt;a href="https://www.debian.org/distrib/"&gt;Debian's official Download page&lt;/a&gt;. I could upload it manually to Proxmox, but we're here to make things tidy and reproducible! So let's create a &lt;code&gt;tofu/cloud-images.tf&lt;/code&gt; file where we will tell opentofu to ask the Proxmox node to download the file.&lt;/p&gt;
  304. &lt;pre&gt;&lt;code&gt;resource "proxmox_virtual_environment_download_file" "debian_13_cloud_image" {
  305.  content_type = "iso"
  306.  datastore_id = "local"
  307.  node_name    = "proximighty"
  308.  url          = "https://cloud.debian.org/images/cloud/trixie/latest/debian-13-generic-amd64.qcow2"
  309.  file_name    = "debian-13-generic-amd64.img"
  310. }
  311. &lt;/code&gt;&lt;/pre&gt;
  312. &lt;blockquote&gt;
  313. &lt;p&gt;[!info] No include needed!&lt;/p&gt;
  314. &lt;p&gt;Opentofu merges all the files in the root of a directory into a single file before processing it. There is no need to include/import our &lt;code&gt;tofu/providers.tf&lt;/code&gt; file into &lt;code&gt;tofu/cloud-images.tf&lt;/code&gt;!&lt;/p&gt;
  315. &lt;/blockquote&gt;
  316. &lt;p&gt;Let's run a &lt;code&gt;tofu plan&lt;/code&gt; to see what opentofu would do.&lt;/p&gt;
  317. &lt;pre&gt;&lt;code&gt;$ tofu plan
  318. OpenTofu used the selected providers to generate the following execution plan. Resource actions
  319. are indicated with the following symbols:
  320.  + create
  321.  
  322. OpenTofu will perform the following actions:
  323.  
  324.  # proxmox_virtual_environment_download_file.debian_13_cloud_image will be created
  325.  + resource "proxmox_virtual_environment_download_file" "debian_13_cloud_image" {
  326.      + content_type        = "iso"
  327.      + datastore_id        = "local"
  328.      + file_name           = "debian-13-generic-amd64.img"
  329.      + id                  = (known after apply)
  330.      + node_name           = "proximighty"
  331.      + overwrite           = true
  332.      + overwrite_unmanaged = false
  333.      + size                = (known after apply)
  334.      + upload_timeout      = 600
  335.      + url                 = "https://cloud.debian.org/images/cloud/trixie/latest/debian-13-generic-amd64.qcow2"
  336.      + verify              = true
  337.    }
  338.  
  339. Plan: 1 to add, 0 to change, 0 to destroy.
  340. &lt;/code&gt;&lt;/pre&gt;
  341. &lt;p&gt;Everything looks alright, let's apply it to actually make the Proxmox host download the image!&lt;/p&gt;
  342. &lt;pre&gt;&lt;code&gt;$ tofu apply
  343.  
  344. OpenTofu used the selected providers to generate the following execution plan. Resource actions
  345. are indicated with the following symbols:
  346.  + create
  347.  
  348. OpenTofu will perform the following actions:
  349.  
  350.  # proxmox_virtual_environment_download_file.debian_13_cloud_image will be created
  351.  + resource "proxmox_virtual_environment_download_file" "debian_13_cloud_image" {
  352.      + content_type        = "iso"
  353.      + datastore_id        = "local"
  354.      + file_name           = "debian-13-generic-amd64.img"
  355.      + id                  = (known after apply)
  356.      + node_name           = "proximighty"
  357.      + overwrite           = true
  358.      + overwrite_unmanaged = false
  359.      + size                = (known after apply)
  360.      + upload_timeout      = 600
  361.      + url                 = "https://cloud.debian.org/images/cloud/trixie/latest/debian-13-generic-amd64.qcow2"
  362.      + verify              = true
  363.    }
  364.  
  365. Plan: 1 to add, 0 to change, 0 to destroy.
  366.  
  367. Do you want to perform these actions?
  368.  OpenTofu will perform the actions described above.
  369.  Only 'yes' will be accepted to approve.
  370.  
  371.  Enter a value: yes
  372.  
  373. proxmox_virtual_environment_download_file.debian_13_cloud_image: Creating...
  374. proxmox_virtual_environment_download_file.debian_13_cloud_image: Creation complete after 8s [id=local:iso/debian-13-generic-amd64.img]
  375.  
  376. Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
  377. &lt;/code&gt;&lt;/pre&gt;
  378. &lt;p&gt;Looking at the Proxmox UI I can see that the image has indeed been downloaded&lt;/p&gt;
  379. &lt;p&gt;&lt;/p&gt;
  380. &lt;p&gt;Excellent! Now we can describe the parameters of the virtual machine we wan to create by creating a &lt;code&gt;tofu/k3s-main.tf&lt;/code&gt; file that contains a &lt;code&gt;virtual_environment_vm&lt;/code&gt; resource like so&lt;/p&gt;
  381. &lt;pre&gt;&lt;code&gt;resource "proxmox_virtual_environment_vm" "k3s-main" {
  382.  name        = "k3s-main"
  383.  description = "Production k3s' main VM"
  384.  tags        = ["production", "k3s", "debian"]
  385.  node_name   = "proximighty"
  386. }
  387. &lt;/code&gt;&lt;/pre&gt;
  388. &lt;p&gt;This is the meta-data of our VM, giving it a name and a Proxmox node to run onto. But we need to be more specific. Let's give it 4 CPUs, and 16 GB of RAM.&lt;/p&gt;
  389. &lt;pre&gt;&lt;code&gt;resource "proxmox_virtual_environment_vm" "k3s-main" {
  390.  name        = "k3s-main"
  391.  description = "Production k3s' main VM"
  392.  tags        = ["production", "k3s", "debian"]
  393.  node_name   = "proximighty"
  394.  
  395.  cpu {
  396.    cores = 4
  397.    type  = "x86-64-v4"
  398.  }
  399.  
  400.  memory {
  401.    dedicated = 16384
  402.    floating  = 16384 # set equal to dedicated to enable ballooning
  403.  }
  404. }
  405. &lt;/code&gt;&lt;/pre&gt;
  406. &lt;p&gt;To figure out the type of cpu to use for your VM, issue the following command on the Proxmox host&lt;/p&gt;
  407. &lt;pre&gt;&lt;code&gt;$ /lib64/ld-linux-x86-64.so.2 --help
  408. [...]
  409. Subdirectories of glibc-hwcaps directories, in priority order:
  410.  x86-64-v4 (supported, searched)
  411.  x86-64-v3 (supported, searched)
  412.  x86-64-v2 (supported, searched)
  413. [...]
  414. &lt;/code&gt;&lt;/pre&gt;
  415. &lt;p&gt;We can then give it a 50 GB disk with the &lt;code&gt;disk&lt;/code&gt; block.&lt;/p&gt;
  416. &lt;pre&gt;&lt;code&gt;resource "proxmox_virtual_environment_vm" "k3s-main" {
  417.  name        = "k3s-main"
  418.  
  419. [...]
  420.  
  421.  disk {
  422.    datastore_id = "local"
  423.    interface    = "virtio0"
  424.    iothread     = true
  425.    size         = 50
  426.    file_id      = proxmox_virtual_environment_download_file.debian_13_cloud_image.id
  427.  }
  428. }
  429. &lt;/code&gt;&lt;/pre&gt;
  430. &lt;p&gt;I use the &lt;code&gt;local&lt;/code&gt;  datastore and not &lt;code&gt;lvm-thin&lt;/code&gt; despite running QEMU because I don't want to allow over-provisioning. &lt;code&gt;lvm-thin&lt;/code&gt; would allow me to allocate a disk of 500 GB to my VM, even if I only have 100 GB available, because the VM will only fill the Proxmox drive with the actual content it uses. You can read more about storage on &lt;a href="https://pve.proxmox.com/wiki/Storage"&gt;Proxmox's wiki&lt;/a&gt;.&lt;/p&gt;
  431. &lt;p&gt;I use a &lt;code&gt;virtio&lt;/code&gt; device, since a colleague told me "virtio uses a special communication channel that requires guest drivers, that are well supported out of the box on Linux. You can be way faster when the guest knows it's a VM and don't have to emulate something that was intended for actual real hardware. It's the same for your network interface and a bunch of other things. &lt;strong&gt;Usually if there is a virtio option you want to use that&lt;/strong&gt;"&lt;/p&gt;
  432. &lt;p&gt;I set the &lt;code&gt;file_id&lt;/code&gt; to the Debian cloud image we downloaded earlier.&lt;/p&gt;
  433. &lt;p&gt;I can then add a network interface that will use the &lt;code&gt;vmbr0&lt;/code&gt; bridge I created when setting up my Proxmox host. I also need an empty &lt;code&gt;serial_device&lt;/code&gt;, or Debian crashes.&lt;/p&gt;
  434. &lt;pre&gt;&lt;code&gt;resource "proxmox_virtual_environment_vm" "k3s-main" {
  435.  name        = "k3s-main"
  436.  
  437. [...]
  438.  
  439.  network_device {
  440.    bridge = "vmbr0"
  441.  }
  442.  
  443.  serial_device {}
  444. }
  445. &lt;/code&gt;&lt;/pre&gt;
  446. &lt;p&gt;Now, instead of spinning up an un-configured VM and manually retrieving the parameters set during boot, we will use cloud-init to pre-configure it. We will do the following:&lt;/p&gt;
  447. &lt;ul&gt;
  448. &lt;li&gt;Configure the network to get a static IP&lt;/li&gt;
  449. &lt;li&gt;Configure the hostname&lt;/li&gt;
  450. &lt;li&gt;Configure the timezone to UTC&lt;/li&gt;
  451. &lt;li&gt;Add a user &lt;code&gt;thib&lt;/code&gt; that &lt;em&gt;doesn't&lt;/em&gt; have a password&lt;/li&gt;
  452. &lt;li&gt;Add &lt;code&gt;thib&lt;/code&gt; to sudoers, without a password&lt;/li&gt;
  453. &lt;li&gt;Add my the public ssh key from my laptop to the trust keys of &lt;code&gt;thib&lt;/code&gt; on the VM, so I can login with a ssh key and never have to use a password.&lt;/li&gt;
  454. &lt;/ul&gt;
  455. &lt;p&gt;The documentation of the Proxmox provider teach us that that &lt;a href="https://search.opentofu.org/provider/bpg/proxmox/latest/docs/guides/cloud-init#native-proxmox-cloud-init-support"&gt;Proxmox has native support for cloud-init&lt;/a&gt;. This cloud-init configuration is done in the &lt;code&gt;initialization&lt;/code&gt; block of the &lt;a href="https://search.opentofu.org/provider/bpg/proxmox/latest/docs/resources/virtual_environment_vm"&gt;&lt;code&gt;virtual_environment_vm&lt;/code&gt;&lt;/a&gt; resource.&lt;/p&gt;
  456. &lt;p&gt;We will first give it an IP&lt;/p&gt;
  457. &lt;pre&gt;&lt;code&gt;resource "proxmox_virtual_environment_vm" "k3s-main" {
  458.  name        = "k3s-main"
  459.  
  460. [...]
  461.  
  462.  initialization {
  463.    datastore_id = "local"
  464.  
  465.    ip_config {
  466.      ipv4 {
  467.        address = "192.168.1.221/24"
  468.        gateway = "192.168.1.254"
  469.      }
  470.    }
  471.  }
  472. }
  473. &lt;/code&gt;&lt;/pre&gt;
  474. &lt;p&gt;I'm only specifying the &lt;code&gt;datastore_id&lt;/code&gt; because by default it uses &lt;code&gt;local-lvm&lt;/code&gt;, which I have not configured on my Proxmox host.&lt;/p&gt;
  475. &lt;p&gt;To create a user and give it ssh keys I could use the &lt;code&gt;user_account&lt;/code&gt; block inside &lt;code&gt;initialization&lt;/code&gt;. Unfortunately it doesn't support adding the user to sudoers, nor installing extra packages. To circumvent that limitation I will have to &lt;a href="https://cloudinit.readthedocs.io/en/latest/explanation/format.html#cloud-config-data"&gt;create a user config data file&lt;/a&gt; and pass it to cloud-init.&lt;/p&gt;
  476. &lt;p&gt;Let's start by creating the user config data file resource within &lt;code&gt;tofu/k3s-main.tf&lt;/code&gt;&lt;/p&gt;
  477. &lt;pre&gt;&lt;code&gt;resource "proxmox_virtual_environment_file" "user_data_cloud_config" {
  478.  content_type = "snippets"
  479.  datastore_id = "local"
  480.  node_name    = "proximighty"
  481.  
  482.  source_raw {
  483.    data = &amp;lt;&amp;lt;-EOF
  484.    #cloud-config
  485.    hostname: mightykube
  486.    timezone: UTC
  487.    users:
  488.      - default
  489.      - name: thib
  490.        lock_passwd: true
  491.        groups:
  492.          - sudo
  493.        shell: /bin/bash
  494.        ssh_authorized_keys:
  495.          - ${trimspace("ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIGC++vbMTrSbQFKFgthj9oLaW1z5fCkQtlPCnG6eObB thib@ergaster.org")}
  496.        sudo: ALL=(ALL) NOPASSWD:ALL
  497.      - name: root
  498.        lock_passwd: true
  499.    package_update: true
  500.    package_upgrade: true
  501.    packages:
  502.      - htop
  503.      - qemu-guest-agent
  504.      - vim
  505.    runcmd:
  506.      - systemctl enable qemu-guest-agent
  507.      - systemctl start qemu-guest-agent
  508.      - echo "done" &amp;gt; /tmp/cloud-config.done
  509.    EOF
  510.  
  511.    file_name = "user-data-cloud-config.yaml"
  512.  }
  513. }
  514. &lt;/code&gt;&lt;/pre&gt;
  515. &lt;p&gt;It's a bit inelegant to keep a copy of my ssh key inside this file. Let's ask opentofu to read it from the actual file on my laptop instead by creating a &lt;code&gt;local_file&lt;/code&gt; resource for it, in &lt;code&gt;tofu/k3s.tf&lt;/code&gt;&lt;/p&gt;
  516. &lt;pre&gt;&lt;code&gt;data "local_file" "ssh_public_key" {
  517.  filename = "/Users/thibaultmartin/.ssh/id_ed25519.pub"
  518. }
  519. &lt;/code&gt;&lt;/pre&gt;
  520. &lt;p&gt;If I try to plan the change, I get the following error&lt;/p&gt;
  521. &lt;pre&gt;&lt;code&gt;$ tofu plan
  522. β•·
  523. β”‚ Error: Inconsistent dependency lock file
  524. β”‚
  525. β”‚ The following dependency selections recorded in the lock file are inconsistent with the
  526. β”‚ current configuration:
  527. β”‚   - provider registry.opentofu.org/hashicorp/local: required by this configuration but no version is selected
  528. β”‚
  529. β”‚ To update the locked dependency selections to match a changed configuration, run:
  530. β”‚   tofu init -upgrade
  531. &lt;/code&gt;&lt;/pre&gt;
  532. &lt;p&gt;Like the error message says, I can fix it with &lt;code&gt;tofu init&lt;/code&gt;&lt;/p&gt;
  533. &lt;pre&gt;&lt;code&gt;$ tofu init -upgrade
  534. Initializing the backend...
  535.  
  536. Initializing provider plugins...
  537. - Finding opentofu/cloudflare versions matching "5.7.1"...
  538. - Finding bpg/proxmox versions matching "0.80.0"...
  539. - Finding latest version of hashicorp/local...
  540. - Installing hashicorp/local v2.5.3...
  541. - Installed hashicorp/local v2.5.3 (signed, key ID 0C0AF313E5FD9F80)
  542. - Using previously-installed opentofu/cloudflare v5.7.1
  543. - Using previously-installed bpg/proxmox v0.80.0
  544.  
  545. Providers are signed by their developers.
  546. If you'd like to know more about provider signing, you can read about it here:
  547. https://opentofu.org/docs/cli/plugins/signing/
  548.  
  549. OpenTofu has made some changes to the provider dependency selections recorded
  550. in the .terraform.lock.hcl file. Review those changes and commit them to your
  551. version control system if they represent changes you intended to make.
  552.  
  553. OpenTofu has been successfully initialized!
  554. [...]
  555. &lt;/code&gt;&lt;/pre&gt;
  556. &lt;p&gt;I can now change the &lt;code&gt;user_data_cloud_config&lt;/code&gt; resource to reference &lt;code&gt;ssh_public_key&lt;/code&gt;&lt;/p&gt;
  557. &lt;pre&gt;&lt;code&gt;resource "proxmox_virtual_environment_file" "user_data_cloud_config" {
  558. [...]
  559.        ssh_authorized_keys:
  560.          - ${trimspace("ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIGC++vbMTrSbQFKFgthj9oLaW1z5fCkQtlPCnG6eObB thib@ergaster.org")}
  561.          - ${trimspace(data.local_file.ssh_public_key.content)}
  562. [...]
  563. }
  564. &lt;/code&gt;&lt;/pre&gt;
  565. &lt;p&gt;Now let's update the &lt;code&gt;initialization&lt;/code&gt; block of &lt;code&gt;k3s-main&lt;/code&gt; to use that cloud-init file&lt;/p&gt;
  566. &lt;pre&gt;&lt;code&gt;resource "proxmox_virtual_environment_vm" "k3s-main" {
  567.  name        = "k3s-main"
  568.  
  569. [...]
  570.  
  571.  initialization {
  572.    datastore_id = "local"
  573.  
  574.    ip_config {
  575.      ipv4 {
  576.        address = "192.168.1.221/24"
  577.        gateway = "192.168.1.254"
  578.      }
  579.    }
  580.  
  581.    user_data_file_id = proxmox_virtual_environment_file.user_data_cloud_config.id
  582.  }
  583. }
  584. &lt;/code&gt;&lt;/pre&gt;
  585. &lt;p&gt;We can check with &lt;code&gt;tofu plan&lt;/code&gt; that everything is alright, and then actually apply the plan with &lt;code&gt;tofu apply&lt;/code&gt;&lt;/p&gt;
  586. &lt;pre&gt;&lt;code&gt;$ tofu apply
  587. data.local_file.ssh_public_key: Reading...
  588. data.local_file.ssh_public_key: Read complete after 0s [id=930cea05ae5e662573618e0d9f3e03920196cc5f]
  589. proxmox_virtual_environment_file.user_data_cloud_config: Refreshing state... [id=local:snippets/user-data-cloud-config.yaml]
  590. proxmox_virtual_environment_download_file.debian_13_cloud_image: Refreshing state... [id=local:iso/debian-13-generic-amd64.img]
  591.  
  592. OpenTofu used the selected providers to generate the following execution plan.
  593. Resource actions are indicated with the following symbols:
  594.  + create
  595.  
  596. OpenTofu will perform the following actions:
  597.  
  598.  # proxmox_virtual_environment_vm.k3s-main will be created
  599.  + resource "proxmox_virtual_environment_vm" "k3s-main" {
  600.    [...]
  601.    }
  602.  
  603. Plan: 1 to add, 0 to change, 0 to destroy.
  604.  
  605. Do you want to perform these actions?
  606.  OpenTofu will perform the actions described above.
  607.  Only 'yes' will be accepted to approve.
  608.  
  609.  Enter a value: yes
  610.  
  611. proxmox_virtual_environment_vm.k3s-main: Creating...
  612. proxmox_virtual_environment_vm.k3s-main: Creation complete after 5s [id=100]
  613.  
  614. Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
  615. &lt;/code&gt;&lt;/pre&gt;
  616. &lt;p&gt;Looking at Proxmox's console, I can see that the VM was created, it healthy, and I can even see that it has the &lt;code&gt;mightykube&lt;/code&gt; hostname I had created for it.&lt;/p&gt;
  617. &lt;p&gt;&lt;/p&gt;
  618. &lt;p&gt;Now I can try to ssh into the newly created VM from my laptop&lt;/p&gt;
  619. &lt;pre&gt;&lt;code&gt;$ ssh thib@192.168.1.221
  620. The authenticity of host '192.168.1.221 (192.168.1.221)' can't be established.
  621. ED25519 key fingerprint is SHA256:39Qocnshj+JMyt4ABpD9ZIjDpOHhXqdet94QeSh+uDo.
  622. This key is not known by any other names.
  623. Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
  624. Warning: Permanently added '192.168.1.221' (ED25519) to the list of known hosts.
  625. Linux mightykube 6.12.41+deb13-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.12.41-1 (2025-08-12) x86_64
  626.  
  627. The programs included with the Debian GNU/Linux system are free software;
  628. the exact distribution terms for each program are described in the
  629. individual files in /usr/share/doc/*/copyright.
  630.  
  631. Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
  632. permitted by applicable law.
  633. thib@mightykube:~$
  634. &lt;/code&gt;&lt;/pre&gt;
  635. &lt;p&gt;Let's check that I can perform actions as root without being prompted for a password&lt;/p&gt;
  636. &lt;pre&gt;&lt;code&gt;thib@mightykube:~$ sudo apt update
  637. Get:1 file:/etc/apt/mirrors/debian.list Mirrorlist [30 B]
  638. Get:2 file:/etc/apt/mirrors/debian-security.list Mirrorlist [39 B]
  639. Hit:3 https://deb.debian.org/debian trixie InRelease
  640. Hit:4 https://deb.debian.org/debian trixie-updates InRelease
  641. Hit:5 https://deb.debian.org/debian trixie-backports InRelease
  642. Hit:6 https://deb.debian.org/debian-security trixie-security InRelease
  643. Reading package lists... Done
  644. Building dependency tree... Done
  645. Reading state information... Done
  646. All packages are up to date.
  647. &lt;/code&gt;&lt;/pre&gt;
  648. &lt;p&gt;Brilliant! Just like that, I have a VM on Proxmox, with a static IP address, a well-known user, ssh key authentication and no password to manage at all!&lt;/p&gt;
  649. &lt;blockquote&gt;
  650. &lt;p&gt;[!warning] No password means no password!&lt;/p&gt;
  651. &lt;p&gt;When creating the VM, cloud-init creates a user but doesn't give it a password. It means we can only rely on SSH to control the VM. If we lose our SSH key or mess up with the sshd config and can't ssh into the VM, we're (kind of) locked out!&lt;/p&gt;
  652. &lt;p&gt;We have access to the VM console via Proxmox, but without a password we can't log into it. It is possible to rescue it by booting a live system and chrooting into our actual system, but it can be tedious. We'll cover that in a future blog post.&lt;/p&gt;
  653. &lt;/blockquote&gt;
  654. &lt;p&gt;We still have credentials in our &lt;code&gt;.tf&lt;/code&gt; files so we can't commit them yet. We will extract the credentials a bit later, but first let's refactor our files for clarity.&lt;/p&gt;
  655. &lt;h3&gt;A single place to attribute IPs&lt;/h3&gt;
  656. &lt;p&gt;Since all my VMs will get a static IP, I want to make sure I keep a tidy list of all the IPs already used. This will help avoid IP clashes. Let's create a new &lt;code&gt;ips.tf&lt;/code&gt; file to keep track of everything&lt;/p&gt;
  657. &lt;pre&gt;&lt;code&gt;locals {
  658.  reserved_ips = {
  659.    proxmox_host = "192.168.1.220/24"
  660.    k3s_main     = "192.168.1.221/24"
  661.    gateway      = "192.168.1.254"
  662.  }
  663. }
  664. &lt;/code&gt;&lt;/pre&gt;
  665. &lt;p&gt;When spinning up the VM for the main k3s node, I will be able to refer to the &lt;code&gt;local.reserved_ips.k3s_main&lt;/code&gt; local variable. So let's update the &lt;code&gt;tofu/k3s-main.tf&lt;/code&gt; file accordingly!&lt;/p&gt;
  666. &lt;pre&gt;&lt;code&gt;resource "proxmox_virtual_environment_vm" "k3s-main" {
  667.  name        = "k3s-main"
  668.  description = "Production k3s' main VM"
  669.  tags        = ["production", "k3s", "debian"]
  670.  node_name   = "proximighty"
  671.  
  672.  [...]
  673.  
  674.  initialization {
  675.    datastore_id = "local"
  676.  
  677.    ip_config {
  678.      ipv4 {
  679.        address = "192.168.1.221/24"
  680.        gateway = "192.168.1.254"
  681.        address = local.reserved_ips.k3s_main
  682.        gateway = local.reserved_ips.gateway
  683.      }
  684.    }
  685.  
  686.    user_data_file_id = proxmox_virtual_environment_file.user_data_cloud_config.id
  687.  }
  688. }
  689. &lt;/code&gt;&lt;/pre&gt;
  690. &lt;p&gt;We now have a single file to allocate IPs to virtual machines. We can see at a glance whether an IP is already used or not. That should save us some trouble! Let's now have a look at the precautions we need to take to save our files with git.&lt;/p&gt;
  691. &lt;h2&gt;Keeping a safe copy of our state&lt;/h2&gt;
  692. &lt;h3&gt;What is tofu state&lt;/h3&gt;
  693. &lt;p&gt;We used opentofu to describe what resources we wanted to create. Let's remove the &lt;code&gt;resource "proxmox_virtual_environment_vm" "k3s-main"&lt;/code&gt; we have created, and run &lt;code&gt;tofu plan&lt;/code&gt; to see how opentofu would react to that.&lt;/p&gt;
  694. &lt;pre&gt;&lt;code&gt;$ tofu plan
  695. data.local_file.ssh_public_key: Reading...
  696. data.local_file.ssh_public_key: Read complete after 0s [id=930cea05ae5e662573618e0d9f3e03920196cc5f]
  697. proxmox_virtual_environment_download_file.debian_13_cloud_image: Refreshing state... [id=local:iso/debian-13-generic-amd64.img]
  698. proxmox_virtual_environment_file.user_data_cloud_config: Refreshing state... [id=local:snippets/user-data-cloud-config.yaml]
  699. proxmox_virtual_environment_vm.k3s-main: Refreshing state... [id=100]
  700.  
  701. OpenTofu used the selected providers to generate the following execution plan.
  702. Resource actions are indicated with the following symbols:
  703.  - destroy
  704.  
  705. OpenTofu will perform the following actions:
  706.  
  707.  # proxmox_virtual_environment_vm.k3s-main will be destroyed
  708.  # (because proxmox_virtual_environment_vm.k3s-main is not in configuration)
  709.  - resource "proxmox_virtual_environment_vm" "k3s-main" {
  710.    [...]
  711.    }
  712.  
  713. Plan: 0 to add, 0 to change, 1 to destroy.
  714. &lt;/code&gt;&lt;/pre&gt;
  715. &lt;p&gt;If I remove a resource block, opentofu will try to delete it. But it might not be aware of other VMs I could have deployed. Hang on but that might be dangerous! If I already had 3 VMs running on Proxmox and started using opentofu after that, would it destroy them all, since I didn't describe them in my files?!&lt;/p&gt;
  716. &lt;p&gt;Fortunately for us, no. Opentofu needs to know what it is in charge of, and leave the rest alone. When I provision something via opentofu, it adds it to a local inventory of all the things it manages. That inventory is called a state file and looks like the following (prettified via &lt;code&gt;jq&lt;/code&gt;)&lt;/p&gt;
  717. &lt;pre&gt;&lt;code&gt;{
  718.  "version": 4,
  719.  "terraform_version": "1.10.5",
  720.  "serial": 13,
  721.  "lineage": "24d431ee-3da9-4407-b649-b0d2c0ca2d67",
  722.  "outputs": {},
  723.  "resources": [
  724.    {
  725.      "mode": "data",
  726.      "type": "local_file",
  727.      "name": "ssh_public_key",
  728.      "provider": "provider[\"registry.opentofu.org/hashicorp/local\"]",
  729.      "instances": [
  730.        {
  731.          "schema_version": 0,
  732.          "attributes": {
  733.            "content": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIGC++vbMTrSbQFKFgthj9oLaW1z5fCkQtlPCnG6eObB thib@ergaster.org\n",
  734.            "content_base64": "c3NoLWVkMjU1MTkgQUFBQUMzTnphQzFsWkRJMU5URTVBQUFBSUlHQysrdmJNVHJTYlFGS0ZndGhqOW9MYVcxejVmQ2tRdGxQQ25HNmVPYkIgdGhpYkBlcmdhc3Rlci5vcmcK",
  735.            "content_base64sha256": "YjQvgHA99AXWCaKLep6phGgdlmkZHvXU3OOhRSsQvms=",
  736.            "content_base64sha512": "tRp4/iG90wX0R1SghdvXwND8Hg6ADNuMMdPXANUYDa2uIjkRkLRgK5YPK6ACz5cbW+SbqvGPGzYpWNNFLGIFpQ==",
  737.            "content_md5": "ed5ee6428ea7c048fe8019bb1a2206b3",
  738.            "content_sha1": "930cea05ae5e662573618e0d9f3e03920196cc5f",
  739.            "content_sha256": "62342f80703df405d609a28b7a9ea984681d9669191ef5d4dce3a1452b10be6b",
  740.            "content_sha512": "b51a78fe21bdd305f44754a085dbd7c0d0fc1e0e800cdb8c31d3d700d5180dadae22391190b4602b960f2ba002cf971b5be49baaf18f1b362958d3452c6205a5",
  741.            "filename": "/Users/thibaultmartin/.ssh/id_ed25519.pub",
  742.            "id": "930cea05ae5e662573618e0d9f3e03920196cc5f"
  743.          },
  744.          "sensitive_attributes": []
  745.        }
  746.      ]
  747.    },
  748.    [...]
  749.  ],
  750.  "check_results": null
  751. }
  752. &lt;/code&gt;&lt;/pre&gt;
  753. &lt;p&gt;The tofu state is a local representation of what opentofu manages. It's absolutely mandatory for opentofu to work: this is how opentofu knows if it needs to deploy, update, or tear down resources. So we need to keep in a safe place, and my laptop is not a safe place at all. It can fail or get stolen. Since the state is a text based file, I could use git to keep remote copies of it.&lt;/p&gt;
  754. &lt;p&gt;But as you can see, the tofu state file contains my public key, that it read from a local file. The state file contains a structured view of what is in the &lt;code&gt;.tf&lt;/code&gt; files it manages. So far we have not added any sensitive credentials, but we might do it and not realize they will end up in state, and thus on a git repo.&lt;/p&gt;
  755. &lt;p&gt;Fortunately, opentofu comes with tools that let us encrypt the state, so we can commit it to a remote git repository with more peace of mind.&lt;/p&gt;
  756. &lt;h3&gt;Encrypting the tofu state&lt;/h3&gt;
  757. &lt;p&gt;Before encrypting our state, the opentofu documentation has &lt;a href="https://opentofu.org/docs/language/state/encryption/#general-guidance-and-pitfalls-please-read"&gt;an important section to read&lt;/a&gt; so you understand what it entails.&lt;/p&gt;
  758. &lt;p&gt;We need to migrate our unencrypted plan to an encrypted one. Let's bear in mind that there's no way back if we screw up, so let's make a backup first (and delete it when we're done). Note that a properly encrypted state can be migrated to a decrypted one. A botched encrypted state will likely be irrecoverable. Let's just copy it in a different directory&lt;/p&gt;
  759. &lt;pre&gt;&lt;code&gt;$ cd ~/Project/infra/tofu
  760. $ mkdir ~/tfbackups
  761. $ cp terraform.tfstate{,.backup} ~/tfbackups/
  762. &lt;/code&gt;&lt;/pre&gt;
  763. &lt;p&gt;To encrypt our state, we need to choose an encryption method: as a single admin homelabber I'm going for the simpler and sturdier method. I don't want to depend on extra infrastructure for secrets management, so I'm using &lt;a href="https://opentofu.org/docs/language/state/encryption/#pbkdf2"&gt;PBKDF2&lt;/a&gt;, which roughly means "generating an encryption key from a long passphrase."&lt;/p&gt;
  764. &lt;p&gt;With that in mind, let's follow &lt;a href="https://opentofu.org/docs/language/state/encryption/#pre-existing-project"&gt;the documentation to migrate a pre-existing project&lt;/a&gt;. Let's open our &lt;code&gt;providers.tf&lt;/code&gt; file and add an &lt;code&gt;encryption&lt;/code&gt; block within the &lt;code&gt;terraform&lt;/code&gt; one.&lt;/p&gt;
  765. &lt;pre&gt;&lt;code&gt;terraform {
  766.  
  767.  encryption {
  768.    method "unencrypted" "migrate" {}
  769.  
  770.    key_provider "pbkdf2" "password_key" {
  771.      passphrase = "REDACTED"
  772.    }
  773.    
  774.    method "aes_gcm" "password_based" {
  775.      keys = key_provider.pbkdf2.password_key
  776.    }
  777.  
  778.    state {
  779.      method = method.aes_gcm.password_based
  780.  
  781.      fallback {
  782.        method = method.unencrypted.migrate
  783.      }
  784.    }
  785.  }
  786.  
  787.  required_providers {
  788.    proxmox = {
  789.      source  = "bpg/proxmox"
  790.      version = "0.80.0"
  791.    }
  792.  }
  793. }
  794.  
  795. provider "proxmox" {
  796.  endpoint = "https://192.168.1.220:8006/"
  797.  
  798.  ssh {
  799.    agent    = true
  800.    username = "root"
  801.  }
  802. }
  803. &lt;/code&gt;&lt;/pre&gt;
  804. &lt;p&gt;This block instructs terraform to encrypt the state with a key generated from our password. It also tells it to expect a pre-existing unencrypted state to exist, that it's okay to read and encrypt it.&lt;/p&gt;
  805. &lt;p&gt;Note that I've used the encryption passphrase directly in that block. We will move it to a safer place later, but for now let's keep things simple.&lt;/p&gt;
  806. &lt;p&gt;Let's now apply this plan to see if our state gets encrypted correctly, but &lt;strong&gt;make sure you do have a cleartext backup first.&lt;/strong&gt;&lt;/p&gt;
  807. &lt;pre&gt;&lt;code&gt;$ cd ~/Projects/infra/tofu
  808. $ tofu apply
  809. &lt;/code&gt;&lt;/pre&gt;
  810. &lt;p&gt;After the apply, we can have a look at the &lt;code&gt;terraform.tfstate&lt;/code&gt; file to check that it has indeed been encrypted.&lt;/p&gt;
  811. &lt;pre&gt;&lt;code&gt;{
  812.  "serial": 13,
  813.  "lineage": "24d431ee-3da9-4407-b649-b0d2c0ca2d67",
  814.  "meta": {
  815.    "key_provider.pbkdf2.password_key": "eyJzYWx0[...]"
  816.  },
  817.  "encrypted_data": "ONXZsJhz[...]",
  818.  "encryption_version": "v0"
  819. }
  820. &lt;/code&gt;&lt;/pre&gt;
  821. &lt;p&gt;I know that opentofu people probably know what they're doing, but I don't like that &lt;code&gt;password_key&lt;/code&gt; field. It starts with &lt;code&gt;eyJ&lt;/code&gt;, &lt;a href="https://ergaster.org/til/base64-encoded-json/"&gt;so that must be a base64 encoded json object&lt;/a&gt;. Let's decode that&lt;/p&gt;
  822. &lt;pre&gt;&lt;code&gt;$ echo "eyJzYWx0[...]" | base64 -d
  823. {"salt":"jzGRZLVANeFJpJRxj8RXg48FfOoB++GF/Honm6sIF9Y=","iterations":600000,"hash_function":"sha512","key_length":32}
  824. &lt;/code&gt;&lt;/pre&gt;
  825. &lt;p&gt;All good, it's just the salt, iterations, hash function and key length parameters. Those are pretty much public, we can commit the file to our repo! But... what about the &lt;code&gt;terraform.tfstate.backup&lt;/code&gt; file? Let's examine this one&lt;/p&gt;
  826. &lt;pre&gt;&lt;code&gt;{
  827.  "version": 4,
  828.  "terraform_version": "1.10.5",
  829.  "serial": 12,
  830.  "lineage": "24d431ee-3da9-4407-b649-b0d2c0ca2d67",
  831.  "outputs": {},
  832.  "resources": [
  833.    {
  834.      "mode": "data",
  835.      "type": "local_file",
  836.      "name": "ssh_public_key",
  837.      "provider": "provider[\"registry.opentofu.org/hashicorp/local\"]",
  838.      "instances": [
  839.        {
  840.          "schema_version": 0,
  841.          "attributes": {
  842.            "content": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIGC++vbMTrSbQFKFgthj9oLaW1z5fCkQtlPCnG6eObB thib@ergaster.org\n",
  843.            [...]
  844.        }
  845.      ]
  846.    },
  847. [...]
  848.  ],
  849.  "check_results": null
  850. }
  851. &lt;/code&gt;&lt;/pre&gt;
  852. &lt;p&gt;Oh dear! That one is &lt;em&gt;not&lt;/em&gt; encrypted! I didn't find any utility for it since terraform can't do "rollbacks", and I couldn't find docs for it. I've deleted the file and I could still perform &lt;code&gt;tofu apply&lt;/code&gt; without a problem. The next iterations should be encrypted, but I will add it to my &lt;code&gt;.gitignore&lt;/code&gt; just in case!&lt;/p&gt;
  853. &lt;p&gt;We're not &lt;em&gt;quite&lt;/em&gt; ready to commit our files though. We still have a secret in plain text! We give away the encryption key we use. Let's extract it into an environment variable so we don't leak it.&lt;/p&gt;
  854. &lt;h3&gt;Removing sensitive information&lt;/h3&gt;
  855. &lt;p&gt;We need to create a new credential in the &lt;code&gt;Infra&lt;/code&gt; folder of our vault, and call it &lt;code&gt;TF_ENCRYPTION&lt;/code&gt;. Its content is the following&lt;/p&gt;
  856. &lt;pre&gt;&lt;code&gt;key_provider "pbkdf2" "password_key" { passphrase = "yourPassphraseGoesHere" }
  857. &lt;/code&gt;&lt;/pre&gt;
  858. &lt;p&gt;Then we need to sync the vault managed by the bitwarden CLI, to ensure it has the credential we just added.&lt;/p&gt;
  859. &lt;pre&gt;&lt;code&gt;$ bw sync
  860. Syncing complete.
  861. &lt;/code&gt;&lt;/pre&gt;
  862. &lt;p&gt;Let's update our &lt;code&gt;~/Projects/infra/.direnv&lt;/code&gt; to make it retrieve the &lt;code&gt;TF_ENCRYPTION&lt;/code&gt; environment variable&lt;/p&gt;
  863. &lt;pre&gt;&lt;code&gt;bitwarden_password_to_env Infra PROXMOX_VE_API_TOKEN TF_ENCRYPTION
  864. &lt;/code&gt;&lt;/pre&gt;
  865. &lt;p&gt;And let's make direnv allow it&lt;/p&gt;
  866. &lt;pre&gt;&lt;code&gt;$ direnv allow ~/Projects/infra/
  867. &lt;/code&gt;&lt;/pre&gt;
  868. &lt;p&gt;Let's remove the block that provided our password from the &lt;code&gt;encryption&lt;/code&gt; block in &lt;code&gt;providers.tf&lt;/code&gt;&lt;/p&gt;
  869. &lt;pre&gt;&lt;code&gt;terraform {
  870.  
  871.  encryption {
  872.    method "unencrypted" "migrate" {}
  873.  
  874.    key_provider "pbkdf2" "password_key" {
  875.      passphrase = "REDACTED"
  876.    }
  877.    
  878.    method "aes_gcm" "password_based" {
  879.      keys = key_provider.pbkdf2.password_key
  880.    }
  881.  
  882.    state {
  883.      method = method.aes_gcm.password_based
  884.  
  885.      fallback {
  886.        method = method.unencrypted.migrate
  887.      }
  888.    }
  889.  }
  890.  
  891. [...]
  892. }
  893. &lt;/code&gt;&lt;/pre&gt;
  894. &lt;p&gt;And let's try a &lt;code&gt;tofu plan&lt;/code&gt; to confirm that opentofu could read the passphrase from the environment variable&lt;/p&gt;
  895. &lt;pre&gt;&lt;code&gt;$ tofu plan
  896. β•·
  897. β”‚ Warning: Unencrypted method configured
  898. β”‚
  899. β”‚   on  line 0:
  900. β”‚   (source code not available)
  901. β”‚
  902. β”‚ Method unencrypted is present in configuration. This is a security risk and
  903. β”‚ should only be enabled during migrations.
  904. β•΅
  905. data.local_file.ssh_public_key: Reading...
  906. data.local_file.ssh_public_key: Read complete after 0s [id=930cea05ae5e662573618e0d9f3e03920196cc5f]
  907. proxmox_virtual_environment_download_file.debian_13_cloud_image: Refreshing state... [id=local:iso/debian-13-generic-amd64.img]
  908. proxmox_virtual_environment_file.user_data_cloud_config: Refreshing state... [id=local:snippets/user-data-cloud-config.yaml]
  909. proxmox_virtual_environment_vm.k3s-main: Refreshing state... [id=100]
  910.  
  911. No changes. Your infrastructure matches the configuration.
  912.  
  913. OpenTofu has compared your real infrastructure against your configuration and found
  914. no differences, so no changes are needed.
  915. &lt;/code&gt;&lt;/pre&gt;
  916. &lt;p&gt;Brilliant! We can now also remove the &lt;code&gt;migrate&lt;/code&gt; and &lt;code&gt;fallback blocks&lt;/code&gt; so opentofu doesn't trust unencrypted content at all, which will prevent malicious actors from tampering with our file.&lt;/p&gt;
  917. &lt;pre&gt;&lt;code&gt;terraform {
  918.  
  919.  encryption {
  920.    method "unencrypted" "migrate" {}
  921.    
  922.    method "aes_gcm" "password_based" {
  923.      keys = key_provider.pbkdf2.password_key
  924.    }
  925.  
  926.    state {
  927.      method = method.aes_gcm.password_based
  928.  
  929.      fallback {
  930.        method = method.unencrypted.migrate
  931.      }
  932.    }
  933.  }
  934.  
  935. [...]
  936. }
  937. &lt;/code&gt;&lt;/pre&gt;
  938. &lt;p&gt;Finally we can delete our cleartext backup&lt;/p&gt;
  939. &lt;pre&gt;&lt;code&gt;$ rm -Rf ~/tfbackups
  940. &lt;/code&gt;&lt;/pre&gt;
  941. &lt;p&gt;VoilΓ , we have an encrypted state that we can push to a remote Github repository, and our state will be reasonably safe for today's standards!&lt;/p&gt;
  942. &lt;h2&gt;Fully configuring and managing the VM&lt;/h2&gt;
  943. &lt;p&gt;As we've seen when setting up the Proxmox host, ansible can be used to put a machine in a desired state. It can write a playbook to install k3s and copy the kubeconfig file to my admin laptop.&lt;/p&gt;
  944. &lt;p&gt;Then there's the question of: how do we make opentofu (who provisions the VMs) and ansible (who deploys services on the VMs) talk to each other? In an ideal world, I would tell opentofu to provision the VM, and then to run an ansible playbook on the hosts it has created.&lt;/p&gt;
  945. &lt;p&gt;There's an &lt;a href="https://registry.terraform.io/providers/ansible/ansible/latest/docs"&gt;ansible opentofu provider&lt;/a&gt; that's supposed to play this role. I didn't find intuitive to use, and most people around me told me they found it so cumbersome they didn't use it. There is a more flexible and sturdy solution: ansible dynamic inventories!&lt;/p&gt;
  946. &lt;h3&gt;Creating a dynamic inventory for k3s VMs&lt;/h3&gt;
  947. &lt;p&gt;Ansible supports creating inventories by calling plugins that will retrieve information from sources. The &lt;a href="https://docs.ansible.com/ansible/latest/collections/community/general/proxmox_inventory.html"&gt;Proxmox inventory source plugin&lt;/a&gt; lets ansible query Proxmox and retrieve information about VMs, and automatically group them together.&lt;/p&gt;
  948. &lt;p&gt;Hang on. Are we really going to create a dynamic inventory &lt;em&gt;for a single VM?&lt;/em&gt; I know we're over engineering things for the sake of learning, but &lt;em&gt;isn't it a bit too much?&lt;/em&gt; As always, it's important to consider what problem we're trying to solve. To me, we're solving two different problems:&lt;/p&gt;
  949. &lt;ol&gt;
  950. &lt;li&gt;&lt;strong&gt;We make sure that there is a single canonical source of truth&lt;/strong&gt;, and it is opentofu. The IP defined in opentofu, it's the one provisioned on Proxmox, and it's the one the dynamic inventory will use to perform operations on the VM. If the VM needs to change its IP, we only have to update it in opentofu, and ansible will follow along.&lt;/li&gt;
  951. &lt;li&gt;&lt;strong&gt;We build a sane foundation for more complex setups&lt;/strong&gt;. It will be easy to extend when deploying more VMs to run complex clusters, while not adding unnecessary complexity.&lt;/li&gt;
  952. &lt;/ol&gt;
  953. &lt;p&gt;So let's start by making sure we have the Proxmox plugin installed. It is part of the &lt;code&gt;community.general&lt;/code&gt; collection on &lt;code&gt;ansible-galaxy&lt;/code&gt;, so let's install it&lt;/p&gt;
  954. &lt;pre&gt;&lt;code&gt;$ ansible-galaxy collection install community.general
  955. &lt;/code&gt;&lt;/pre&gt;
  956. &lt;p&gt;Then in the &lt;code&gt;~/Projects/infra/ansible/inventory&lt;/code&gt; directory, we can create a &lt;code&gt;proximighty.proxmox.yaml&lt;/code&gt;. The file has to end with &lt;code&gt;.proxmox.yaml&lt;/code&gt; for the Proxmox plugin to work.&lt;/p&gt;
  957. &lt;pre&gt;&lt;code&gt;plugin: community.general.proxmox
  958. url: https://192.168.1.200:8006
  959. user: "terraform@pve"
  960. token_id: "provider"
  961. token_secret: "REDACTED"
  962. &lt;/code&gt;&lt;/pre&gt;
  963. &lt;p&gt;Let's break it down:&lt;/p&gt;
  964. &lt;ul&gt;
  965. &lt;li&gt;&lt;code&gt;plugin&lt;/code&gt; tells ansible to use the Proxmox inventory source plugin.&lt;/li&gt;
  966. &lt;li&gt;&lt;code&gt;url&lt;/code&gt; is the URL of the Proxmox cluster.&lt;/li&gt;
  967. &lt;li&gt;&lt;code&gt;user&lt;/code&gt; is the Proxmox user we authenticate as. Here I'm reusing the same value as the service account we have created for opentofu.&lt;/li&gt;
  968. &lt;li&gt;&lt;code&gt;token_id&lt;/code&gt; is the ID of the token we have issued for the user. I'm also reusing the same value as the API Key we have created for opentofu.&lt;/li&gt;
  969. &lt;li&gt;&lt;code&gt;token_secret&lt;/code&gt; is the password for the API Key. Here again I'm reusing the same value as the API Key we have created for opentofu. I'm writting it in the plain text file for now, we will clean it up later.&lt;/li&gt;
  970. &lt;/ul&gt;
  971. &lt;p&gt;Now we can try to pass that dynamic inventory configuration to ansible for it to build an inventory from Proxmox.&lt;/p&gt;
  972. &lt;pre&gt;&lt;code&gt;$ ansible-inventory -i proximighty.proxmox.yaml --list
  973. [WARNING]:  * Failed to parse
  974. /Users/thibaultmartin/Projects/infra/ansible/inventory/proximighty.proxmox.yaml
  975. with auto plugin: HTTPSConnectionPool(host='192.168.1.200', port=8006): Max retries
  976. exceeded with url: /api2/json/nodes (Caused by SSLError(SSLCertVerificationError(1,
  977. '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local
  978. issuer certificate (_ssl.c:1028)')))
  979. [WARNING]:  * Failed to parse
  980. /Users/thibaultmartin/Projects/infra/ansible/inventory/proximighty.proxmox.yaml
  981. with yaml plugin: Plugin configuration YAML file, not YAML inventory
  982. [WARNING]:  * Failed to parse
  983. /Users/thibaultmartin/Projects/infra/ansible/inventory/proximighty.proxmox.yaml
  984. with ini plugin: Invalid host pattern 'plugin:' supplied, ending in ':' is not
  985. allowed, this character is reserved to provide a port.
  986. [WARNING]: Unable to parse
  987. /Users/thibaultmartin/Projects/infra/ansible/inventory/proximighty.proxmox.yaml as
  988. an inventory source
  989. [WARNING]: No inventory was parsed, only implicit localhost is available
  990. {
  991.    "_meta": {
  992.        "hostvars": {}
  993.    },
  994.    "all": {
  995.        "children": [
  996.            "ungrouped"
  997.        ]
  998.    }
  999. }
  1000. &lt;/code&gt;&lt;/pre&gt;
  1001. &lt;p&gt;And it fails! This is unfortunately not a surprise. We asked the plugin to look up into Proxmox and gave it a https URL. But when Proxmox runs for the first time, it generates a self-signed certificate. It is a perfectly fine certificate we can use to handle https requests. The only problem is that our laptop doesn't trust the Proxmox host, who signed the certificate for itself.&lt;/p&gt;
  1002. &lt;p&gt;The good news is that &lt;a href="https://pve.proxmox.com/wiki/Certificate_Management"&gt;Proxmox can retrieve certificates&lt;/a&gt; signed by authorities our laptop trusts! The bad news is that we need to understand what we're doing to do it properly. Like earlier, when we configured the Proxmox provider for opentofu, let's ask the Proxmox plugin to use the certificate even if it doesn't trust the authority who signed it. Since we're in a homelab on a home network, the risk of accidentally reaching a host that impersonates our Proxmox host is still fairly low, so it's acceptable to temporarily take this risk here again.&lt;/p&gt;
  1003. &lt;p&gt;Let's add the following line to our dynamic inventory configuration to ignore the certificate signature&lt;/p&gt;
  1004. &lt;pre&gt;&lt;code&gt;plugin: community.general.proxmox
  1005. url: https://192.168.1.200:8006
  1006. user: "terraform@pve"
  1007. token_id: "provider"
  1008. token_secret: "REDACTED"
  1009. validate_certs: false
  1010. &lt;/code&gt;&lt;/pre&gt;
  1011. &lt;p&gt;And now, running the inventory command again&lt;/p&gt;
  1012. &lt;pre&gt;&lt;code&gt;$ cd ~/Projects/infra/ansible/inventory
  1013. $ ansible-inventory -i proximighty.proxmox.yaml --list
  1014. {
  1015.    "_meta": {
  1016.        "hostvars": {}
  1017.    },
  1018.    "all": {
  1019.        "children": [
  1020.            "ungrouped",
  1021.            "proxmox_all_lxc",
  1022.            "proxmox_all_qemu",
  1023.            "proxmox_all_running",
  1024.            "proxmox_all_stopped",
  1025.            "proxmox_nodes",
  1026.            "proxmox_proximighty_lxc",
  1027.            "proxmox_proximighty_qemu"
  1028.        ]
  1029.    },
  1030.    "proxmox_all_qemu": {
  1031.        "hosts": [
  1032.            "k3s-main"
  1033.        ]
  1034.    },
  1035.    "proxmox_all_running": {
  1036.        "hosts": [
  1037.            "k3s-main"
  1038.        ]
  1039.    },
  1040.    "proxmox_nodes": {
  1041.        "hosts": [
  1042.            "proximighty"
  1043.        ]
  1044.    },
  1045.    "proxmox_proximighty_qemu": {
  1046.        "hosts": [
  1047.            "k3s-main"
  1048.        ]
  1049.    }
  1050. }
  1051. &lt;/code&gt;&lt;/pre&gt;
  1052. &lt;p&gt;Great! We can see that our k3s-main VM appears! We didn't learn a lot about it though. Let's ask the Proxmox plugin to give us more information about the VMs with the &lt;a href="https://docs.ansible.com/ansible/latest/collections/community/general/proxmox_inventory.html#parameter-want_facts"&gt;&lt;code&gt;want_facts&lt;/code&gt;&lt;/a&gt; parameter&lt;/p&gt;
  1053. &lt;pre&gt;&lt;code&gt;plugin: community.general.proxmox
  1054. url: https://192.168.1.200:8006
  1055. user: "terraform@pve"
  1056. token_id: "provider"
  1057. token_secret: "REDACTED"
  1058. want_facts: true
  1059. validate_certs: false
  1060. &lt;/code&gt;&lt;/pre&gt;
  1061. &lt;p&gt;Let's run it again and see if we get more interesting results&lt;/p&gt;
  1062. &lt;pre&gt;&lt;code&gt;$ cd ~/Projects/infra/ansible/inventory
  1063. $ ansible-inventory -i proximighty.proxmox.yaml --list
  1064. {
  1065.    "_meta": {
  1066.        "hostvars": {
  1067.            "k3s-main": {
  1068.                "proxmox_acpi": 1,
  1069.                "proxmox_agent": {
  1070.                    "enabled": "0",
  1071.                    "fstrim_cloned_disks": "0",
  1072.                    "type": "virtio"
  1073.                },
  1074.                "proxmox_balloon": 16384,
  1075.                "proxmox_bios": "seabios",
  1076.                "proxmox_boot": {
  1077.                    "order": "virtio0;net0"
  1078.                },
  1079.                "proxmox_cicustom": {
  1080.                    "user": "local:snippets/user-data-cloud-config.yaml"
  1081.                },
  1082.                "proxmox_cores": 4,
  1083.                "proxmox_cpu": {
  1084.                    "cputype": "x86-64-v4"
  1085.                },
  1086.                "proxmox_cpuunits": 1024,
  1087.                "proxmox_description": "Production k3s' main VM",
  1088.                "proxmox_digest": "b68508152b464627d06cba6505ed195aa3d34f59",
  1089.                "proxmox_ide2": {
  1090.                    "disk_image": "local:100/vm-100-cloudinit.qcow2",
  1091.                    "media": "cdrom"
  1092.                },
  1093.                "proxmox_ipconfig0": {
  1094.                    "gw": "192.168.1.254",
  1095.                    "ip": "192.168.1.221/24"
  1096.                },
  1097.                "proxmox_keyboard": "en-us",
  1098.                "proxmox_memory": "16384",
  1099.                "proxmox_meta": {
  1100.                    "creation-qemu": "9.2.0",
  1101.                    "ctime": "1753547614"
  1102.                },
  1103.                "proxmox_name": "k3s-main",
  1104.                "proxmox_net0": {
  1105.                    "bridge": "vmbr0",
  1106.                    "firewall": "0",
  1107.                    "virtio": "BC:24:11:A6:96:8B"
  1108.                },
  1109.                "proxmox_node": "proximighty",
  1110.                "proxmox_numa": 0,
  1111.                "proxmox_onboot": 1,
  1112.                "proxmox_ostype": "other",
  1113.                "proxmox_protection": 0,
  1114.                "proxmox_qmpstatus": "running",
  1115.                "proxmox_scsihw": {
  1116.                    "disk_image": "virtio-scsi-pci"
  1117.                },
  1118.                "proxmox_serial0": "socket",
  1119.                "proxmox_smbios1": {
  1120.                    "uuid": "0d47f7c8-e0b4-4302-be03-64aa931a4c4e"
  1121.                },
  1122.                "proxmox_snapshots": [],
  1123.                "proxmox_sockets": 1,
  1124.                "proxmox_status": "running",
  1125.                "proxmox_tablet": 1,
  1126.                "proxmox_tags": "debian;k3s;production",
  1127.                "proxmox_tags_parsed": [
  1128.                    "debian",
  1129.                    "k3s",
  1130.                    "production"
  1131.                ],
  1132.                "proxmox_template": 0,
  1133.                "proxmox_virtio0": {
  1134.                    "aio": "io_uring",
  1135.                    "backup": "1",
  1136.                    "cache": "none",
  1137.                    "discard": "ignore",
  1138.                    "disk_image": "local:100/vm-100-disk-0.qcow2",
  1139.                    "iothread": "1",
  1140.                    "replicate": "1",
  1141.                    "size": "500G"
  1142.                },
  1143.                "proxmox_vmgenid": "e00a2059-1310-4b0b-87f7-7818e7cdb9ae",
  1144.                "proxmox_vmid": 100,
  1145.                "proxmox_vmtype": "qemu"
  1146.            }
  1147.        }
  1148.    },
  1149.    "all": {
  1150.        "children": [
  1151.            "ungrouped",
  1152.            "proxmox_all_lxc",
  1153.            "proxmox_all_qemu",
  1154.            "proxmox_all_running",
  1155.            "proxmox_all_stopped",
  1156.            "proxmox_nodes",
  1157.            "proxmox_proximighty_lxc",
  1158.            "proxmox_proximighty_qemu"
  1159.        ]
  1160.    },
  1161.    "proxmox_all_qemu": {
  1162.        "hosts": [
  1163.            "k3s-main"
  1164.        ]
  1165.    },
  1166.    "proxmox_all_running": {
  1167.        "hosts": [
  1168.            "k3s-main"
  1169.        ]
  1170.    },
  1171.    "proxmox_nodes": {
  1172.        "hosts": [
  1173.            "proximighty"
  1174.        ]
  1175.    },
  1176.    "proxmox_proximighty_qemu": {
  1177.        "hosts": [
  1178.            "k3s-main"
  1179.        ]
  1180.    }
  1181. }
  1182. &lt;/code&gt;&lt;/pre&gt;
  1183. &lt;p&gt;That's a tonne of information! Probably more than we need, and we still don't know how to connect to a specific host. Let's add some order into that. First, let's group all the VMs that have &lt;code&gt;k3s&lt;/code&gt; in their tags under a an ansible group called &lt;code&gt;k3s&lt;/code&gt;&lt;/p&gt;
  1184. &lt;pre&gt;&lt;code&gt;plugin: community.general.proxmox
  1185. url: https://192.168.1.200:8006
  1186. user: "terraform@pve"
  1187. token_id: "provider"
  1188. token_secret: "REDACTED"
  1189. want_facts: true
  1190. groups:
  1191.  k3s: "'k3s' in (proxmox_tags_parsed|list)"
  1192. validate_certs: false
  1193. &lt;/code&gt;&lt;/pre&gt;
  1194. &lt;p&gt;And now let's tell ansible how to figure out what IP to use for a host. Since we are the ones provisioning the VMs, we know for sure that we have configured them to use a static IP, on the single virtual network interface we gave them.&lt;/p&gt;
  1195. &lt;p&gt;Let's use the &lt;a href="https://docs.ansible.com/ansible/latest/collections/community/general/proxmox_inventory.html#parameter-compose"&gt;&lt;code&gt;compose&lt;/code&gt;&lt;/a&gt; parameter to populate an &lt;code&gt;ansible_host&lt;/code&gt; variable that contains the IP of the VM.&lt;/p&gt;
  1196. &lt;pre&gt;&lt;code&gt;plugin: community.general.proxmox
  1197. url: https://192.168.1.200:8006
  1198. user: "terraform@pve"
  1199. token_id: "provider"
  1200. token_secret: "REDACT"
  1201. want_facts: true
  1202. groups:
  1203.  k3s: "'k3s' in (proxmox_tags_parsed|list)"
  1204. compose:
  1205.  ansible_host: proxmox_ipconfig0.ip | default(proxmox_net0.ip) | ansible.utils.ipaddr('address')
  1206. validate_certs: false
  1207. &lt;/code&gt;&lt;/pre&gt;
  1208. &lt;p&gt;And finally let's test this again&lt;/p&gt;
  1209. &lt;pre&gt;&lt;code&gt;$ cd ~/Projects/infra/ansible/inventory
  1210. ansible-inventory -i proximighty.proxmox.yaml --list
  1211. {
  1212.    "_meta": {
  1213.        "hostvars": {
  1214.            "k3s-main": {
  1215.                "ansible_host": "192.168.1.221",
  1216.                "proxmox_acpi": 1,
  1217.                "proxmox_agent": {
  1218.                    "enabled": "0",
  1219.                    "fstrim_cloned_disks": "0",
  1220.                    "type": "virtio"
  1221.                },
  1222.                [...]
  1223.            }
  1224.        }
  1225.    },
  1226.    "all": {
  1227.        "children": [
  1228.            "ungrouped",
  1229.            "proxmox_all_lxc",
  1230. [...]
  1231.            "k3s"
  1232.        ]
  1233.    },
  1234.    "k3s": {
  1235.        "hosts": [
  1236.            "k3s-main"
  1237.        ]
  1238.    },
  1239. [...]
  1240. }
  1241. &lt;/code&gt;&lt;/pre&gt;
  1242. &lt;p&gt;Brilliant! We now have a &lt;code&gt;k3s&lt;/code&gt; group, that contains our single &lt;code&gt;k3s-main&lt;/code&gt; VM, and it's been able to retrieve its IP successfully! Let's create a simple playbook to try to execute on the VM one command that works and one that doesn't.&lt;/p&gt;
  1243. &lt;p&gt;Let's create a &lt;code&gt;~/Projects/infra/ansible/k3s/test.yaml&lt;/code&gt;&lt;/p&gt;
  1244. &lt;pre&gt;&lt;code&gt;---
  1245. - name: Execute commands on the k3s host
  1246.  hosts: k3s
  1247.  remote_user: thib
  1248.  tasks:
  1249.    - name: Echo on the remote server
  1250.      ansible.builtin.command: echo "It worked"
  1251.      changed_when: false
  1252.    - name: Get k3s installed version
  1253.      ansible.builtin.command: k3s --version
  1254.      register: k3s_version_output
  1255.      changed_when: false
  1256.      ignore_errors: true
  1257. &lt;/code&gt;&lt;/pre&gt;
  1258. &lt;p&gt;The only two notable things here are&lt;/p&gt;
  1259. &lt;ul&gt;
  1260. &lt;li&gt;&lt;code&gt;hosts&lt;/code&gt; is the name of the group we created in the dynamic inventory&lt;/li&gt;
  1261. &lt;li&gt;&lt;code&gt;remote_user&lt;/code&gt; is the user I have pre-configured via cloud-init when spinning up the VM&lt;/li&gt;
  1262. &lt;/ul&gt;
  1263. &lt;pre&gt;&lt;code&gt;$ cd
  1264. $ ansible-playbook -i inventory/proximighty.proxmox.yaml k3s/test.yaml
  1265.  
  1266. PLAY [Execute commands on the k3s host] ********************************************
  1267.  
  1268. TASK [Gathering Facts] *************************************************************
  1269. ok: [k3s-main]
  1270.  
  1271. TASK [Echo on the remote server] ***************************************************
  1272. ok: [k3s-main]
  1273.  
  1274. TASK [Get k3s installed version] ***************************************************
  1275. fatal: [k3s-main]: FAILED! =&amp;gt; {"changed": false, "cmd": "k3s --version", "msg": "[Errno 2] No such file or directory: b'k3s'", "rc": 2, "stderr": "", "stderr_lines": [], "stdout": "", "stdout_lines": []}
  1276. ...ignoring
  1277.  
  1278. PLAY RECAP *************************************************************************
  1279. k3s-main                   : ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=1  
  1280. &lt;/code&gt;&lt;/pre&gt;
  1281. &lt;p&gt;It works! Now that we know how to build an inventory based on Proxmox tags and could make a simply ansible playbook use it, let's move forward and actually deploy k3s on our VM!&lt;/p&gt;
  1282. &lt;h3&gt;Deploying k3s on the k3s-main VM&lt;/h3&gt;
  1283. &lt;p&gt;The k3s maintainers have created a &lt;a href="https://github.com/k3s-io/k3s-ansible/"&gt;k3s-ansible playbook&lt;/a&gt; that can preconfigure a machine to ensure it will be ready to make k3s run, and deploy a single or multi-node cluster. It's a great playbook and it's important not to reinvent the wheel. But I also like to understand what I execute, and keep things minimal to limit the risk of breakage.&lt;/p&gt;
  1284. &lt;p&gt;Let's take inspiration from this excellent playbook to build one tailored for our (very simple) needs: deploying k3s-server on a single node. When trying to install k3s server via the playbook on Debian, it executes 2 roles:&lt;/p&gt;
  1285. &lt;ul&gt;
  1286. &lt;li&gt;&lt;a href="https://github.com/k3s-io/k3s-ansible/blob/master/roles/prereq/tasks/main.yml"&gt;&lt;code&gt;prereq&lt;/code&gt;&lt;/a&gt; that performs a series of checks to ensure k3s can be installed and run well&lt;/li&gt;
  1287. &lt;li&gt;&lt;a href="https://github.com/k3s-io/k3s-ansible/blob/master/roles/k3s_server/tasks/main.yml"&gt;&lt;code&gt;k3s_server&lt;/code&gt;&lt;/a&gt; that downloads, preconfigures and install k3s&lt;/li&gt;
  1288. &lt;/ul&gt;
  1289. &lt;p&gt;We know that the OS powering our virtual machine is always going to be a Debian cloud image. None of the checks in &lt;code&gt;prereq&lt;/code&gt; are useful for a fresh vanilla Debian stable. So let's skip it entirely.&lt;/p&gt;
  1290. &lt;p&gt;Let's have a closer look at what &lt;code&gt;k3s_server&lt;/code&gt; does, and carry the important bits over to our playbook. We want to&lt;/p&gt;
  1291. &lt;ol&gt;
  1292. &lt;li&gt;Check whether k3s is already installed so we don't override it if it was already installed&lt;/li&gt;
  1293. &lt;li&gt;Download the install script&lt;/li&gt;
  1294. &lt;li&gt;Execute the install script to download k3s&lt;/li&gt;
  1295. &lt;li&gt;Create a systemd service for k3s to start automatically&lt;/li&gt;
  1296. &lt;li&gt;Enable the service&lt;/li&gt;
  1297. &lt;li&gt;Copy the kubeconfig file generated by k3s to our laptop, and merge it with our kubeconfig under the cluster name and context &lt;code&gt;mightykube&lt;/code&gt;&lt;/li&gt;
  1298. &lt;/ol&gt;
  1299. &lt;p&gt;To do things cleanly, we will create a &lt;code&gt;k3s_server&lt;/code&gt; role in a &lt;code&gt;k3s&lt;/code&gt; directory.&lt;/p&gt;
  1300. &lt;pre&gt;&lt;code&gt;---
  1301. - name: Get k3s installed version
  1302.  ansible.builtin.command: k3s --version
  1303.  register: k3s_server_version_output
  1304.  changed_when: false
  1305.  ignore_errors: true
  1306.  
  1307. - name: Set k3s installed version
  1308.  when: not ansible_check_mode and k3s_server_version_output.rc == 0
  1309.  ansible.builtin.set_fact:
  1310.    k3s_server_installed_version: "{{ k3s_server_version_output.stdout_lines[0].split(' ')[2] }}"
  1311.  
  1312. - name: Download and execute k3s installer if k3s is not already installed
  1313.  when: not ansible_check_mode and (k3s_server_version_output.rc != 0 or k3s_server_installed_version is version(k3s_server_version, '&amp;lt;'))
  1314.  block:
  1315.    - name: Download K3s install script
  1316.      ansible.builtin.get_url:
  1317.        url: https://get.k3s.io/
  1318.        timeout: 120
  1319.        dest: /usr/local/bin/k3s-install.sh
  1320.        owner: root
  1321.        group: root
  1322.        mode: "0755"
  1323.  
  1324.    - name: Install K3s binary
  1325.      ansible.builtin.command:
  1326.        cmd: /usr/local/bin/k3s-install.sh
  1327.      environment:
  1328.        INSTALL_K3S_SKIP_START: "true"
  1329.        INSTALL_K3S_VERSION: "{{ k3s_server_version }}"
  1330.      changed_when: true
  1331.  
  1332.    - name: Copy K3s service file [Single]
  1333.      ansible.builtin.template:
  1334.        src: "k3s-single.service.j2"
  1335.        dest: "/etc/systemd/system/k3s.service"
  1336.        owner: root
  1337.        group: root
  1338.        mode: "0644"
  1339.      register: k3s_server_service_file_single
  1340.  
  1341.    - name: Enable and check K3s service
  1342.      ansible.builtin.systemd:
  1343.        name: k3s
  1344.        daemon_reload: true
  1345.        state: started
  1346.        enabled: true
  1347.  
  1348. - name: Check whether kubectl is installed on control node
  1349.  ansible.builtin.command: 'kubectl'
  1350.  register: k3s_server_kubectl_installed
  1351.  ignore_errors: true
  1352.  delegate_to: 127.0.0.1
  1353.  become: false
  1354.  changed_when: false
  1355.  
  1356. # Copy the k3s config to a second file to detect changes.
  1357. # If no changes are found, we can skip copying the kubeconfig to the control node.
  1358. - name: Copy k3s.yaml to second file
  1359.  ansible.builtin.copy:
  1360.    src: /etc/rancher/k3s/k3s.yaml
  1361.    dest: /etc/rancher/k3s/k3s-copy.yaml
  1362.    mode: "0600"
  1363.    remote_src: true
  1364.  register: k3s_server_k3s_yaml_file_copy
  1365.  
  1366. - name: Apply k3s kubeconfig to control node if file has change and control node has kubectl installed
  1367.  when:
  1368.    - k3s_server_kubectl_installed.rc == 0
  1369.    - k3s_server_k3s_yaml_file_copy.changed
  1370.  block:
  1371.    - name: Copy kubeconfig to control node
  1372.      ansible.builtin.fetch:
  1373.        src: /etc/rancher/k3s/k3s.yaml
  1374.        dest: "~/.kube/config.new"
  1375.        flat: true
  1376.  
  1377.    - name: Change server address in kubeconfig on control node
  1378.      ansible.builtin.shell: |
  1379.        KUBECONFIG=~/.kube/config.new kubectl config set-cluster default --server=https://{{ hostvars[groups['k3s'][0]]['ansible_host'] }}:6443
  1380.      delegate_to: 127.0.0.1
  1381.      become: false
  1382.      register: k3s_server_csa_result
  1383.      changed_when:
  1384.        - k3s_server_csa_result.rc == 0
  1385.  
  1386.    - name: Setup kubeconfig context on control node - mightykube
  1387.      ansible.builtin.replace:
  1388.        path: "~/.kube/config.new"
  1389.        regexp: 'default'
  1390.        replace: 'mightykube'
  1391.      delegate_to: 127.0.0.1
  1392.      become: false
  1393.  
  1394.    - name: Merge with any existing kubeconfig on control node
  1395.      ansible.builtin.shell: |
  1396.        TFILE=$(mktemp)
  1397.        KUBECONFIG=~/.kube/config.new:~/.kube/config kubectl config set-context mightykube --user=mightykube --cluster=mightykube
  1398.        KUBECONFIG=~/.kube/config.new:~/.kube/config kubectl config view --flatten &amp;gt; ${TFILE}
  1399.        mv ${TFILE} ~/.kube/config
  1400.      delegate_to: 127.0.0.1
  1401.      become: false
  1402.      register: k3s_server_mv_result
  1403.      changed_when:
  1404.        - k3s_server_mv_result.rc == 0
  1405.  
  1406. &lt;/code&gt;&lt;/pre&gt;
  1407. &lt;p&gt;Let's also create a template file for the systemd service under &lt;code&gt;~/Projects/infra/ansible/k3s/roles/k3s_server/templates&lt;/code&gt;&lt;/p&gt;
  1408. &lt;pre&gt;&lt;code&gt;[Unit]
  1409. Description=Lightweight Kubernetes
  1410. Documentation=https://k3s.io
  1411. Wants=network-online.target
  1412. After=network-online.target
  1413.  
  1414. [Install]
  1415. WantedBy=multi-user.target
  1416.  
  1417. [Service]
  1418. Type=notify
  1419. EnvironmentFile=-/etc/default/%N
  1420. EnvironmentFile=-/etc/sysconfig/%N
  1421. EnvironmentFile=-/etc/systemd/system/k3s.service.env
  1422. KillMode=process
  1423. Delegate=yes
  1424. # Having non-zero Limit*s causes performance problems due to accounting overhead
  1425. # in the kernel. We recommend using cgroups to do container-local accounting.
  1426. LimitNOFILE=1048576
  1427. LimitNPROC=infinity
  1428. LimitCORE=infinity
  1429. TasksMax=infinity
  1430. TimeoutStartSec=0
  1431. Restart=always
  1432. RestartSec=5s
  1433. ExecStartPre=-/sbin/modprobe br_netfilter
  1434. ExecStartPre=-/sbin/modprobe overlay
  1435. ExecStart=/usr/local/bin/k3s server
  1436.  
  1437. &lt;/code&gt;&lt;/pre&gt;
  1438. &lt;p&gt;Let's create a &lt;code&gt;~/Projects/infra/ansible/k3s/k3s_server/default/main.yaml&lt;/code&gt; to set the version of k3s we want to install, and that we might change in the future when doing upgrades.&lt;/p&gt;
  1439. &lt;pre&gt;&lt;code&gt;k3s_server_version: "v1.33.3+k3s1"
  1440. &lt;/code&gt;&lt;/pre&gt;
  1441. &lt;p&gt;Finally, let's create a &lt;code&gt;~/Projects/infra/ansible/k3s/deploy.yaml&lt;/code&gt; that calls the role we just created on the &lt;code&gt;k3s&lt;/code&gt; servers group.&lt;/p&gt;
  1442. &lt;pre&gt;&lt;code&gt;---
  1443. - name: Install k3s
  1444.  hosts: k3s
  1445.  remote_user: thib
  1446.  tasks:
  1447.    - name: Install k3s server
  1448.      ansible.builtin.import_role:
  1449.        name: k3s_server
  1450.  
  1451. &lt;/code&gt;&lt;/pre&gt;
  1452. &lt;p&gt;We can now use everything together by calling the playbook we created (and the role it calls) with the dynamic inventory generated by the Proxmox plugin. Let's try!&lt;/p&gt;
  1453. &lt;pre&gt;&lt;code&gt;$ cd ~/Projects/infra/ansible
  1454. $ ansible-playbook -i inventory/proximighty.proxmox.yaml k3s/deploy.yaml
  1455. &lt;/code&gt;&lt;/pre&gt;
  1456. &lt;p&gt;Using &lt;code&gt;kubectl&lt;/code&gt; on my laptop, I can confirm that my single node cluster is ready&lt;/p&gt;
  1457. &lt;pre&gt;&lt;code&gt;$ kubectl get nodes
  1458. NAME         STATUS   ROLES                  AGE   VERSION
  1459. mightykube   Ready    control-plane,master   4m   v1.33.3+k3s1
  1460. &lt;/code&gt;&lt;/pre&gt;
  1461. &lt;p&gt;Great! We have used ansible to automate the installation of a single node k3s cluster, and how to control it from our laptop. Thanks to our dynamic inventory, ansible also figured out what VM to install it onto automatically. Look, Mom! We have a Kubernetes at home!&lt;/p&gt;
  1462. &lt;p&gt;It's time to clean things up and remove sensitive credentials from our ansible scripts.&lt;/p&gt;
  1463. &lt;h3&gt;Removing sensitive information&lt;/h3&gt;
  1464. &lt;p&gt;When writing our ansible playbook, we didn't add new credentials. When setting up opentofu we created an API Key, and stored it in our Bitwarden vault under the name &lt;code&gt;PROXMOX_VE_API_TOKEN&lt;/code&gt;. When configuring the dynamic inventory, we reused that same API key but wrote it in the plain text file.&lt;/p&gt;
  1465. &lt;p&gt;There is a minor difference though. Opentofu uses the API Key formatted as&lt;/p&gt;
  1466. &lt;pre&gt;&lt;code&gt;terraform@pve!provider=REDACTED
  1467. &lt;/code&gt;&lt;/pre&gt;
  1468. &lt;p&gt;Ansible on the other hand uses the API Key formatted as&lt;/p&gt;
  1469. &lt;pre&gt;&lt;code&gt;user: "terraform@pve"
  1470. token_id: "provider"
  1471. token_secret: "REDACTED"
  1472. &lt;/code&gt;&lt;/pre&gt;
  1473. &lt;p&gt;The information is the same, but formatted differently. Fortunately for us, ansible supports &lt;a href="https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_filters.html#searching-strings-with-regular-expressions"&gt;searching strings with regular expressions&lt;/a&gt;. The regex to break it down into the three parts we need is rather simple:&lt;/p&gt;
  1474. &lt;pre&gt;&lt;code&gt;([^!]+)!([^=]+)=(.+)
  1475. &lt;/code&gt;&lt;/pre&gt;
  1476. &lt;p&gt;Ansible also has a &lt;a href="https://docs.ansible.com/ansible/latest/collections/ansible/builtin/env_lookup.html"&gt;lookup method&lt;/a&gt; to read environment variables. Let's put all the pieces together in our dynamic inventory file&lt;/p&gt;
  1477. &lt;pre&gt;&lt;code&gt;plugin: community.general.proxmox
  1478. url: https://192.168.1.200:8006
  1479. user: 'terraform@pve'
  1480. token_id: 'provider'
  1481. token_secret: 'REDACTED'
  1482. user: "{{ (lookup('ansible.builtin.env', 'PROXMOX_VE_API_TOKEN') | regex_search('([^!]+)!([^=]+)=(.+)', '\\1'))[0] }}"
  1483. token_id: "{{ (lookup('ansible.builtin.env', 'PROXMOX_VE_API_TOKEN') | regex_search('([^!]+)!([^=]+)=(.+)', '\\2'))[0] }}"
  1484. token_secret: "{{ (lookup('ansible.builtin.env', 'PROXMOX_VE_API_TOKEN') | regex_search('([^!]+)!([^=]+)=(.+)', '\\3'))[0] }}"
  1485. want_facts: true
  1486. groups:
  1487.  k3s: "'k3s' in (proxmox_tags_parsed|list)"
  1488. compose:
  1489.  ansible_host: proxmox_ipconfig0.ip | default(proxmox_net0.ip) | ansible.utils.ipaddr('address')
  1490. validate_certs: false
  1491. &lt;/code&gt;&lt;/pre&gt;
  1492. &lt;p&gt;VoilΓ ! Just like that we have removed the secrets from our files, and we're ready to commit them!&lt;/p&gt;
  1493. &lt;blockquote&gt;
  1494. &lt;p&gt;[!info] Why not use the &lt;a href="https://docs.ansible.com/ansible/latest/collections/community/general/bitwarden_lookup.html"&gt;Bitwarden plugin for ansible&lt;/a&gt;?&lt;/p&gt;
  1495. &lt;p&gt;It's a good alternative, but I already rely on direnv to extract the relevant secrets from my vault and store them temporarily in environment variables.&lt;/p&gt;
  1496. &lt;p&gt;Using the Bitwarden plugin in my playbook would tightly couple the playbook to ansible. By relying on the environment variables, only direnv is coupled to Bitwarden!&lt;/p&gt;
  1497. &lt;/blockquote&gt;
  1498. &lt;p&gt;Now we can spin up a VM and install k3s on it in a handful of seconds! Our homelab is making steady progress. Next up we will see how to get services running on our cluster with GitOps!&lt;/p&gt;
  1499. &lt;p&gt;&lt;em&gt;A huge thanks to my friends and colleagues Davide, &lt;a href="https://sandhose.fr"&gt;Quentin&lt;/a&gt;, Ark, &lt;a href="https://half-shot.uk"&gt;Half-Shot&lt;/a&gt;, and Ben!&lt;/em&gt;&lt;/p&gt;</description><author>Thibault Martin</author><dc:creator>Thibault Martin</dc:creator><pubDate>Wed, 20 Aug 2025 10:00:00 GMT</pubDate><guid isPermaLink="true">https://ergaster.org/posts/2025/08/20-cloud-tech-on-prem/</guid></item><item><title>Peter Hutterer: Why is my device a touchpad and a mouse and a keyboard?</title><link>http://who-t.blogspot.com/2025/08/why-is-my-device-touchpad-and-mouse-and.html</link><description>&lt;p&gt;
  1500. If you have spent any time around HID devices under Linux (for example if you
  1501. are an avid mouse, touchpad or keyboard user) then you may have noticed that
  1502. your single physical device actually shows up as multiple device nodes (for
  1503. free! and nothing happens for free these days!).
  1504. If you haven't noticed this, run &lt;code&gt;libinput record&lt;/code&gt; and you may be
  1505. part of the lucky roughly 50% who get free extra event nodes.
  1506. &lt;/p&gt;
  1507.  
  1508. &lt;p&gt;
  1509. The pattern is always the same. Assuming you have a device named
  1510. &lt;code&gt;FooBar ExceptionalDog 2000 AI&lt;/code&gt;[1] what you will see are multiple devices
  1511.  
  1512. &lt;pre&gt;
  1513. /dev/input/event0: FooBar ExceptionalDog 2000 AI Mouse
  1514. /dev/input/event1: FooBar ExceptionalDog 2000 AI Keybard
  1515. /dev/input/event2: FooBar ExceptionalDog 2000 AI Consumer Control
  1516. &lt;/pre&gt;
  1517.  
  1518. The Mouse/Keyboard/Consumer Control/... suffixes are a quirk of the kernel's
  1519. HID implementation which splits out a device based on the &lt;b&gt;Application Collection&lt;/b&gt;. [2]
  1520. &lt;/p&gt;
  1521. &lt;p&gt;
  1522. A &lt;a href="https://who-t.blogspot.com/2018/12/understanding-hid-report-descriptors.html"&gt;HID report descriptor&lt;/a&gt;
  1523. may use collections to group things together. A "Physical Collection" indicates
  1524. "these things are (on) the same physical thingy". A "Logical Collection" indicates
  1525. "these things belong together".  And you can of course nest these things
  1526. near-indefinitely so e.g. a logical collection inside a physical collection is
  1527. a common thing.
  1528. &lt;/p&gt;
  1529. &lt;p&gt;
  1530. An "Application Collection" is a high-level abstractions to group something together
  1531. so it can be detected by software. The "something" is defined by the HID usage for this
  1532. collection. For example, you'll never guess what this device might be based on the
  1533. &lt;a href="https://github.com/hidutils/hid-recorder"&gt;hid-recorder&lt;/a&gt; output:
  1534. &lt;pre&gt;
  1535. # 0x05, 0x01,                    // Usage Page (Generic Desktop)              0
  1536. # 0x09, 0x06,                    // Usage (Keyboard)                          2
  1537. # 0xa1, 0x01,                    // Collection (Application)                  4
  1538. ...
  1539. # 0xc0,                          // End Collection                            74
  1540. &lt;/pre&gt;
  1541.  
  1542. Yep, it's a keyboard. Pop the champagne[3] and hooray, you deserve it.
  1543. &lt;/p&gt;
  1544. &lt;p&gt;
  1545. The kernel, ever eager to help, takes top-level application collections (i.e.
  1546. those not inside another collection) and applies a usage-specific suffix to the
  1547. device. For the above Generic Desktop/Keyboard usage you get "Keyboard", the other
  1548. ones currently supported are "Keypad" and "Mouse" as well as the slightly more
  1549. niche "System Control", "Consumer Control" and "Wireless Radio Control" and
  1550. "System Multi Axis". In the Digitizer usage page we have "Stylus", "Pen",
  1551. "Touchscreen" and "Touchpad". Any other Application Collection is
  1552. currently unsuffixed (though see [2] again, e.g. the hid-uclogic driver uses
  1553. "Touch Strip" and other suffixes).
  1554. &lt;/p&gt;
  1555. &lt;p&gt;
  1556. This suffix is necessary because the kernel also splits out the data sent
  1557. within each collection as separate evdev event node. Since HID is (mostly)
  1558. hidden from userspace this makes it much easier for userspace to identify
  1559. different devices because you can look at a event node and say "well, it has
  1560. buttons and x/y, so must be a mouse" (this is exactly what udev does when applying
  1561.  the various &lt;code&gt;ID_INPUT&lt;/code&gt; properties, with varying
  1562. levels of success).
  1563. &lt;/p&gt;
  1564. &lt;p&gt;
  1565. The side effect of this however is that your device may show up as multiple
  1566. devices and &lt;i&gt;most of those extra devices will never send events&lt;/i&gt;. Sometimes
  1567. that is due to the device supporting multiple modes (e.g. a touchpad may by default emulate
  1568. a mouse for backwards compatibility but once the kernel toggles it to touchpad
  1569. mode the mouse feature is mute). Sometimes it's just laziness when vendors re-use
  1570. the same firmware and leave unused bits in place.
  1571. &lt;/p&gt;
  1572. &lt;p&gt;
  1573. It's &lt;i&gt;largely&lt;/i&gt; a cosmetic problem only, e.g. libinput treats every event
  1574. node as individual device and if there is a device that never sends events it
  1575. won't affect the other event nodes. It can cause user confusion though: "why
  1576. does my laptop say there's a mouse?" and in some cases it can cause functional
  1577. degradation - the two I can immediately recall are udev detecting the mouse
  1578. node of a touchpad as pointing stick (because i2c mice aren't a thing), hence
  1579. the pointing stick configuration may show up in unexpected places. And fake
  1580. mouse devices prevent features like "disable touchpad if a mouse is plugged in"
  1581. from working correctly. At the moment we don't have a good solution for detecting
  1582. these fake devices - short of shipping giant databases with product-specific entries
  1583. we cannot easily detect which device is fake. After all, a Keyboard node on a
  1584. gaming mouse may only send events if the user configured the firmware to send
  1585. keyboard events, and the same is true for a Mouse node on a gaming keyboard.
  1586. &lt;/p&gt;
  1587. &lt;p&gt;
  1588. So for now, the only solution to those is a per-user
  1589. &lt;a href="https://wayland.freedesktop.org/libinput/doc/latest/ignoring-devices.html"&gt;udev rule to ignore a device&lt;/a&gt;. If we ever
  1590. figure out a better fix, expect to find a gloating blog post in this very space.
  1591. &lt;/p&gt;
  1592.  
  1593. &lt;p&gt;
  1594. &lt;small&gt;
  1595. [1] input device naming is typically bonkers, so I'm just sticking with precedence here&lt;br /&gt;
  1596. [2] if there's a custom kernel driver this may not apply and there are quirks to change this so this isn't true for all devices&lt;br /&gt;
  1597. [3] or sparkling wine, let's not be regionist here&lt;br /&gt;
  1598. &lt;/small&gt;
  1599. &lt;/p&gt;</description><author>Peter Hutterer</author><dc:creator>Peter Hutterer</dc:creator><pubDate>Wed, 20 Aug 2025 11:12:00 GMT</pubDate><guid isPermaLink="true">http://who-t.blogspot.com/2025/08/why-is-my-device-touchpad-and-mouse-and.html</guid></item><item><title>Cassidy James Blaede: Here’s to What’s Next</title><link>https://cassidyjames.com/blog/heres-to-whats-next/</link><description>&lt;img src="https://cassidyjames.com/images/binoculars.jpg" /&gt;
  1600.              
  1601.            
  1602.  
  1603.            &lt;p&gt;For the past three and a half years, I’ve done the most rewarding work of my life with &lt;a href="https://endlessaccess.org"&gt;Endless Access&lt;/a&gt; (formerly Endless OS Foundation). I’ve helped ship Endless OS on computers to tens of thousands of people in need around the globeβ€”people who might not otherwise have access to a real computer and especially reliable access to all of the offline knowledge and tools that come out of the box. I’ve visited students and teachers in the rural US who are struggling due to a lack of funding, and watched as their eyes lit up when they got ahold of Endless Key, an app packed full of safe and fun offline materials to support their self-guided learning. And I’ve helped learners and educators both unlock so much creativity and skill-building with our game-making learning programs and related open source projects.&lt;/p&gt;
  1604.  
  1605. &lt;p&gt;Unfortunately, all good things must come to an end: due to strategic decisions at the organization, my particular role won’t exist in the coming months. That said, I have some time to look for my next challenge and adventureβ€”and I am excited to do so purposefully.&lt;/p&gt;
  1606.  
  1607. &lt;h3 id="what-is-next"&gt;What &lt;em&gt;is&lt;/em&gt; next?&lt;/h3&gt;
  1608.  
  1609. &lt;p&gt;&lt;img alt="Binoculars" class="card" src="https://cassidyjames.com/images/binoculars.jpg" /&gt;&lt;/p&gt;
  1610.  
  1611. &lt;p&gt;At the end of the day, my family has to eat and we have to pay for a roof over our heads; otherwise, retiring into the wilderness to raise my kids, garden, and play with LEGO would sound pretty nice right about now! Given that I need a decent income to support us, I’m currently looking for a role with some combination of the following:&lt;/p&gt;
  1612.  
  1613. &lt;ul&gt;
  1614.  &lt;li&gt;open source&lt;/li&gt;
  1615.  &lt;li&gt;connecting people&lt;/li&gt;
  1616.  &lt;li&gt;empowering people to build things&lt;/li&gt;
  1617.  &lt;li&gt;tech for good&lt;/li&gt;
  1618. &lt;/ul&gt;
  1619.  
  1620. &lt;p&gt;These are both broad and vague because I don’t have a perfect vision of what I want; my ideal job would hit all four of those, but I’m also a realist and know that I might not be able to support my family with some utopian gig working on something like GNOME with my specific skills and experience. (That said, if you’d like to hire me for that, I’m all ears!) I would love to continue working at a nonprofit, but I’m open to a company that is strongly aligned with my values. I’d prefer to work on a core product that is open source, but I could see myself working for an org that at least substantially supports open source. I don’t think I want to be a community managerβ€”unless I can be convinced the org knows precisely what they want and that lines up with my approachβ€”but I do have a passion for connecting with people and helping them.&lt;/p&gt;
  1621.  
  1622. &lt;p&gt;If you know of something that sounds like it would be a good fit, please reach out via &lt;a href="mailto:job@cassidyjames.com"&gt;email&lt;/a&gt;, &lt;a href="https://signal.me/#eu/MaAGJHh_C36AKgv_VOidJ0e2WrFDp1TFEGV88d1GF_sDioh_00NLP6FmKtvJ1OmO"&gt;Signal&lt;/a&gt;, &lt;a href="https://matrix.to/#/@cassidyjames:gnome.org"&gt;Matrix&lt;/a&gt;, or &lt;a href="https://mastodon.blaede.family/@cassidy"&gt;Mastodon&lt;/a&gt;. You can also check out my &lt;a href="https://cassidyjames.com/resume"&gt;rΓ©sumΓ©&lt;/a&gt; or &lt;a href="https://linkedin.com/in/cassidy-james-blaede"&gt;connect on LinkedIn&lt;/a&gt;.&lt;/p&gt;
  1623.  
  1624. &lt;p&gt;And don’t worry: no matter what I end up taking on next, I plan to keep my volunteer position on the GNOME Foundation Board of Directors and stick around the GNOME and Flathub communities to help as much as I am able. πŸ˜‰&lt;/p&gt;</description><author>Cassidy James Blaede</author><dc:creator>Cassidy James Blaede</dc:creator><pubDate>Wed, 20 Aug 2025 00:00:00 GMT</pubDate><guid isPermaLink="true">https://cassidyjames.com/blog/heres-to-whats-next/</guid></item><item><title>Michael Meeks: 2025-08-19 Tuesday</title><link>https://meeksfamily.uk/~michael/blog/2025-08-19.html</link><description>&lt;ul&gt; &lt;!-- --&gt;
  1625. &lt;li&gt;
  1626. Up early, breakfast, bid a fond farewell to the American
  1627. Meeks' - into planning call, lunch, sync with Laser.
  1628. &lt;/li&gt;
  1629. &lt;li&gt;
  1630. Monthly Management meeting, customer sync, admin catch-up,
  1631. sync with Lily, dinner.
  1632. &lt;/li&gt;
  1633. &lt;/ul&gt;</description><author>Michael Meeks</author><dc:creator>Michael Meeks</dc:creator><pubDate>Tue, 19 Aug 2025 21:00:00 GMT</pubDate><guid isPermaLink="true">https://meeksfamily.uk/~michael/blog/2025-08-19.html</guid></item><item><title>Sam Thursfield: Status update, 19/08/2025</title><link>https://samthursfield.wordpress.com/2025/08/19/status-update-19-08-2025/</link><description>&lt;p&gt;Hello! I&amp;#8217;m working on an interesting project this month, related to open source Linux operating systems. Actually I&amp;#8217;m not working on it this week, I&amp;#8217;m instead battling with vinyl floor tiles. Don&amp;#8217;t let anyone tell you they are easy to fit. But I think on the 5th attempt I&amp;#8217;ve got the technique. The wooden mallet is essential.&lt;/p&gt;
  1634.  
  1635.  
  1636.  
  1637. &lt;figure class="wp-block-image size-large is-resized"&gt;&lt;a href="https://samthursfield.wordpress.com/wp-content/uploads/2025/08/img_20250818_163348.jpg"&gt;&lt;img alt="Vinyl floor tiles, frustrated face" class="wp-image-3051" height="1024" src="https://samthursfield.wordpress.com/wp-content/uploads/2025/08/img_20250818_163348.jpg?w=768" style="width: 800px; height: 800px;" width="768" /&gt;&lt;/a&gt;&lt;/figure&gt;
  1638.  
  1639.  
  1640.  
  1641. &lt;p&gt;When I started these &amp;#8220;status update&amp;#8221; posts, back in &lt;a href="https://samthursfield.wordpress.com/2021/11/17/status-update-november-2021/"&gt;2021&lt;/a&gt;, I imagined they&amp;#8217;d be to talk about open technology projects I was working on, mixed with a bit of music and art and so on. In fact I write more about politics these days. Let me explain why.&lt;/p&gt;
  1642.  
  1643.  
  1644.  
  1645. &lt;p&gt;In my &lt;a href="https://samthursfield.wordpress.com/2025/05/18/book-club-2025-edition/"&gt;book review &lt;/a&gt;earlier this year I mentioned economics dude Gary Stevenson. I still didn&amp;#8217;t read his book but I do watch all his videos now and I&amp;#8217;m learning a lot.&lt;/p&gt;
  1646.  
  1647.  
  1648.  
  1649. &lt;p&gt;I learned a bit about the housing crisis, for example. The housing crisis in Manchester had several major effects on my life. I just read today in &lt;a href="https://manchestermill.co.uk/living-in-exile-the-saharawis-find-home-in-levenshulme/?ref=the-mill-newsletter"&gt;The Manchester Mill&lt;/a&gt; that the average rent in Salford jumped from Β£640/mo to Β£1,121/mo in a decade. &lt;/p&gt;
  1650.  
  1651.  
  1652.  
  1653. &lt;p&gt;(Lucky for me, I got into computers early in life, and nobody understands their secrets so they still have to pay a good salary to those of us who do. So far I&amp;#8217;ve weathered the crisis. Many of my friends don&amp;#8217;t have that luck, and some of them have been struggling for 15 years already. Some even had to become &lt;em&gt;project managers&lt;/em&gt;.)&lt;/p&gt;
  1654.  
  1655.  
  1656.  
  1657. &lt;p&gt;Until about 2020, I assumed the Manchester housing crisis was caused by people moving up from London. Galicia had some of the lowest rent I&amp;#8217;d ever seen, when I first moved here, and it&amp;#8217;s only around 2021, when rents started suddenly doubling just as I&amp;#8217;d seen happen in Manchester, that I realised the same crisis was about to play out here as well, and perhaps it wasn&amp;#8217;t entirely the fault of Gen-X Londoners. I thought, maybe it&amp;#8217;s a Europe-wide housing crisis?&lt;/p&gt;
  1658.  
  1659.  
  1660.  
  1661. &lt;p&gt;Let me summarize the video Gary Stevenson did about the housing crisis (&lt;a href="https://www.youtube.com/watch?v=BTlUyS-T-_4"&gt;this one&lt;/a&gt;), to save you 28 minutes. It&amp;#8217;s not just houses but &lt;em&gt;all types of asset&lt;/em&gt; which are rapidly going up in price, and it&amp;#8217;s happening worldwide. We notice the houses because we need them to live normal lives, unlike other types of asset such as gold or government bonds which most of us can live without.&lt;/p&gt;
  1662.  
  1663.  
  1664.  
  1665. &lt;p&gt;The most recent video is titled like this: &lt;strong&gt;&amp;#8220;Is the economy causing a mental health crisis?&lt;/strong&gt;&amp;#8220;. I&amp;#8217;ve embedded it below. (&lt;em&gt;It&amp;#8217;s hosted on a platform controlled by Google, but Gary is good enough to turn off the worst of the YouTube ads, those bastards that pop up during a video in mid-sentence or while you&amp;#8217;re figuring out a yoga pose&lt;/em&gt;.)&lt;/p&gt;
  1666.  
  1667.  
  1668.  
  1669. &lt;figure class="wp-block-embed is-type-video is-provider-youtube wp-block-embed-youtube wp-embed-aspect-16-9 wp-has-aspect-ratio"&gt;&lt;div class="wp-block-embed__wrapper"&gt;
  1670. &lt;div class="jetpack-video-wrapper"&gt;&lt;div class="embed-youtube"&gt;&lt;/div&gt;&lt;/div&gt;
  1671. &lt;/div&gt;&lt;/figure&gt;
  1672.  
  1673.  
  1674.  
  1675. &lt;p&gt;My answer to that question, when I saw it, was &amp;#8220;&lt;strong&gt;Yes, obviously&lt;/strong&gt;&amp;#8220;. For example, if rent increases by 75% in your city and you&amp;#8217;re forced back into living with your parents age 35, it&amp;#8217;s tough to deal with alright. What do you think? &lt;/p&gt;
  1676.  
  1677.  
  1678.  
  1679. &lt;p&gt;But the video is about more than that. The reason asset prices are through the roof is because &lt;em&gt;the super rich are taking all the assets&lt;/em&gt;. The 1% have more money than ever. Wealth inequality is rapidly rising, and nothing is stopping it. For thousands of years, the aristocracy owned all the land and castles and manor houses, and the rest of us had a few cabbages and, perhaps if you were middle class, a pig.&lt;/p&gt;
  1680.  
  1681.  
  1682.  
  1683. &lt;p&gt;The second half of the 20th century levelled the playing field and put in place systems which evened things out and meant your grandparents maybe could buy a house. The people in charge of those systems have given up, or have been overpowered by the super rich.&lt;/p&gt;
  1684.  
  1685.  
  1686.  
  1687. &lt;p&gt;In fact, the video &amp;#8220;Is the economy causing a mental health crisis?&amp;#8221; is about the effect on your mental health when you realize that all of society as you know it is headed towards complete collapse.&lt;/p&gt;
  1688.  
  1689.  
  1690.  
  1691. &lt;p&gt;(Lucky for me, I grew up thinking society was headed for collapse due to the climate crisis, so I listened to a lot of punk rock and over-developed my capacity for nihilism. Maybe my mind looks for crises everywhere? Or maybe I was born in a time well-supplied with global crises. I share a birthday with the Chernobyl disaster.)&lt;/p&gt;
  1692.  
  1693.  
  1694.  
  1695. &lt;p&gt;So how does all this relate back to open technology?&lt;/p&gt;
  1696.  
  1697.  
  1698.  
  1699. &lt;p&gt;Maybe it doesn&amp;#8217;t. I went to the GNOME conference last month and had very little overtly &amp;#8220;political&amp;#8221; conversations. We chatted about GNOME OS, live-streaming, openQA, the GNOME Foundation, the history of the GNOME project, accessibility at conferences, our jobs, and so on. Which was great, I for some reason find all that stuff mega interesting. (Hence why I went there instead of a conference about 21st century world politics).&lt;/p&gt;
  1700.  
  1701.  
  1702.  
  1703. &lt;p&gt;Or maybe it does. Tech is part of everyone&amp;#8217;s lives now. Some of the most powerful organizations in the world now are tech companies and they get their power &lt;em&gt;from being omnipresent. &lt;/em&gt;Software engineers &lt;em&gt;built &lt;/em&gt;all of this. What were we thinking? &lt;/p&gt;
  1704.  
  1705.  
  1706.  
  1707. &lt;p&gt;I think we just enjoyed getting paid to work on fun problems. I suppose none of today&amp;#8217;s tech billionaires seemed like particularly evil-minded people in the mid 2000s. Spotify used to talk about reducing MP3 piracy, not gutting the income streams of 95% of professional recording artists. Google used to have a now laughable policy of &amp;#8220;Don&amp;#8217;t be evil&amp;#8221;.&lt;/p&gt;
  1708.  
  1709.  
  1710. &lt;div class="wp-block-image"&gt;
  1711. &lt;figure class="aligncenter size-large"&gt;&lt;a href="https://samthursfield.wordpress.com/wp-content/uploads/2025/08/image-1.png"&gt;&lt;img alt="" class="wp-image-3040" height="400" src="https://samthursfield.wordpress.com/wp-content/uploads/2025/08/image-1.png?w=400" width="400" /&gt;&lt;/a&gt;&lt;/figure&gt;&lt;/div&gt;
  1712.  
  1713.  
  1714. &lt;p&gt;There is one exception who were clearly evil bastards in the 2000s as well. The US anti-trust case against Microsoft, settled in 2001, is an evidence trail of lies and anti-competitive behaviour under Bill Gates&amp;#8217; leadership. Perhaps in an attempt to one-up his predecessor, the Satya Nadella Microsoft is now helping the far-right government of Israel to commit war crimes every day. &lt;a href="https://noazureforapartheid.com/"&gt;No Azure for Apartheid&lt;/a&gt;. At least they are consistent, I suppose.&lt;/p&gt;
  1715.  
  1716.  
  1717.  
  1718. &lt;p&gt;In fact, I first got interested in Linux due to Microsoft. Initially for selfish reasons. I was a child with a dialup internet connection, and I just wanted to have 5 browser windows open without the OS crashing. (For younger readers &amp;#8212; browser tabs weren&amp;#8217;t invented until the 21st century).&lt;/p&gt;
  1719.  
  1720.  
  1721.  
  1722. &lt;p&gt;Something has kept me interested in open operating systems, even in this modern era when you can download an MP3 in 5 seconds instead of 5 consecutive evenings. It&amp;#8217;s partly the community of fun people around Linux. It&amp;#8217;s partly that it led me to the job that has seen me through the housing crisis so far. And its partly the sense that we&lt;em&gt; are&lt;/em&gt; the alternative to Big Tech.&lt;/p&gt;
  1723.  
  1724.  
  1725.  
  1726. &lt;p&gt;Open source isn&amp;#8217;t going to &amp;#8220;take over the world&amp;#8221;. That&amp;#8217;s what Microsoft, Spotify and Google were always going to do (and have now done). I&amp;#8217;m honestly not sure where open source is going.  Linux will go wherever hardware manufacturers force it to go, as it always has done.&lt;/p&gt;
  1727.  
  1728.  
  1729.  
  1730. &lt;p&gt;GNOME may or may not make it mainstream one day. I&amp;#8217;m all for it, if it means some funding for &lt;a href="https://gitlab.gnome.org/GNOME/localsearch/"&gt;localsearch&lt;/a&gt; maintainers. If it doesn&amp;#8217;t, that&amp;#8217;s also fine, and we don&amp;#8217;t &lt;em&gt;need &lt;/em&gt;to be part of some coherent plan to save the world or to achieve a particular political aim. Nothing goes according to plan anyway. Its fine to work on stuff just cus its interesting. &lt;/p&gt;
  1731.  
  1732.  
  1733.  
  1734. &lt;p&gt;What we &lt;em&gt;are&lt;/em&gt; doing is leading by example, showing that its &lt;em&gt;possible&lt;/em&gt; to develop high quality software independently of any single corporation. You can create institutions where contributors do what we think is right, instead of doing what lunatics like Sam Altman or Mark Zockerborg think.&lt;/p&gt;
  1735.  
  1736.  
  1737.  
  1738. &lt;p&gt;At the same time, &lt;em&gt;everything&lt;/em&gt; is political.&lt;/p&gt;
  1739.  
  1740.  
  1741.  
  1742. &lt;p&gt;What would happen if I travelled back to 2008 and asked the PHP developers building Facebook: &amp;#8220;Do you think this thing could play a determining role in a genocide in Myanmar?&amp;#8221;&lt;/p&gt;
  1743.  
  1744.  
  1745.  
  1746. &lt;p&gt;I met someone this weekend who had just quit Spotify. She isn&amp;#8217;t a tech person. She didn&amp;#8217;t even know Bandcamp exists. Just didn&amp;#8217;t want to give more money to a company that&amp;#8217;s clearly evil. This is the future of tech, if there is any. People who pay attention to the world, who are willing to do things the hard way and stop giving money to people who are clearly evil.&lt;/p&gt;
  1747.  
  1748.  
  1749.  
  1750. &lt;p&gt;&lt;/p&gt;
  1751.  
  1752.  
  1753.  
  1754. &lt;p&gt;&lt;/p&gt;</description><author>Sam Thursfield</author><dc:creator>Sam Thursfield</dc:creator><pubDate>Tue, 19 Aug 2025 13:05:21 GMT</pubDate><guid isPermaLink="true">https://samthursfield.wordpress.com/2025/08/19/status-update-19-08-2025/</guid></item><item><title>Christian Hergert: Status Week 33</title><link>https://blogs.gnome.org/chergert/2025/08/18/status-week-33/</link><description>&lt;p&gt;This week is still largely focused on finalizing API/ABI for the 1.0 of Foundry in a few weeks.&lt;/p&gt;
  1755. &lt;h2&gt;Foundry&lt;/h2&gt;
  1756. &lt;ul&gt;
  1757. &lt;li&gt;
  1758. &lt;p&gt;Did a bunch of work on LLM completion and and conversation APIs.    They are not focused on supporting everything possible but instead    making some of the common stuff extremely simple. That goes for both    the model size of things and the UI side of things.&lt;/p&gt;
  1759. &lt;p&gt;For example, heavy usage of GListModel everywhere we can.&lt;/p&gt;
  1760. &lt;/li&gt;
  1761. &lt;li&gt;
  1762. &lt;p&gt;Created new abstractions for LlmTool, LlmToolProviders, and the    actual call of a tool (aptly, LlmToolCall). One reason this all    takes so much time to scaffold is that you want to allow some amount    of flexibility when connecting models, but also avoid too much API    surface area.&lt;/p&gt;
  1763. &lt;p&gt;I think I&amp;#8217;ve managed to do that here.&lt;/p&gt;
  1764. &lt;/li&gt;
  1765. &lt;li&gt;
  1766. &lt;p&gt;Landed Ollama implementation of the &lt;code&gt;FoundryLlmConversation&lt;/code&gt; API.    The ollama server appears to be stateless, which means copying the    conversation over-and-over as you go. I guess this at least gives    you an idea of your context window.&lt;/p&gt;
  1767. &lt;/li&gt;
  1768. &lt;li&gt;
  1769. &lt;p&gt;Setup a couple tool call implementations to test out that    infrastructure. For example, it&amp;#8217;s really easy to tell the model that    you build with &lt;code&gt;build&lt;/code&gt; tool and then provide it the results.&lt;/p&gt;
  1770. &lt;/li&gt;
  1771. &lt;li&gt;
  1772. &lt;p&gt;Fixed some licensing issues where I mostly just forgot to update    the headers when copying them over. Things should be in a good    place now for distributions to adhere to their SPDX rules.&lt;/p&gt;
  1773. &lt;/li&gt;
  1774. &lt;li&gt;
  1775. &lt;p&gt;Language settings now have a very last resort setting which are the    &amp;#8220;defaults&amp;#8221; we ship with the library. That is just sensible stuff    like using 4 spaces for tabs/indent in Python.&lt;/p&gt;
  1776. &lt;p&gt;Settings at any layer can override these values.&lt;/p&gt;
  1777. &lt;/li&gt;
  1778. &lt;li&gt;
  1779. &lt;p&gt;Lots of work on project templates. We have both GTK 4 and Adwaita    templates again. They support C/Python/rust/JavaScript like Builder    does too.&lt;/p&gt;
  1780. &lt;p&gt;But this time I tried to go a bit further. They should have a bunch    of integration bits setup which we didn&amp;#8217;t get to before.&lt;/p&gt;
  1781. &lt;/li&gt;
  1782. &lt;li&gt;
  1783. &lt;p&gt;Setup an example Flatpak manifest for applications wanting to use    libfoundry (see &lt;code&gt;examples/flatpak/&lt;/code&gt;) that should help get you    started.&lt;/p&gt;
  1784. &lt;/li&gt;
  1785. &lt;li&gt;
  1786. &lt;p&gt;Setup i18n/l10n for &lt;code&gt;libfoundry&lt;/code&gt;. I don&amp;#8217;t think anything is    consuming translations for GNOME 49 though, so mostly just gets    us up and running for 50.&lt;/p&gt;
  1787. &lt;/li&gt;
  1788. &lt;li&gt;
  1789. &lt;p&gt;Landed some new API for working with the stage/index within    &lt;code&gt;FoundryGitVcs&lt;/code&gt;. Tested it with a speed-run challenge a bit    later on in this report.&lt;/p&gt;
  1790. &lt;/li&gt;
  1791. &lt;/ul&gt;
  1792. &lt;h2&gt;Assist&lt;/h2&gt;
  1793. &lt;ul&gt;
  1794. &lt;li&gt;
  1795. &lt;p&gt;To test out the LLM APIs and ensure they can actually be used I did    a speed-run to implement a &amp;#8220;Foundry-based Developer Chat&amp;#8221; with a    time limit of two hours.&lt;/p&gt;
  1796. &lt;p&gt;The reality is that I&amp;#8217;m still _much_ faster writing code with all    of my templates and snippets than I thought.&lt;/p&gt;
  1797. &lt;p&gt;The new templates in Foundry are killer though.&lt;/p&gt;
  1798. &lt;/li&gt;
  1799. &lt;li&gt;
  1800. &lt;p&gt;It requires a model which supports tool calls if you want to do    anything interesting with it. I&amp;#8217;m not sure if there are models    which can do both written output _and_ tool-calls which makes this    a bit annoying to wait while it figures out it should call a tool.&lt;/p&gt;
  1801. &lt;/li&gt;
  1802. &lt;li&gt;
  1803. &lt;p&gt;While doing this, I realized a bunch of little things to fix in the    LLM APIs. One piece still missing that I&amp;#8217;d want to have in the    future is the ability for specialized &lt;code&gt;FoundryLlmMessage&lt;/code&gt; which    not only have text content but typed data as well.&lt;/p&gt;
  1804. &lt;p&gt;For example, a tool call that is essentially a &lt;code&gt;ls&lt;/code&gt; should really    display the output as an interactive directory list and not text.&lt;/p&gt;
  1805. &lt;p&gt;But since this was a speed run, I did not implement that. Only made    sure that the APIs could adapt to it in the future.&lt;/p&gt;
  1806. &lt;/li&gt;
  1807. &lt;/ul&gt;
  1808. &lt;h2&gt;Staged&lt;/h2&gt;
  1809. &lt;ul&gt;
  1810. &lt;li&gt;
  1811. &lt;p&gt;Started another speed-run app to test out the version control engine    we have in Foundry. This one is basically just to replace my very    quick use of &lt;code&gt;git-gui&lt;/code&gt; to line stage patches.&lt;/p&gt;
  1812. &lt;p&gt;Came up with a neat way to highlight old/new versions of a file    and then display them with &lt;code&gt;GtkListView&lt;/code&gt; instead of using a source    view. No reason to power up the editing infrastructure if you&amp;#8217;ll    never be editing.&lt;/p&gt;
  1813. &lt;/li&gt;
  1814. &lt;/ul&gt;
  1815. &lt;h2&gt;Manuals&lt;/h2&gt;
  1816. &lt;ul&gt;
  1817. &lt;li&gt;
  1818. &lt;p&gt;Discovered I wasn&amp;#8217;t getting notifications since the move to the    &lt;code&gt;GNOME/&lt;/code&gt; namespace so flushed out the backlog of MR there.&lt;/p&gt;
  1819. &lt;/li&gt;
  1820. &lt;/ul&gt;
  1821. &lt;h2&gt;GtkSourceView&lt;/h2&gt;
  1822. &lt;ul&gt;
  1823. &lt;li&gt;
  1824. &lt;p&gt;Fix click-through on the overview map which broke again during this    development cycle. My fault for not reviewing and/or testing better.&lt;/p&gt;
  1825. &lt;/li&gt;
  1826. &lt;li&gt;
  1827. &lt;p&gt;Now that we have GNOME CI doing LSAN/ASAN/UBSAN/coverage/scanbuild    I went ahead and fixed a bunch of leaks that are part of the    testsuite.&lt;/p&gt;
  1828. &lt;p&gt;Additionally, it helped me find a few that were there in everyday    code use, so that is always a lovely thing to fix.&lt;/p&gt;
  1829. &lt;/li&gt;
  1830. &lt;/ul&gt;
  1831. &lt;h2&gt;Ptyxis&lt;/h2&gt;
  1832. &lt;ul&gt;
  1833. &lt;li&gt;
  1834. &lt;p&gt;Merge some last minute string changes before we can&amp;#8217;t anymore.&lt;/p&gt;
  1835. &lt;/li&gt;
  1836. &lt;li&gt;
  1837. &lt;p&gt;Still having to unfortunately close issues which come from Debian    not sourcing &lt;code&gt;/etc/profile.d/vte.sh&lt;/code&gt; by default, thus breaking    integration features.&lt;/p&gt;
  1838. &lt;p&gt;The good news I hear is that will be changing before long.&lt;/p&gt;
  1839. &lt;/li&gt;
  1840. &lt;li&gt;
  1841. &lt;p&gt;Other good news is that Ptyxis has landed in the 25.10 builds and    will also be landing in Debian unstable in the near future as the    default terminal.&lt;/p&gt;
  1842. &lt;/li&gt;
  1843. &lt;li&gt;
  1844. &lt;p&gt;After some back-and-forth I merged support for the kgx palette as    the &amp;#8220;GNOME&amp;#8221; palette in Ptyxis. My very hopeful desire is that this    becomes something maintained by the design team. The problem is just    that terminal colors are a huge piles of hacks on hacks.&lt;/p&gt;
  1845. &lt;/li&gt;
  1846. &lt;li&gt;
  1847. &lt;p&gt;Nightly builds should be fixed. Apparently something changed in the    CI setup and since we&amp;#8217;re under &lt;code&gt;chergert/ptyxis/&lt;/code&gt; and not &lt;code&gt;GNOME/&lt;/code&gt;    it didn&amp;#8217;t get automatically applied.&lt;/p&gt;
  1848. &lt;/li&gt;
  1849. &lt;li&gt;
  1850. &lt;p&gt;Some styling changed in libadwaita this cycle and I needed to adapt    how we propagate our styling to tab close buttons.&lt;/p&gt;
  1851. &lt;p&gt;Really though, this all just needs to be redone (like Text Editor    and Builder) to use &lt;code&gt;var()&lt;/code&gt; properly in CSS.&lt;/p&gt;
  1852. &lt;/li&gt;
  1853. &lt;/ul&gt;
  1854. &lt;h2&gt;Libspelling&lt;/h2&gt;
  1855. &lt;ul&gt;
  1856. &lt;li&gt;
  1857. &lt;p&gt;Merged patch improving life-cycle tracking of the piecetable/b+tree    regions (branches/leaves).&lt;/p&gt;
  1858. &lt;/li&gt;
  1859. &lt;/ul&gt;
  1860. &lt;h2&gt;Sysprof&lt;/h2&gt;
  1861. &lt;ul&gt;
  1862. &lt;li&gt;
  1863. &lt;p&gt;More code review and future feature planning so we can land GSoC    things after I branch for 49 (very soon I hope).&lt;/p&gt;
  1864. &lt;/li&gt;
  1865. &lt;/ul&gt;
  1866. &lt;h2&gt;Other&lt;/h2&gt;
  1867. &lt;ul&gt;
  1868. &lt;li&gt;
  1869. &lt;p&gt;Turned 41, saw Stevie Ray Vaughan&amp;#8217;s broadcaster guitar, finally had    the &amp;#8220;weird&amp;#8221; pizza at Lovely&amp;#8217;s fifty/fifty, jammed at MoPOP with my    niece.&lt;/p&gt;
  1870. &lt;/li&gt;
  1871. &lt;li&gt;
  1872. &lt;p&gt;Lots of random little things this week to lend a hand/ear here or    there as we get closer to release.&lt;/p&gt;
  1873. &lt;/li&gt;
  1874. &lt;/ul&gt;</description><author>Christian Hergert</author><dc:creator>Christian Hergert</dc:creator><pubDate>Mon, 18 Aug 2025 23:59:06 GMT</pubDate><guid isPermaLink="true">https://blogs.gnome.org/chergert/2025/08/18/status-week-33/</guid></item><item><title>Sebastian Wick: Display Next Hackfest 2025</title><link>https://blog.sebastianwick.net/posts/display-next-hackfest-2025/</link><description>&lt;p&gt;A few weeks ago, a bunch of display driver and compositor developers met once again for the third iteration of the Display Next Hackfest. The tradition was started by Red Hat, followed by Igalia (thanks Melissa), and now AMD (thanks Harry). We met in the AMD offices in Markham,  Ontario, Canada; and online, to discuss issues, present things we worked on, figure out future steps on a bunch of topics related to displays, GPUs, and compositors.&lt;/p&gt;
  1875. &lt;figure&gt;&lt;img alt="A bunch of people with laptops sitting at desks facing each other. A screens in the background with remote attendees." src="https://blog.sebastianwick.net/posts/display-next-hackfest-2025/display-hackfest-next-0.jpeg" /&gt;&lt;figcaption&gt;
  1876.      &lt;h4&gt;The Display Next Hackfest in the AMD Markham offices&lt;/h4&gt;
  1877.    &lt;/figcaption&gt;
  1878. &lt;/figure&gt;
  1879.  
  1880. &lt;p&gt;It was really nice meeting everyone again, and also seeing some new faces! Notably, &lt;a href="https://poynton.ca/"&gt;Charles Poynton&lt;/a&gt; who &amp;ldquo;decided that HD should have 1080 image rows, and square pixels&amp;rdquo;, and Keith Lee who works for AMD and designed their color pipeline, joined us this year. This turned out to be invaluable. It was also great to see AMD not only organizing the event, but also showing genuine interest and support for what we are trying to achieve.&lt;/p&gt;
  1881. &lt;p&gt;This year&amp;rsquo;s edition is likely going to be the last dedicated Display Next Hackfest, but we’re already plotting to somehow fuse it with XDC next year in some way.&lt;/p&gt;
  1882. &lt;p&gt;If you’re looking for a more detailed technical rundown of what we were doing there, you can read &lt;a href="https://zamundaaa.github.io/wayland/2025/07/22/display-next-hackfest-2025.html"&gt;Xaver’s&lt;/a&gt;, or &lt;a href="https://bootlin.com/blog/back-from-display-next-hackfest-2025/"&gt;Louis’&lt;/a&gt; blog posts, or our &lt;a href="https://hwentland.github.io/work/2025hackfest-notes.html"&gt;notes&lt;/a&gt;.&lt;/p&gt;
  1883. &lt;p&gt;With all that being said, here is an incomplete list of things I found exciting:&lt;/p&gt;
  1884. &lt;ul&gt;
  1885. &lt;li&gt;The biggest update of the Atomic KMS API (used to control displays) is about to get merged. The Color Pipeline API is something &lt;a href="https://gitlab.freedesktop.org/pq/color-and-hdr/-/issues/11"&gt;I came up with&lt;/a&gt; three years ago, and thanks to the tireless efforts of AMD, Intel and Igalia and others, this is about to become reality. Read &lt;a href="https://melissawen.github.io/blog/2025/05/19/drm-info-with-kms-color-api"&gt;Melissa’s blog post&lt;/a&gt; for more details.&lt;/li&gt;
  1886. &lt;li&gt;As part of the work enabling displaying HDR content in Wayland compositors, we&amp;rsquo;ve been unhappy with the current HDR modes in displays, as they are essentially created for video playback and have lots of unpredictable behavior. To address this, myself and Xaver have since last year been lobbying for displays to allow the use of Source Based Tone Mapping (SBTM), and this year, it seems that what we have asked for have made it to the right people. Let&amp;rsquo;s see!&lt;/li&gt;
  1887. &lt;li&gt;In a similar vein, on mobile devices we want to dynamically increase or decrease the HDR headroom, depending on what content applications want to show. This requires backlight changes to be somewhat atomic and having a mapping to luminance. The planned KMS backlight API will allow us to expose this, if the platform supports it. I worked a lot on backlight support in mutter this year so we can immediately start using this when it becomes available.&lt;/li&gt;
  1888. &lt;li&gt;Charles, Christopher, and I had a discussion about compositing HDR and SDR content, and specifically about how to adjust content that was mastered for a dark viewing environment that is being shown in a bright viewing environment, so that the perception is maintained. I believe that we now have a complete picture of how compositing should work, and I’m working on documenting this in the &lt;a href="https://gitlab.freedesktop.org/pq/color-and-hdr/"&gt;color-and-hdr&lt;/a&gt; repo.&lt;/li&gt;
  1889. &lt;li&gt;For Variable Refresh Rates (VRR) we want a new KMS API to set the minimum and maximum refresh cycle, where setting min=max gives us a fixed refresh rate without a mode set. To make use of VRR in more than the single-fullscreen-window case, we also agreed that a Wayland protocol letting clients communicate their preferred refresh rate would be a good idea.&lt;/li&gt;
  1890. &lt;/ul&gt;
  1891. &lt;p&gt;Like always, lots of work ahead of us, but it’s great to actually see the progress this year with the entire ecosystem having HDR support now.&lt;/p&gt;
  1892. &lt;figure&gt;&lt;img alt="Me sitting at a desk with 4 small glasses of beer in front of me" src="https://blog.sebastianwick.net/posts/display-next-hackfest-2025/display-hackfest-next-1.jpeg" /&gt;&lt;figcaption&gt;
  1893.      &lt;h4&gt;Sampling local craft beers&lt;/h4&gt;
  1894.    &lt;/figcaption&gt;
  1895. &lt;/figure&gt;
  1896.  
  1897. &lt;p&gt;See you all at XDC this year (or at least the one next year)!&lt;/p&gt;</description><author>Sebastian Wick</author><dc:creator>Sebastian Wick</dc:creator><pubDate>Fri, 15 Aug 2025 17:41:49 GMT</pubDate><guid isPermaLink="true">https://blog.sebastianwick.net/posts/display-next-hackfest-2025/</guid></item><item><title>This Week in GNOME: #212 Happy Birthday!</title><link>https://thisweek.gnome.org/posts/2025/08/twig-212/</link><description>&lt;p&gt;Update on what happened across the GNOME project in the week from August 08 to August 15.&lt;!--more--&gt;&lt;/p&gt;
  1898. &lt;p&gt;&lt;a href="https://thisweek.gnome.org/reporters/9eedba11c7e457b8ba65972af3b2af61eb58cefc34a2b9c88d114b59d3b4c1dd"&gt;Cassidy&lt;/a&gt; says&lt;/p&gt;
  1899. &lt;blockquote&gt;
  1900. &lt;p&gt;On August 15, 1997, Miguel de Icaza &lt;a href="https://mail.gnome.org/archives/gtk-list/1997-August/msg00123.html"&gt;announced&lt;/a&gt; the start of GNOME on the GTK mailing list. Twenty-eight years later a lot has changed, but we continue to develop and iterate on β€œa free and complete set of user friendly applications and desktop tools… based entirely on free software.”&lt;/p&gt;
  1901. &lt;p&gt;To help us continue this work far into the future, we hope you join us in celebrating our birthday by &lt;a href="https://donate.gnome.org"&gt;becoming a Friend of GNOME today&lt;/a&gt;! πŸŽ‚&lt;/p&gt;
  1902. &lt;/blockquote&gt;
  1903. &lt;h2 id="gnome-core-apps-and-libraries"&gt;GNOME Core Apps and Libraries&lt;/h2&gt;
  1904. &lt;p&gt;&lt;a href="https://thisweek.gnome.org/reporters/e3230d121c8381abb58fb07eaea08fe380837fb56b303dfe877383ec5a133327"&gt;FineFindus&lt;/a&gt; announces&lt;/p&gt;
  1905. &lt;blockquote&gt;
  1906. &lt;p&gt;We have now merged the next part of the Rust port of GNOME Disks, which ports the disk image restore dialog (or the more common use case: flashing ISO disk images to USB drives) to Rust.
  1907. This also enables the new Disk Image Mounter to write disk images to drives when clicking on a disk image file without opening GNOME Disks.&lt;/p&gt;
  1908. &lt;p&gt;&lt;img height="780" src="https://thisweek.gnome.org/_astro/gnome_disk_restore_dialog.Bb35FhC3_6gBa9.webp" width="550" /&gt;&lt;/p&gt;
  1909. &lt;/blockquote&gt;
  1910. &lt;h3 id="libadwaita"&gt;Libadwaita &lt;a href="https://gitlab.gnome.org/GNOME/libadwaita"&gt;β†—&lt;/a&gt;&lt;/h3&gt;
  1911. &lt;p&gt;Building blocks for modern GNOME apps using GTK4.&lt;/p&gt;
  1912. &lt;p&gt;&lt;a href="https://thisweek.gnome.org/reporters/e3a9c56a9ce41ddc9c066c03a9ec1c2e6e38363097df4ac55cb1666e45210164"&gt;Alice (she/her) πŸ³οΈβ€βš§οΈπŸ³οΈβ€πŸŒˆ&lt;/a&gt; reports&lt;/p&gt;
  1913. &lt;blockquote&gt;
  1914. &lt;p&gt;A week ago GTK landed CSS media queries support. As of today, libadwaita supports it too, both in its own styles and in app-provided styles. So, apps can now write CSS like this:&lt;/p&gt;
  1915. &lt;pre class="astro-code github-dark" style="background-color: #24292e; color: #e1e4e8;" tabindex="0"&gt;&lt;code&gt;&lt;span class="line"&gt;&lt;span style="color: #B392F0;"&gt;:root&lt;/span&gt;&lt;span style="color: #E1E4E8;"&gt; {&lt;/span&gt;&lt;/span&gt;
  1916. &lt;span class="line"&gt;&lt;span style="color: #FFAB70;"&gt;  --my-custom-color&lt;/span&gt;&lt;span style="color: #E1E4E8;"&gt;: &lt;/span&gt;&lt;span style="color: #79B8FF;"&gt;black&lt;/span&gt;&lt;span style="color: #E1E4E8;"&gt;;&lt;/span&gt;&lt;/span&gt;
  1917. &lt;span class="line"&gt;&lt;span style="color: #E1E4E8;"&gt;}&lt;/span&gt;&lt;/span&gt;
  1918. &lt;span class="line"&gt;&lt;/span&gt;
  1919. &lt;span class="line"&gt;&lt;span style="color: #85E89D;"&gt;my-widget&lt;/span&gt;&lt;span style="color: #E1E4E8;"&gt; {&lt;/span&gt;&lt;/span&gt;
  1920. &lt;span class="line"&gt;&lt;span style="color: #79B8FF;"&gt;  color&lt;/span&gt;&lt;span style="color: #E1E4E8;"&gt;: &lt;/span&gt;&lt;span style="color: #79B8FF;"&gt;var&lt;/span&gt;&lt;span style="color: #E1E4E8;"&gt;(&lt;/span&gt;&lt;span style="color: #FFAB70;"&gt;--my-custom-color&lt;/span&gt;&lt;span style="color: #E1E4E8;"&gt;);&lt;/span&gt;&lt;/span&gt;
  1921. &lt;span class="line"&gt;&lt;span style="color: #E1E4E8;"&gt;}&lt;/span&gt;&lt;/span&gt;
  1922. &lt;span class="line"&gt;&lt;/span&gt;
  1923. &lt;span class="line"&gt;&lt;span style="color: #F97583;"&gt;@media&lt;/span&gt;&lt;span style="color: #E1E4E8;"&gt; (prefers-color-scheme: dark) {&lt;/span&gt;&lt;/span&gt;
  1924. &lt;span class="line"&gt;&lt;span style="color: #B392F0;"&gt;  :root&lt;/span&gt;&lt;span style="color: #E1E4E8;"&gt; {&lt;/span&gt;&lt;/span&gt;
  1925. &lt;span class="line"&gt;&lt;span style="color: #FFAB70;"&gt;    --my-custom-color&lt;/span&gt;&lt;span style="color: #E1E4E8;"&gt;: &lt;/span&gt;&lt;span style="color: #79B8FF;"&gt;white&lt;/span&gt;&lt;span style="color: #E1E4E8;"&gt;;&lt;/span&gt;&lt;/span&gt;
  1926. &lt;span class="line"&gt;&lt;span style="color: #E1E4E8;"&gt;  }&lt;/span&gt;&lt;/span&gt;
  1927. &lt;span class="line"&gt;&lt;span style="color: #E1E4E8;"&gt;}&lt;/span&gt;&lt;/span&gt;
  1928. &lt;span class="line"&gt;&lt;/span&gt;
  1929. &lt;span class="line"&gt;&lt;span style="color: #F97583;"&gt;@media&lt;/span&gt;&lt;span style="color: #E1E4E8;"&gt; (prefers-contrast: more) {&lt;/span&gt;&lt;/span&gt;
  1930. &lt;span class="line"&gt;&lt;span style="color: #85E89D;"&gt;  my-widget&lt;/span&gt;&lt;span style="color: #E1E4E8;"&gt; {&lt;/span&gt;&lt;/span&gt;
  1931. &lt;span class="line"&gt;&lt;span style="color: #79B8FF;"&gt;    box-shadow&lt;/span&gt;&lt;span style="color: #E1E4E8;"&gt;: &lt;/span&gt;&lt;span style="color: #79B8FF;"&gt;inset&lt;/span&gt;&lt;span style="color: #79B8FF;"&gt; 0&lt;/span&gt;&lt;span style="color: #79B8FF;"&gt; 0&lt;/span&gt;&lt;span style="color: #79B8FF;"&gt; 0&lt;/span&gt;&lt;span style="color: #79B8FF;"&gt; 1&lt;/span&gt;&lt;span style="color: #F97583;"&gt;px&lt;/span&gt;&lt;span style="color: #79B8FF;"&gt; var&lt;/span&gt;&lt;span style="color: #E1E4E8;"&gt;(&lt;/span&gt;&lt;span style="color: #FFAB70;"&gt;--border-color&lt;/span&gt;&lt;span style="color: #E1E4E8;"&gt;);&lt;/span&gt;&lt;/span&gt;
  1932. &lt;span class="line"&gt;&lt;span style="color: #E1E4E8;"&gt;  }&lt;/span&gt;&lt;/span&gt;
  1933. &lt;span class="line"&gt;&lt;span style="color: #E1E4E8;"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
  1934. &lt;p&gt;&lt;code&gt;style-dark.css&lt;/code&gt;, &lt;code&gt;style-hc.css&lt;/code&gt; and &lt;code&gt;style-hc-dark.css&lt;/code&gt; are still supported for this cycle, but they will be deprecated early next cycle and removed in libadwaita 2.0, so apps are encouraged to switch to media queries.&lt;/p&gt;
  1935. &lt;/blockquote&gt;
  1936. &lt;h3 id="maps"&gt;Maps &lt;a href="https://apps.gnome.org/Maps"&gt;β†—&lt;/a&gt;&lt;/h3&gt;
  1937. &lt;p&gt;Maps gives you quick access to maps all across the world.&lt;/p&gt;
  1938. &lt;p&gt;&lt;a href="https://thisweek.gnome.org/reporters/e73431565fde01d560b5f73af1a3614dd7e0c30942d74879d09ff1204625759e"&gt;mlundblad&lt;/a&gt; reports&lt;/p&gt;
  1939. &lt;blockquote&gt;
  1940. &lt;p&gt;Maps now shows highway shields in place popovers when clicking on road labels (when custom localized shields are defined). And also the user’s avatar is shown in the OpenStreetMap account dialog for setting POI editing (when the user has set an avatar on their account)&lt;/p&gt;
  1941. &lt;p&gt;&lt;img height="743" src="https://thisweek.gnome.org/_astro/highway-shield-in-place-details-1.CNKd06kg_zaIah.webp" width="858" /&gt;&lt;/p&gt;
  1942. &lt;p&gt;&lt;img height="743" src="https://thisweek.gnome.org/_astro/highway-shields-in-place-details-2.V3M_evKj_1SFmUT.webp" width="858" /&gt;&lt;/p&gt;
  1943. &lt;p&gt;&lt;img height="743" src="https://thisweek.gnome.org/_astro/osm-account-avatar.C0J5maoj_ZsfOmX.webp" width="858" /&gt;&lt;/p&gt;
  1944. &lt;/blockquote&gt;
  1945. &lt;h2 id="third-party-projects"&gt;Third Party Projects&lt;/h2&gt;
  1946. &lt;p&gt;&lt;a href="https://thisweek.gnome.org/reporters/bb3e9d7926c189910307ea63a7f7d27f088a692038123855623ceb10966d0998"&gt;Jeff&lt;/a&gt; reports&lt;/p&gt;
  1947. &lt;blockquote&gt;
  1948. &lt;p&gt;In this blog post, Mitchell Hashimoto discusses the recent rewrite of the Ghostty GTK frontend. He focuses on how Zig interfaces with the GObject type system and using Valgrind to ensure that memory leaks are not introduced by application code. &lt;a href="https://mitchellh.com/writing/ghostty-gtk-rewrite"&gt;https://mitchellh.com/writing/ghostty-gtk-rewrite&lt;/a&gt;&lt;/p&gt;
  1949. &lt;/blockquote&gt;
  1950. &lt;p&gt;&lt;a href="https://thisweek.gnome.org/reporters/c509221633bb6e87912ced8c18ea4ae2761b2049a54cce2d6e66792033cc1cfb"&gt;andypiper&lt;/a&gt; says&lt;/p&gt;
  1951. &lt;blockquote&gt;
  1952. &lt;p&gt;Oh, hi. Long time reader, first time poster. I released Fedinspect, a little GNOME app for developers to πŸ” inspect the configuration of &lt;a href="https://jointhefediverse.net/"&gt;fediverse&lt;/a&gt; servers, and also run WebFinger lookup queries for individual ActivityPub actors. It will query nodeinfo and other .well-known URIs for these servers, and you can dig into JSON responses and HTTP headers as needed. Maybe niche, hopefully useful to some folks!&lt;/p&gt;
  1953. &lt;p&gt;You can &lt;a href="https://flathub.org/apps/org.andypiper.Fedinspect"&gt;find it on Flathub&lt;/a&gt;. Also, the icon in particular could do with some help to be a bit more GNOMEish, so &lt;a href="https://codeberg.org/andypiper/fedinspect-gtk"&gt;feel free to help out&lt;/a&gt; if you’re so inclined!&lt;/p&gt;
  1954. &lt;p&gt;&lt;img height="2428" src="https://thisweek.gnome.org/_astro/fedinspect.Cp-nuJ3y_Z1HdnBd.webp" width="1932" /&gt;&lt;/p&gt;
  1955. &lt;/blockquote&gt;
  1956. &lt;p&gt;&lt;a href="https://thisweek.gnome.org/reporters/88a714eaaa0f98ce1e3735b7c089b643750a19d8b548ee98e9417b3f67c32e28"&gt;Ronnie Nissan&lt;/a&gt; reports&lt;/p&gt;
  1957. &lt;blockquote&gt;
  1958. &lt;p&gt;&lt;a href="https://flathub.org/apps/io.github.getnf.embellish"&gt;Embellish v0.5.1&lt;/a&gt; was released today, featuring a redesign to the header bar and a new Icons page to explore, search and copy Nerd Fonts icons.&lt;/p&gt;
  1959. &lt;p&gt;The codebase also switched to using Blueprint instead of UI files.&lt;/p&gt;
  1960. &lt;p&gt;The issue where the list of fonts would jump to the top whenever a font was installed or removed has also been fixed.&lt;/p&gt;
  1961. &lt;p&gt;Embellish is available only through Flathub, hope you enjoy the new feature.&lt;/p&gt;
  1962. &lt;p&gt;&lt;img height="722" src="https://thisweek.gnome.org/_astro/embellish-v0.5.1-icons-page.fT8r5Ghv_1NFS5f.webp" width="672" /&gt;&lt;/p&gt;
  1963. &lt;/blockquote&gt;
  1964. &lt;p&gt;&lt;a href="https://thisweek.gnome.org/reporters/061d41eb80a43440bf9447406e072b686064bc0a68d55d07a985bc5822f385ef"&gt;Alain&lt;/a&gt; says&lt;/p&gt;
  1965. &lt;blockquote&gt;
  1966. &lt;p&gt;πŸš€ Planify 4.13.2 – Improvements, Fixes &amp;#x26; More Control Over Your Tasks
  1967. The new 4.13.2 release of Planify is here, focusing on delivering a more stable, smoother, and customizable task management experience.&lt;/p&gt;
  1968. &lt;p&gt;Here’s what’s new and improved:&lt;/p&gt;
  1969. &lt;ul&gt;
  1970. &lt;li&gt;Better all-day event handling – Events are now correctly detected based on your local time.&lt;/li&gt;
  1971. &lt;li&gt;More control with Todoist – If you can’t log in via OAuth, you can now manually enter your Todoist token.&lt;/li&gt;
  1972. &lt;li&gt;Improved text editing – The description area now has a limited height with scrolling, placeholders behave correctly, and your text won’t reset when repositioning the cursor.&lt;/li&gt;
  1973. &lt;li&gt;Natural sorting – Lists now correctly order strings with numbers (e.g., item2 before item10).&lt;/li&gt;
  1974. &lt;li&gt;Smoother navigation – Improved visual alignment for note-type tasks and the option to display completed tasks directly below pending ones with pagination.&lt;/li&gt;
  1975. &lt;li&gt;Stability fixes – Adjustments to project view transitions, keyboard shortcuts, task duplication, and more.&lt;/li&gt;
  1976. &lt;/ul&gt;
  1977. &lt;p&gt;πŸ’¬ We’ve also updated translations, added a Discord link, and made several under-the-hood optimizations.&lt;/p&gt;
  1978. &lt;p&gt;&lt;img height="778" src="https://thisweek.gnome.org/_astro/planner.CahrUhYJ_1XojCl.webp" width="1079" /&gt;&lt;/p&gt;
  1979. &lt;/blockquote&gt;
  1980. &lt;p&gt;&lt;a href="https://thisweek.gnome.org/reporters/0fd9cb6e7823840869419af2670b2f48e47d6e4e9813a4712bb2fbbd9e1bf110"&gt;Sepehr Rasouli&lt;/a&gt; reports&lt;/p&gt;
  1981. &lt;blockquote&gt;
  1982. &lt;p&gt;&lt;a href="https://flathub.org/apps/io.github.sepehr_rs.Sudoku"&gt;Sudoku V1.1.2&lt;/a&gt; is here! Sudoku is a new modern app focused on delivering a clean, distraction-free experience. Designed with simplicity and comfort in mind, it features a straightforward interface that helps players stay focused and enjoy the game without unnecessary clutter or complications.&lt;/p&gt;
  1983. &lt;p&gt;Features:&lt;/p&gt;
  1984. &lt;ul&gt;
  1985. &lt;li&gt;Modern GTK4 and libadwaita interface&lt;/li&gt;
  1986. &lt;li&gt;Keyboard shortcuts for quick access to all functions&lt;/li&gt;
  1987. &lt;li&gt;Save and load games seamlessly to continue your progress anytime&lt;/li&gt;
  1988. &lt;li&gt;Highlight active row and cell to improve focus and ease of play&lt;/li&gt;
  1989. &lt;li&gt;Conflict highlighting to spot mistakes β€” perfect for learning&lt;/li&gt;
  1990. &lt;li&gt;Fun for all skill levels, from beginners to experts&lt;/li&gt;
  1991. &lt;/ul&gt;
  1992. &lt;p&gt;The project is still in its early stages, so contributions are warmly welcome!&lt;/p&gt;
  1993. &lt;p&gt;&lt;img height="738" src="https://thisweek.gnome.org/_astro/sudoku-dark.DfQMc3Wg_q5SgT.webp" width="850" /&gt;&lt;/p&gt;
  1994. &lt;/blockquote&gt;
  1995. &lt;p&gt;&lt;a href="https://thisweek.gnome.org/reporters/696a79d300f2c36e7120630dab1e85058bb69e6fa93840b82bc3dfa3b6977d3e"&gt;Semen Fomchenkov&lt;/a&gt; announces&lt;/p&gt;
  1996. &lt;blockquote&gt;
  1997. &lt;h2 id="introducing-hashsum--a-modern-checksum-utility"&gt;Introducing Hashsum β€” a modern checksum utility&lt;/h2&gt;
  1998. &lt;p&gt;This week, the &lt;strong&gt;ALT Gnome&lt;/strong&gt; and &lt;strong&gt;ALT Linux Team&lt;/strong&gt; present &lt;a href="https://altlinux.space/alt-gnome/Hashsum"&gt;&lt;strong&gt;Hashsum&lt;/strong&gt;&lt;/a&gt; β€” a file checksum calculation utility built with &lt;strong&gt;GTK4/Libadwaita&lt;/strong&gt;, inspired by the ideas behind &lt;strong&gt;Collision&lt;/strong&gt; and &lt;strong&gt;GTK Hash&lt;/strong&gt;.&lt;/p&gt;
  1999. &lt;p&gt;We greatly appreciate the minimalist interface of &lt;strong&gt;Collision&lt;/strong&gt;, but most GTK developers in our community create applications in Vala, so we decided to take the base from Collision and rewrite it from Crystal to make future development and maintenance easier. With Hashsum, we’ve combined the clean UI of Collision with the broad algorithm support of GTK Hash, adding the conveniences our community has been asking for.&lt;/p&gt;
  2000. &lt;h2 id="features"&gt;Features&lt;/h2&gt;
  2001. &lt;ul&gt;
  2002. &lt;li&gt;Modern &lt;strong&gt;GTK4/Libadwaita&lt;/strong&gt; interface inspired by &lt;strong&gt;Collision&lt;/strong&gt;.&lt;/li&gt;
  2003. &lt;li&gt;Support for the following algorithms: &lt;strong&gt;MD5&lt;/strong&gt;, &lt;strong&gt;SHA-1&lt;/strong&gt;, &lt;strong&gt;SHA-256&lt;/strong&gt;, &lt;strong&gt;SHA-512&lt;/strong&gt;, &lt;strong&gt;BLAKE3&lt;/strong&gt;, &lt;strong&gt;CRC-32&lt;/strong&gt;, &lt;strong&gt;Adler-32&lt;/strong&gt;, &lt;strong&gt;GOST R 34.11-94&lt;/strong&gt;, &lt;strong&gt;Streebog-256/512&lt;/strong&gt; (via &lt;em&gt;gcrypt&lt;/em&gt; and &lt;em&gt;blake3&lt;/em&gt;).&lt;/li&gt;
  2004. &lt;li&gt;Flexible selection: enable only the algorithms you actually need.&lt;/li&gt;
  2005. &lt;li&gt;Accurate progress display for large file computations.&lt;/li&gt;
  2006. &lt;li&gt;&lt;strong&gt;Files&lt;/strong&gt; (&lt;em&gt;Nautilus&lt;/em&gt;) plugin: calculate checksums directly from the file manager’s context menu.&lt;/li&gt;
  2007. &lt;li&gt;Developed in &lt;strong&gt;Vala&lt;/strong&gt; with love.&lt;/li&gt;
  2008. &lt;/ul&gt;
  2009. &lt;h2 id="whats-next"&gt;What’s next?&lt;/h2&gt;
  2010. &lt;p&gt;We plan to submit Hashsum to &lt;strong&gt;Flathub&lt;/strong&gt;, but our immediate focus will be on adding features important to the community β€” ensuring it’s not just a direct analog of Collision.
  2011. Ideas and bug reports are welcome: &lt;strong&gt;&lt;a href="https://altlinux.space/alt-gnome/Hashsum/issues/new"&gt;https://altlinux.space/alt-gnome/Hashsum/issues/new&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
  2012. &lt;p&gt;Best regards to the developers of the Collision project β€” your enthusiasm and drive for innovation are truly inspiring.&lt;/p&gt;
  2013. &lt;p&gt;&lt;img height="978" src="https://thisweek.gnome.org/_astro/Hashsum-1.CHcH-d9w_Z1pQOFf.webp" width="807" /&gt;&lt;/p&gt;
  2014. &lt;p&gt;&lt;img height="978" src="https://thisweek.gnome.org/_astro/Hashsum-2.CpxOOYti_1AAjVE.webp" width="807" /&gt;&lt;/p&gt;
  2015. &lt;/blockquote&gt;
  2016. &lt;h3 id="parabolic"&gt;Parabolic &lt;a href="https://flathub.org/apps/details/org.nickvision.tubeconverter"&gt;β†—&lt;/a&gt;&lt;/h3&gt;
  2017. &lt;p&gt;Download web video and audio.&lt;/p&gt;
  2018. &lt;p&gt;&lt;a href="https://thisweek.gnome.org/reporters/b77e70357e23d9014c0bf6590a1f80bae500e21461bfab9eabc65fd8567d6dd7"&gt;Nick&lt;/a&gt; announces&lt;/p&gt;
  2019. &lt;blockquote&gt;
  2020. &lt;p&gt;Parabolic &lt;a href="https://github.com/NickvisionApps/Parabolic/releases/tag/2025.8.0"&gt;V2025.8.0&lt;/a&gt; is here! This release contains new features, bug fixes, and an updated &lt;code&gt;yt-dlp&lt;/code&gt;.&lt;/p&gt;
  2021. &lt;p&gt;Here’s the full changelog:&lt;/p&gt;
  2022. &lt;ul&gt;
  2023. &lt;li&gt;Added the ability to update yt-dlp from within the app when a newer version is available&lt;/li&gt;
  2024. &lt;li&gt;Added padding to single digit numbered titles in playlist downloads&lt;/li&gt;
  2025. &lt;li&gt;Replaced None translation language with en_US&lt;/li&gt;
  2026. &lt;li&gt;Fixed an issue where validating some media would cause the app to crash&lt;/li&gt;
  2027. &lt;li&gt;Fixed an issue where the app would not open on Windows&lt;/li&gt;
  2028. &lt;li&gt;Fixed an issue where download rows disappeared on GNOME&lt;/li&gt;
  2029. &lt;li&gt;Updated yt-dlp&lt;/li&gt;
  2030. &lt;/ul&gt;
  2031. &lt;p&gt;&lt;img height="829" src="https://thisweek.gnome.org/_astro/Parabolic_V2025.8.0.9IJNaNlj_1S9pcr.webp" width="1046" /&gt;&lt;/p&gt;
  2032. &lt;/blockquote&gt;
  2033. &lt;h3 id="fractal"&gt;Fractal &lt;a href="https://gitlab.gnome.org/World/fractal"&gt;β†—&lt;/a&gt;&lt;/h3&gt;
  2034. &lt;p&gt;Matrix messaging app for GNOME written in Rust.&lt;/p&gt;
  2035. &lt;p&gt;&lt;a href="https://thisweek.gnome.org/reporters/cb8ebb1826d577060c92faada43d4ec4c2dff3628749a1dfc63866b2747e19b3"&gt;KΓ©vin Commaille&lt;/a&gt; says&lt;/p&gt;
  2036. &lt;blockquote&gt;
  2037. &lt;p&gt;Knock, knock, knock… on &lt;del&gt;wood&lt;/del&gt; rooms, baby 🎡 Ooh ooh ooh ooh ooh ooh 🎢 That’s right, Fractal 12 adds support for knocking, among other things. Read all about the improvements since 11.2:&lt;/p&gt;
  2038. &lt;ul&gt;
  2039. &lt;li&gt;Requesting invites to rooms (aka knocking) is now possible, as is enabling such requests for room admins.&lt;/li&gt;
  2040. &lt;li&gt;The upcoming room version 12 is supported, with the special power level of room creators.&lt;/li&gt;
  2041. &lt;li&gt;A room can be marked as unread via the context menu in the sidebar.&lt;/li&gt;
  2042. &lt;li&gt;You can now see if a section in the sidebar has any notifications or activity when it is collapsed.&lt;/li&gt;
  2043. &lt;li&gt;Clicking on the name of the sender of a message adds a mention to them in the composer.&lt;/li&gt;
  2044. &lt;li&gt;The safety setting to hide media previews in rooms is now synced between Matrix clients and we added another safety setting (which is also synced) to hide avatars in invites.&lt;/li&gt;
  2045. &lt;/ul&gt;
  2046. &lt;p&gt;As usual, this release includes other improvements and fixes thanks to all our contributors, and our upstream projects.&lt;/p&gt;
  2047. &lt;p&gt;We want to address special thanks to the translators who worked on this version. We know this is a huge undertaking and have a deep appreciation for what you’ve done. If you want to help with this effort, head over to &lt;a href="https://l10n.gnome.org/"&gt;Damned Lies&lt;/a&gt;.&lt;/p&gt;
  2048. &lt;p&gt;This version is available right now on &lt;a href="https://flathub.org/apps/org.gnome.Fractal"&gt;Flathub&lt;/a&gt;.&lt;/p&gt;
  2049. &lt;p&gt;If you want to join the gang, you can start by fixing one of our &lt;a href="https://gitlab.gnome.org/World/fractal/-/issues/?label_name%5B%5D=4.%20Newcomers"&gt;newcomers issues&lt;/a&gt;. We are always looking for new members!&lt;/p&gt;
  2050. &lt;/blockquote&gt;
  2051. &lt;h2 id="internships"&gt;Internships&lt;/h2&gt;
  2052. &lt;p&gt;&lt;a href="https://thisweek.gnome.org/reporters/ac7c633dc6d4af73820620771f530a64f6a3d30f156d65ff13026936aa5d2e37"&gt;Aryan Kaushik&lt;/a&gt; reports&lt;/p&gt;
  2053. &lt;blockquote&gt;
  2054. &lt;p&gt;The GNOME Foundation is interested in participating in the December-March cohort of Outreachy.&lt;/p&gt;
  2055. &lt;p&gt;If you are interested in mentoring AND have a project idea in mind, please visitΒ &lt;a href="https://gitlab.gnome.org/Teams/Engagement/internship-project-ideas/-/issues"&gt;https://gitlab.gnome.org/Teams/Engagement/internship-project-ideas/-/issues&lt;/a&gt; and submit your proposal by 10th September 2025.&lt;/p&gt;
  2056. &lt;p&gt;We are always on the lookout for project ideas that move the GNOME project forward!&lt;/p&gt;
  2057. &lt;p&gt;If you have any questions, please feel free to post them on our matrix - &lt;a href="https://matrix.to/#/#internship:gnome.org"&gt;#internship:gnome.org&lt;/a&gt; or e-mailΒ &lt;a href="mailto:soc-admins@gnome.org"&gt;soc-admins@gnome.org&lt;/a&gt;.&lt;/p&gt;
  2058. &lt;p&gt;Looking forward to your proposals!&lt;/p&gt;
  2059. &lt;/blockquote&gt;
  2060. &lt;h2 id="gnome-foundation"&gt;GNOME Foundation&lt;/h2&gt;
  2061. &lt;p&gt;&lt;a href="https://thisweek.gnome.org/reporters/9143e882f165daa6337ff08ea407ba2fd030447de25d67c8eb0acdaf3005d958"&gt;steven&lt;/a&gt; says&lt;/p&gt;
  2062. &lt;blockquote&gt;
  2063. &lt;p&gt;New Foundation Update:&lt;/p&gt;
  2064. &lt;p&gt;&lt;a href="https://blogs.gnome.org/steven/2025/08/08/2025-08-08-foundation-update/"&gt;https://blogs.gnome.org/steven/2025/08/08/2025-08-08-foundation-update/&lt;/a&gt;&lt;/p&gt;
  2065. &lt;ul&gt;
  2066. &lt;li&gt;bureaucracy (yay?)&lt;/li&gt;
  2067. &lt;li&gt;apology to GIMP&lt;/li&gt;
  2068. &lt;li&gt;advisory board room&lt;/li&gt;
  2069. &lt;li&gt;early draft budget&lt;/li&gt;
  2070. &lt;li&gt;501(c)(3) structural improvements&lt;/li&gt;
  2071. &lt;li&gt;explaining the travel policy freeze&lt;/li&gt;
  2072. &lt;/ul&gt;
  2073. &lt;/blockquote&gt;
  2074. &lt;h2 id="thats-all-for-this-week"&gt;That’s all for this week!&lt;/h2&gt;
  2075. &lt;p&gt;See you next week, and be sure to stop by &lt;a href="https://matrix.to/#/#thisweek:gnome.org"&gt;#thisweek:gnome.org&lt;/a&gt; with updates on your own projects!&lt;/p&gt;</description><author>This Week in GNOME</author><dc:creator>This Week in GNOME</dc:creator><pubDate>Fri, 15 Aug 2025 00:00:00 GMT</pubDate><guid isPermaLink="true">https://thisweek.gnome.org/posts/2025/08/twig-212/</guid></item><item><title>Gedit Technology blog: Mid-August News</title><link>https://gedit-text-editor.org/blog/2025-08-14-mid-august-news.html</link><description>&lt;p&gt;
  2076.  Misc news about the
  2077.  &lt;a href="https://gedit-text-editor.org/" target="_blank"&gt;gedit&lt;/a&gt;
  2078.  text editor, mid-August edition! (Some sections are a bit technical).
  2079. &lt;/p&gt;
  2080.  
  2081. &lt;h3&gt;Code Comment plugin rewritten&lt;/h3&gt;
  2082. &lt;p&gt;
  2083.  I forgot to talk about it in the mid-July news, but the Code Comment plugin
  2084.  has been rewritten in C (it was previously implemented in Python) and the bulk
  2085.  of it is implemented as re-usable code in libgedit-tepl. The implementation is
  2086.  now shared between
  2087.  &lt;a href="https://gitlab.gnome.org/World/gedit/enter-tex" target="_blank"&gt;Enter TeX&lt;/a&gt;
  2088.  and gedit.
  2089. &lt;/p&gt;
  2090.  
  2091. &lt;h3&gt;File loading and saving: a new GtkSourceEncoding class&lt;/h3&gt;
  2092. &lt;p&gt;
  2093.  I've modified the GtkSourceEncoding class that is part of
  2094.  libgedit-gtksourceview, and adapted gedit accordingly.
  2095.  The new version of GtkSourceEncoding comes from an experiment that I did in
  2096.  libgedit-tepl several years ago.
  2097. &lt;/p&gt;
  2098. &lt;p&gt;
  2099.  GtkSourceEncoding represents a character set (or "charset" for short). It is
  2100.  used in combination with &lt;code&gt;iconv&lt;/code&gt; to convert text files from one
  2101.  encoding to another (for example from ISO-8859-15 to UTF-8).
  2102. &lt;/p&gt;
  2103. &lt;p&gt;
  2104.  The purpose of the experiment that was done in libgedit-tepl (the TeplEncoding
  2105.  class) was to accomodate the needs for a
  2106.  &lt;a href="https://www.freedesktop.org/wiki/Software/uchardet/" target="_blank"&gt;uchardet&lt;/a&gt;
  2107.  usage (note that uchardet is not yet used by gedit, but it would be useful).
  2108.  uchardet is a library to automatically detect the encoding of some input text.
  2109.  It returns an iconv-compatible charset, as a string.
  2110. &lt;/p&gt;
  2111. &lt;p&gt;
  2112.  It is &lt;em&gt;this string&lt;/em&gt; - returned by uchardet - that we want to store and
  2113.  pass to &lt;code&gt;iconv&lt;/code&gt; unmodified, to not lose information.
  2114. &lt;/p&gt;
  2115. &lt;p&gt;
  2116.  The problem with the old version of GtkSourceEncoding: there was a fixed set
  2117.  of GtkSourceEncoding instances, all &lt;code&gt;const&lt;/code&gt; (so without the need to
  2118.  free them). When trying to get an instance for an unknown charset string, NULL
  2119.  was returned. So this was not appropriate for a uchardet usage (or at least,
  2120.  not a clean solution: with the charset string returned by uchardet it was not
  2121.  guaranteed that a corresponding GtkSourceEncoding instance was available).
  2122. &lt;/p&gt;
  2123. &lt;p&gt;
  2124.  Since GtkSourceEncoding is used in a lot of places, we don't want to change
  2125.  the code to represent a charset as just a string. And a simple string is
  2126.  anyway too basic, GtkSourceEncoding provides useful features.
  2127. &lt;/p&gt;
  2128. &lt;p&gt;
  2129.  So, long story short: the new GtkSourceEncoding class returns new instances
  2130.  that must be freed, and has a constructor that just makes a copy of the
  2131.  charset string (there is the &lt;code&gt;get_charset()&lt;/code&gt; method to get back the
  2132.  string, unmodified).
  2133. &lt;/p&gt;
  2134. &lt;p&gt;
  2135.  So gedit can keep using the GtkSourceEncoding abstraction, and we are one step
  2136.  closer to being able to use uchardet or something similar!
  2137. &lt;/p&gt;
  2138.  
  2139. &lt;h3&gt;Know more about the gedit's maintainer&lt;/h3&gt;
  2140. &lt;p&gt;
  2141.  I now have a personal web site, or more accurately a single web
  2142.  &lt;em&gt;page&lt;/em&gt;:&lt;br /&gt;
  2143.  &lt;a href="https://wilmet-software.be/" target="_blank"&gt;wilmet-software.be&lt;/a&gt;
  2144.  (SΓ©bastien Wilmet)
  2145. &lt;/p&gt;
  2146. &lt;p&gt;
  2147.  gedit is a 27-years-old project, the first lines were written in 1998
  2148.  (and &lt;em&gt;normally&lt;/em&gt; it won't be part of the
  2149.  &lt;a href="https://en.wikipedia.org/wiki/27_Club" target="_blank"&gt;27 Club&lt;/a&gt;!).
  2150.  I've been a contributor to the project for 14 years, so more than half the
  2151.  project existence. Time flies!
  2152. &lt;/p&gt;
  2153.  
  2154. &lt;h3&gt;Robust file loading - some progress&lt;/h3&gt;
  2155. &lt;p&gt;
  2156.  After the rework of GtkSourceEncoding (which is part of the File Loading and
  2157.  Saving subsystem in libgedit-gtksourceview), I've made good progress to make
  2158.  the file loading more robust - although there is more work still to do.
  2159. &lt;/p&gt;
  2160. &lt;p&gt;
  2161.  It is a basis in programming to check all program input. gedit makes things a
  2162.  bit harder to accomplish this. To open a &lt;em&gt;document&lt;/em&gt;:
  2163. &lt;/p&gt;
  2164. &lt;ul&gt;
  2165.  &lt;li&gt;
  2166.    There is first the problem of the character encoding. It is
  2167.    not sufficient for a general-purpose text editor to accept only UTF-8. So
  2168.    text files can be almost anything in binary form.
  2169.  &lt;/li&gt;
  2170.  &lt;li&gt;
  2171.    Then gedit allows to open documents containing
  2172.    invalid characters in the specified or auto-detected
  2173.    encoding. With this, documents can &lt;em&gt;really&lt;/em&gt; be anything in binary
  2174.    form.
  2175.  &lt;/li&gt;
  2176.  &lt;li&gt;
  2177.    Finally the GtkTextView widget used at the heart of gedit has several
  2178.    limitations: (1) very big files (like log files or database dumps) are not
  2179.    supported, a limit on the content size must be set (and if reached, still
  2180.    allow to load the file with truncated content). (2) very long lines cause
  2181.    performance problems and can freeze the application.
  2182.  &lt;/li&gt;
  2183. &lt;/ul&gt;
  2184.  
  2185. &lt;p&gt;
  2186.  So, the good news is that progress has been made on this. (There is only a
  2187.  good news, let's stay positive!).
  2188. &lt;/p&gt;
  2189.  
  2190. &lt;p&gt;&lt;small&gt;
  2191.  If you appreciate the work that I do in gedit, I would like to know your
  2192.  feedback, what I should improve or which important feature is missing. You can
  2193.  contact me
  2194.  &lt;a href="https://wilmet-software.be/" target="_blank"&gt;by email&lt;/a&gt;
  2195.  for example, or on a
  2196.  &lt;a href="https://gedit-text-editor.org/" target="_blank"&gt;discussion channel&lt;/a&gt;.
  2197.  Thank you :-) !
  2198. &lt;/small&gt;&lt;/p&gt;</description><author>Gedit Technology blog</author><dc:creator>Gedit Technology blog</dc:creator><pubDate>Thu, 14 Aug 2025 10:00:00 GMT</pubDate><guid isPermaLink="true">https://gedit-text-editor.org/blog/2025-08-14-mid-august-news.html</guid></item><item><title>Aryan Kaushik: GUADEC 2025 Experience</title><link>https://www.aryank.in/posts/2025-08-13-guadec-2025-experience/</link><description>&lt;h1&gt;Ciao a tutti!&lt;/h1&gt;
  2199. &lt;p&gt;In this blog, I'm pumped to share my experience attending GUADEC 2025 held in Brescia, Italy.&lt;/p&gt;
  2200. &lt;h4&gt;&lt;strong&gt;Let's start :)&lt;/strong&gt;&lt;/h4&gt;
  2201. &lt;p&gt;During the conference, I presented multiple talks -&lt;/p&gt;
  2202. &lt;p&gt;&lt;a href="https://www.youtube.com/live/-xEhHObnCug?t=10745s"&gt;My main talk on GNOME Internships, Google Summer of Code, Outreachy&lt;/a&gt;&lt;/p&gt;
  2203. &lt;p&gt;&lt;a href="https://www.youtube.com/live/18Ir6RXkIeA?t=27547"&gt;Lightning talk on Need for GUADEC and Regional events&lt;/a&gt;&lt;/p&gt;
  2204. &lt;p&gt;&lt;a href="https://www.youtube.com/live/Z7F3fghCQB4?t=28832"&gt;Lightning talk on GNOME Extensions as the gateway ****&lt;/a&gt;&lt;/p&gt;
  2205. &lt;p&gt;BoF on GNOME Foundation Internships - Unrecorded πŸ˜…&lt;/p&gt;
  2206. &lt;p&gt;The main talk was on Day 1 of the conference, which was quite exciting. I had the pleasure of sharing my journey and insights on how to leverage FOSS internships like GSoC and Outreachy to build a successful career in open source.&lt;/p&gt;
  2207. &lt;p&gt;Attending this conference was far from easy. Due to the tragic Air India flight incident, I had to scrap my original travel plans and book a last-minute alternative to Italy. It was stressful β€” both emotionally and financially, but I was determined to make it to GUADEC.&lt;/p&gt;
  2208. &lt;p&gt;Another trouble was with Visa; I had to apply for a Schengen Visa, which was hectic to say the least. Submitting 150+ pages of documents and waking up the GNOME Foundation team at night (their time) just to get some random letters the VFS office (embassy delegates) wanted during the submission process was bliss. So sincere thanks to Steven (ED), Anisa, Kristi, Rosanna, Asmit and others for helping me out with this at the very last minute. You all are heroes!&lt;/p&gt;
  2209. &lt;p&gt;I usually carry double the required documents just to be on the safe side, but this was just something else.&lt;/p&gt;
  2210. &lt;p&gt;Anyway, let's proceed with the blog :D&lt;/p&gt;
  2211. &lt;p&gt;&lt;img alt="University of Brescia image" src="https://www.aryank.in/images/uploads/guadec25_university_of_brescia.jpg" /&gt;&lt;/p&gt;
  2212. &lt;h4&gt;&lt;strong&gt;The touchdown&lt;/strong&gt;&lt;/h4&gt;
  2213. &lt;p&gt;Due to a lack of flights to Brescia, I had to take a flight to Milan and then travel by train to Brescia.&lt;/p&gt;
  2214. &lt;p&gt;But, since this was my first conference after graduating the same month (yipeee), I fortunately was able to extend my trip for the first time ever.&lt;/p&gt;
  2215. &lt;p&gt;This let me explore Italy, a dream come true. I visited Milan and the Monza circuit before heading to Brescia.&lt;/p&gt;
  2216. &lt;p&gt;&lt;img alt="Milan image" src="https://www.aryank.in/images/uploads/guadec25_milan.jpg" /&gt;&lt;/p&gt;
  2217. &lt;p&gt;I have no clue about racing, but visiting the Monza circuit was a surreal experience. The history, the cars, and the atmosphere were just amazing.&lt;/p&gt;
  2218. &lt;p&gt;&lt;img alt="Duomo di milano image" src="https://www.aryank.in/images/uploads/guadec25_milan_2.jpg" /&gt;&lt;/p&gt;
  2219. &lt;h4&gt;&lt;strong&gt;The pre-conference party&lt;/strong&gt;&lt;/h4&gt;
  2220. &lt;p&gt;After some nice struggles with public transport in Brescia (I loved it afterwards though), I decided to take a 20-minute walk to the venue of the pre-conference party.&lt;/p&gt;
  2221. &lt;p&gt;I don't mind walking, but as I was waiting for the bus, initially, I got late and had to walk fast. The worst part? The bus that didn't allow me to board was constantly catching up to me for half the journey, boosting the frustration.&lt;/p&gt;
  2222. &lt;p&gt;&lt;img alt="Brescia image" src="https://www.aryank.in/images/uploads/guadec25_brescia.jpg" /&gt;&lt;/p&gt;
  2223. &lt;p&gt;But well... I finally reached! Met some of my friends and had nice conversations with the organisers, attendees, speakers and some really engaging discussions with Mauro from Canonical.&lt;/p&gt;
  2224. &lt;p&gt;After which, he consented to me kidnapping him for an Indian dinner. The place we got to was closed (I hate Google Maps), but fortunately, we found another Indian restaurant in very close proximity.&lt;/p&gt;
  2225. &lt;p&gt;We had tons of exchanges about the future of Ubuntu, Canonical and GNOME. It was great to meet in person after GUADEC 2023.&lt;/p&gt;
  2226. &lt;h4&gt;&lt;strong&gt;The first day of the conference&lt;/strong&gt;&lt;/h4&gt;
  2227. &lt;p&gt;The first day was quite good, got to attend the talks which I was looking forward to and was also able to help with the Ubuntu booth setup (well, not much but somewhat?).&lt;/p&gt;
  2228. &lt;p&gt;&lt;img alt="&amp;quot;Canonical booth image&amp;quot;" src="https://www.aryank.in/images/uploads/guadec25_canonical_booth.jpg" /&gt;&lt;/p&gt;
  2229. &lt;p&gt;After tackling some great wifi chip delights, we were able to get the anonymous forms up and running for the booth.&lt;/p&gt;
  2230. &lt;p&gt;And then came my talk. Maria Majadas introduced me (she is awesome btw!), and after some setup tackling, I was able to present my talk on &amp;quot;Making a career out of FOSS Internships GSoC/Outreachy&amp;quot;.&lt;/p&gt;
  2231. &lt;p&gt;I had to rush a bit due to the time constraints, but I was able to cover most of the points I wanted to. So yay!&lt;/p&gt;
  2232. &lt;p&gt;Afterwards, I was able to volunteer for moderating the talk, &amp;quot;The state of GTK&amp;quot; by Matthias, which was quite insightful. It was great to see the progress GTK has made and the future plans.&lt;/p&gt;
  2233. &lt;p&gt;We then had a great panel discussion, which was quite a nice twist.&lt;/p&gt;
  2234. &lt;p&gt;&lt;img alt="Panel discussion image" src="https://www.aryank.in/images/uploads/guadec25_panel_session.jpg" /&gt;&lt;/p&gt;
  2235. &lt;p&gt;Later Aarti and Sri (Who both are awesome), whom I met for the first time in person, invited me for some snacks and drinks. The stories they shared were just so amazing and inspiring.
  2236. Due to them, for the first time at GUADEC, I was able to have normal conversations and not just very professional ones. This elevated the conference 10x for me.&lt;/p&gt;
  2237. &lt;p&gt;If you both are reading this, I just want to say you both are amazing, and I hope to see you again soon!&lt;/p&gt;
  2238. &lt;p&gt;Then Mauro kidnapped me for a nice Italian dinner. We found a nice pizzeria with amazing views and food. I let him order for me, just like he did with me :).&lt;/p&gt;
  2239. &lt;p&gt;And I have to say, that was the best pizza I ever had.&lt;/p&gt;
  2240. &lt;p&gt;&lt;img alt="Brescia dinner and drinks image" src="https://www.aryank.in/images/uploads/guadec25_day1_dinner.jpg" /&gt;&lt;/p&gt;
  2241. &lt;p&gt;Also, I learned some new pizza cutting tricks and info on why you should NEVER share Pizza (apart from exchanging slices to try). This will stay with me for life xD.&lt;/p&gt;
  2242. &lt;p&gt;Oh man, that was a lot for the first day. I was exhausted but happy.&lt;/p&gt;
  2243. &lt;h4&gt;&lt;strong&gt;The second day of the conference&lt;/strong&gt;&lt;/h4&gt;
  2244. &lt;p&gt;On the second day, the highlight talks for me were &amp;quot;Getting Things Done in GNOME&amp;quot;, &amp;quot;State of the Shell&amp;quot; and &amp;quot;Have a GTK app with no tests? No Problem!&amp;quot; (Which I had the pleasure to attend and moderate).&lt;/p&gt;
  2245. &lt;p&gt;&lt;img alt="Getting Things Done in GNOME image" src="https://www.aryank.in/images/uploads/guadec25_getting_things_done_in_gnome.jpg" /&gt;&lt;/p&gt;
  2246. &lt;p&gt;I also gave another lightning talk on &amp;quot;Why do we need GUADEC or GNOME events?&amp;quot; which was quite fun. I shared my experiences and insights on the importance of such events in fostering community and collaboration.&lt;/p&gt;
  2247. &lt;p&gt;Thanks to Rosanna for giving me the idea to do so. It was really great to share my thoughts and experiences with the community.&lt;/p&gt;
  2248. &lt;p&gt;After the conference, I took a detour to visit the beautiful Brescia Castle. The views were out of this world. I also, instead of taking the bus to the top or climbing up stairs, took the gravel path around the castle (It had fences which I decided to jump over :)). But it was worth it, climbing this way allowed me to see every corner of the city layer by layer. That you can't beat!&lt;/p&gt;
  2249. &lt;p&gt;&lt;img alt="Brescia Castle image" src="https://www.aryank.in/images/uploads/guadec25_brescia_castle.jpg" /&gt;&lt;/p&gt;
  2250. &lt;h4&gt;&lt;strong&gt;The third day of the conference&lt;/strong&gt;&lt;/h4&gt;
  2251. &lt;p&gt;As you can guess by now, it was great as well, and I gave another talk - &amp;quot;GNOME Extensions: the gateway drug to GNOME&amp;quot; and also helped in moderating some sessions.&lt;/p&gt;
  2252. &lt;p&gt;Also, I'll highly recommend you to watch the talk on Gnurd - https://www.youtube.com/live/Z7F3fghCQB4?si=H_HgN6IHeRdSVu10&amp;amp;t=27391
  2253. It was nice!&lt;/p&gt;
  2254. &lt;p&gt;And we ended the day with a great dinner celebrating 25 years of GUADEC. The food was amazing, the company was great, and the atmosphere was just perfect.&lt;/p&gt;
  2255. &lt;h4&gt;&lt;strong&gt;The BoFs&lt;/strong&gt;&lt;/h4&gt;
  2256. &lt;p&gt;Being a GNOME GSoC'22 Intern, and now a part of the GNOME Internship Committee, I had my fourth and final talk (kind of), GNOME Internship Committee Meetup, where we discussed the future of the program, the challenges we face, and how we can improve it.&lt;/p&gt;
  2257. &lt;p&gt;Thanks, Felipe, for organising it and inviting me to be a part of it. It was great to see the progress we have made and the plans we have for the future.&lt;/p&gt;
  2258. &lt;p&gt;The next day, I attended the &amp;quot;GTK Settings Hackfest&amp;quot; BoF, and it reminded me why physical meetups are so powerful. Discussing my blockers directly with the maintainers and fixing stuff together. It can't get more delightful than that!&lt;/p&gt;
  2259. &lt;p&gt;We then went to Lake Iseo for a trip. And the picture will give you a glimpse of the beauty of the place.&lt;/p&gt;
  2260. &lt;p&gt;&lt;img alt="Lake Iseo" src="https://www.aryank.in/images/uploads/guadec25_iseo.jpg" /&gt;
  2261. &lt;img alt="Lake Iseo second image" src="https://www.aryank.in/images/uploads/guadec25_iseo_2.jpg" /&gt;&lt;/p&gt;
  2262. &lt;h4&gt;&lt;strong&gt;The Bergamo tour&lt;/strong&gt;&lt;/h4&gt;
  2263. &lt;p&gt;The tour was a great opportunity to check out Bergamo and interact with people.&lt;/p&gt;
  2264. &lt;p&gt;Special shoutout to Ignacy for being my partner in crime for clicking pictures. The skyline, the view from the top and the food were just amazing. We had a great time exploring the city and taking pictures.&lt;/p&gt;
  2265. &lt;p&gt;&lt;img alt="Bergamo" src="https://www.aryank.in/images/uploads/guadec25_bergamo.jpg" /&gt;&lt;/p&gt;
  2266. &lt;p&gt;It was also Federico's birthday, so we celebrated it with a cake and some drinks. Celebrating the founder at the 25th GUADEC was the cherry on top.&lt;/p&gt;
  2267. &lt;p&gt;Federico also gave great insights about Coffee. I was looking forward to buying a Bialetti Moka pot, but I wasn't sure. But after his advice, I splurged. And I have to say, it was worth it. The coffee is just amazing, and the experience of making it is just delightful.
  2268. Instant is not the same anymore :(.&lt;/p&gt;
  2269. &lt;p&gt;So thanks to Federico, I now have a taste of Italy at home. Next stop, getting a grinder!&lt;/p&gt;
  2270. &lt;p&gt;&lt;img alt="Bialetti" src="https://www.aryank.in/images/uploads/guadec25_bialetti.jpg" /&gt;&lt;/p&gt;
  2271. &lt;h4&gt;&lt;strong&gt;Meeting people&lt;/strong&gt;&lt;/h4&gt;
  2272. &lt;p&gt;At last, I met many new people and got to learn a lot. Made new friends, got to meet people I look up to and many more.&lt;/p&gt;
  2273. &lt;p&gt;I hope I wasn't that introverted, but yeah, slowly getting comfortable around new people, especially thanks to Aarti and Sri for making me feel comfortable and helping me break the ice.&lt;/p&gt;
  2274. &lt;p&gt;&lt;img alt="GUADEC 25 peeps" src="https://www.aryank.in/images/uploads/guadec25_awesome_people.jpg" /&gt;&lt;/p&gt;
  2275. &lt;h4&gt;&lt;strong&gt;The End&lt;/strong&gt;&lt;/h4&gt;
  2276. &lt;p&gt;This GUADEC was just awesome. And I was also able to visit 4 cities in Italy, which was a dream come true. Normally, due to college, I couldn't visit any other city than the conference city, but this time I was able to extend my trip and explore Italy a bit.&lt;/p&gt;
  2277. &lt;p&gt;Thanks to all the people for making the event so great. It was an experience like no other. I would also like to thank GNOME Foundation for sponsoring the trip :)
  2278. I hope I used it to the fullest and made the most out of it. :D&lt;/p&gt;
  2279. &lt;p&gt;I also renewed my GNOME Foundation membership just recently, which is awesome.&lt;/p&gt;
  2280. &lt;p&gt;&lt;img alt="Sponsored by gnome badge" src="https://www.aryank.in/images/uploads/sponsored-by-foundation.png" /&gt;&lt;/p&gt;</description><author>Aryan Kaushik</author><dc:creator>Aryan Kaushik</dc:creator><pubDate>Wed, 13 Aug 2025 20:00:00 GMT</pubDate><guid isPermaLink="true">https://www.aryank.in/posts/2025-08-13-guadec-2025-experience/</guid></item><item><title>Christian Hergert: Week 32 Status</title><link>https://blogs.gnome.org/chergert/2025/08/11/week-32-status/</link><description>&lt;h2&gt;Foundry&lt;/h2&gt;
  2281. &lt;p&gt;This week was largely around getting the new template engine landed so it can be part of the 1.0 ABI. Basically just racing to get everything landed in time to commit to the API/ABI contract.&lt;/p&gt;
  2282. &lt;ul&gt;
  2283. &lt;li&gt;
  2284. &lt;p&gt;&lt;code&gt;FoundryTextBuffer&lt;/code&gt; gained some new type prerequisites to make it    easier for writing applications against them. Since Foundry is a    command line tool as well as a library, we don&amp;#8217;t just use    &lt;code&gt;GtkTextBuffer&lt;/code&gt; since the CLI doesn&amp;#8217;t even link against GTK. But it    is abstracted in such a way that the GTK application would implement    the &lt;code&gt;FoundryTextBuffer&lt;/code&gt; interface with a derived &lt;code&gt;GtkSourceBuffer&lt;/code&gt;.&lt;/p&gt;
  2285. &lt;/li&gt;
  2286. &lt;li&gt;
  2287. &lt;p&gt;&lt;code&gt;FoundryTextSettings&lt;/code&gt; has landed which provides a layered approach    to text editor settings similar (but better) than we have currently    in GNOME Builder. There is a new modeline implementation,    editorconfig, and gsettings backed settings provider which apply    in that order (with per-file overrides allowed at the tip).&lt;/p&gt;
  2288. &lt;p&gt;Where the settings-backed implementation surpasses Builder is that    it allows for layering there too. You can have user-overrides by    project, project defaults, as well as Foundry defaults.&lt;/p&gt;
  2289. &lt;p&gt;I still need to get the default settings per-language that we have    already (and are mostly shared with Text Editor too) as reasonable    defaults.&lt;/p&gt;
  2290. &lt;/li&gt;
  2291. &lt;li&gt;
  2292. &lt;p&gt;To allow changing the GSettings-based text settings above, the    &lt;code&gt;foundry settings set ...&lt;/code&gt; command gained support for specific    paths using the same &lt;code&gt;:/&lt;/code&gt; suffix that the &lt;code&gt;gsettings&lt;/code&gt; command uses.&lt;/p&gt;
  2293. &lt;/li&gt;
  2294. &lt;li&gt;
  2295. &lt;p&gt;Spent some time on the upcoming chat API for models so I can    experiment with what is possible when you control the entire tools    stack.&lt;/p&gt;
  2296. &lt;/li&gt;
  2297. &lt;li&gt;
  2298. &lt;p&gt;Dropped some features so they wouldn&amp;#8217;t be part of the 1.0. We can    implement them later on as time permits. Specifically I don&amp;#8217;t want    to commit to a MCP or DAP implementation yet since I&amp;#8217;m not fond of    either of them as an API.&lt;/p&gt;
  2299. &lt;/li&gt;
  2300. &lt;li&gt;
  2301. &lt;p&gt;The &lt;code&gt;FoundryInput&lt;/code&gt; subsystem gained support for license and language    inputs. This makes it much simpler to write templates in the new    internal template format.&lt;/p&gt;
  2302. &lt;/li&gt;
  2303. &lt;li&gt;
  2304. &lt;p&gt;Allow running &lt;code&gt;foundry template create ./FILE.template&lt;/code&gt; to create    a set of files or project from a template file. That allows you to    interate on your own templates for your project without having to    have them installed at the right location.&lt;/p&gt;
  2305. &lt;/li&gt;
  2306. &lt;li&gt;
  2307. &lt;p&gt;Wrote new project templates for empty project, shared library    project, and gtk4 projects. Still need to finish the gtk4 project    a bit to match feature parity with the version from Builder.&lt;/p&gt;
  2308. &lt;p&gt;I very much am happy with how the library project turned out because    this time around it supports Gir, Pkgconfig, Vapi generation, gi-doc,    and more. I still need to get library potfile support though.&lt;/p&gt;
  2309. &lt;p&gt;I also wrote new templates for creating gobjects and gtkwidgets in    C (but we can port to other languages if necessary). This is a new    type of &amp;#8220;code template&amp;#8221; as opposed to &amp;#8220;project template&amp;#8221;. It still    allows for multiple files to be created in the target project.&lt;/p&gt;
  2310. &lt;p&gt;What is particularly useful about it though is that we can allow    projects to expose templates specific to that project in the UI.    In Foundry, that means you have template access to create new    plugins, LSPs, and services quite easily.&lt;/p&gt;
  2311. &lt;/li&gt;
  2312. &lt;li&gt;
  2313. &lt;p&gt;Projects can specify their default license now to make more things    just happen automatically for contributors when creating new files.&lt;/p&gt;
  2314. &lt;/li&gt;
  2315. &lt;li&gt;
  2316. &lt;p&gt;Templates can include the default project license header simply now    by doing `{{include &amp;#8220;license.c&amp;#8221;}} where the suffix gets the properly    commented license block.&lt;/p&gt;
  2317. &lt;/li&gt;
  2318. &lt;li&gt;
  2319. &lt;p&gt;The API for expand templates has changed to return a &lt;code&gt;GListModel&lt;/code&gt; of    &lt;code&gt;FoundryTemplateOutput&lt;/code&gt;. The primary motivator here is that I want to    be able to have UI in Builder that lets you preview template before    actually saving the templates to disk.&lt;/p&gt;
  2320. &lt;/li&gt;
  2321. &lt;li&gt;
  2322. &lt;p&gt;A new API landed that we had in Builder for listing build targets.    Currently, only the &lt;code&gt;meson&lt;/code&gt; plugin implements the    &lt;code&gt;FoundryBuildTargetProvider&lt;/code&gt;. This is mostly plumbing for    upcoming features.&lt;/p&gt;
  2323. &lt;/li&gt;
  2324. &lt;li&gt;
  2325. &lt;p&gt;The new template format is a bit of amalgamation from a few formats    that is just based on my experience trying to find a way to maintain    these templates.&lt;/p&gt;
  2326. &lt;p&gt;It starts with a &lt;code&gt;GKeyFile&lt;/code&gt; block that describes the template and    inputs to the template.&lt;/p&gt;
  2327. &lt;p&gt;Then you have a series of what looks like markdown code blocks. You    can have conditionals around them which allows for optionally    including files based on input.&lt;/p&gt;
  2328. &lt;p&gt;The filename for the blocks can also be expanded based on template    inputs. The expansions are just &lt;code&gt;TmplExpr&lt;/code&gt; expressions from    template-glib.&lt;/p&gt;
  2329. &lt;p&gt;An example can be found at:&lt;/p&gt;
  2330. &lt;p&gt;https://gitlab.gnome.org/GNOME/foundry/-/blob/main/plugins/meson-templates/library.project&lt;/p&gt;
  2331. &lt;/li&gt;
  2332. &lt;/ul&gt;
  2333. &lt;h2&gt;Template-GLib&lt;/h2&gt;
  2334. &lt;ul&gt;
  2335. &lt;li&gt;
  2336. &lt;p&gt;Found some oopsies in how &lt;code&gt;TmplExpr&lt;/code&gt; evaluated branches so fixed    those up. Last year I wrote most of a C compiler and taking a look    at this code really makes me want to rewrite it all. The intermixing    of Yacc and GObject Introspection is ripe for improvement.&lt;/p&gt;
  2337. &lt;/li&gt;
  2338. &lt;li&gt;
  2339. &lt;p&gt;Added support for &lt;code&gt;==&lt;/code&gt; and &lt;code&gt;!=&lt;/code&gt; of &lt;code&gt;GStrv&lt;/code&gt; expressions.&lt;/p&gt;
  2340. &lt;/li&gt;
  2341. &lt;/ul&gt;
  2342. &lt;h2&gt;Other&lt;/h2&gt;
  2343. &lt;ul&gt;
  2344. &lt;li&gt;
  2345. &lt;p&gt;Play CI whack-a-mole for ICU changes in nightly SDKs&lt;/p&gt;
  2346. &lt;/li&gt;
  2347. &lt;li&gt;
  2348. &lt;p&gt;Propagate foundry changes to projects depending on it so that we    have useful flatpak manifests with minimal feature flags enabled.&lt;/p&gt;
  2349. &lt;/li&gt;
  2350. &lt;li&gt;
  2351. &lt;p&gt;Took a look at some performance issues in GNOME OS and passed along    some debugging techniques. Especially useful for when all you got    is an array of registers and need to know something.&lt;/p&gt;
  2352. &lt;/li&gt;
  2353. &lt;li&gt;
  2354. &lt;p&gt;Libpeas release for GNOME 49 beta&lt;/p&gt;
  2355. &lt;/li&gt;
  2356. &lt;/ul&gt;</description><author>Christian Hergert</author><dc:creator>Christian Hergert</dc:creator><pubDate>Mon, 11 Aug 2025 20:15:38 GMT</pubDate><guid isPermaLink="true">https://blogs.gnome.org/chergert/2025/08/11/week-32-status/</guid></item><item><title>Peter Hutterer: xkeyboard-config 2.45 has a new install location</title><link>http://who-t.blogspot.com/2025/08/xkeyboard-config-245-has-new-install.html</link><description>&lt;p&gt;
  2357.  This is a heads ups that if you install xkeyboard-config 2.45 (the package that provides the XKB &lt;b&gt;data&lt;/b&gt; files), some manual interaction may be needed. &lt;a href="https://xkeyboard-config.freedesktop.org/blog/2-45-release/#breaking-changes-2"&gt;Version 2.45 has changed the install location&lt;/a&gt; after over 20 years to be a) more correct and b) more flexible.
  2358. &lt;/p&gt;
  2359. &lt;p&gt;
  2360.  When you select a keyboard layout like "fr" or "de" (or any other ones really), what typically happens in the background is that an XKB parser (xkbcomp if you're on X, libxkbcommon if you're on Wayland) goes off and parses the data files provided by xkeyboard-config to populate the layouts. For historical reasons these data files have resided in &lt;code&gt;/usr/share/X11/xkb&lt;/code&gt; and that directory is hardcoded in more places than it should be (i.e. more than zero).
  2361.  As of xkeyboard-config 2.45 however, the data files are now installed in the much more sensible directory &lt;code&gt;/usr/share/xkeyboard-config-2&lt;/code&gt; with a matching &lt;code&gt;xkeyboard-config-2.pc&lt;/code&gt; for anyone who relies on the data files. The old location is symlinked to the new location so everything keeps working, people are happy, no hatemail needs to be written, etc. Good times.
  2362. &lt;/p&gt;
  2363. &lt;p&gt;
  2364.  The reason for this change is two-fold: moving it to a package-specific directory opens up the (admittedly mostly theoretical) use-case of some other package providing XKB data files. But even more so, it finally allows us to start versioning the data files and introduce new formats that may be backwards-incompatible for current parsers. This is not yet the case however, the current format in the new location is &lt;b&gt;guaranteed&lt;/b&gt; to be the same as the format we've always had, it's really just a location change in preparation for future changes.
  2365. &lt;/p&gt;
  2366. &lt;p&gt;
  2367.  Now, from an upstream perspective this is not just hunky, it's also dory. Distributions however struggle a bit more with this change because of packaging format restrictions. RPM for example is quite unhappy with a &lt;a href="https://docs.fedoraproject.org/en-US/packaging-guidelines/Directory_Replacement/"&gt;directory being replaced by a symlink&lt;/a&gt; which means that &lt;a href="https://src.fedoraproject.org/rpms/xkeyboard-config/pull-request/3"&gt;Fedora&lt;/a&gt; and &lt;a href="https://build.opensuse.org/request/show/1293638"&gt;OpenSuSE&lt;/a&gt; have to resort to the &lt;code&gt;.rpmmoved&lt;/code&gt; hack. If you have ever used the &lt;a href="https://who-t.blogspot.com/2021/02/a-pre-supplied-custom-keyboard-layout.html"&gt;custom layout&lt;/a&gt; and/or added other files to the XKB data files you will need to &lt;b&gt;manually move those files from &lt;code&gt;/usr/share/X11/xkb.rpmmoved/&lt;/code&gt; to the new equivalent location&lt;/b&gt;. If you have never used that layout and/or modified local you can just delete &lt;code&gt;/usr/share/X11/xkb.rpmmoved&lt;/code&gt;.  Of course, if you're on Wayland you shouldn't need to modify system directories anyway since you can do &lt;a href="https://xkbcommon.org/doc/current/user-configuration.html"&gt;it in your $HOME&lt;/a&gt;.
  2368. &lt;/p&gt;
  2369. &lt;p&gt;
  2370.  Corresponding issues on what to do on &lt;a href="https://gitlab.archlinux.org/archlinux/packaging/packages/xkeyboard-config/-/issues/1"&gt;Arch&lt;/a&gt; and &lt;a href="https://bugs.gentoo.org/957712"&gt;Gentoo&lt;/a&gt;, I'm not immediately aware of other distributions's issues but if you search for them in your bugtracker you'll find them.
  2371. &lt;/p&gt;</description><author>Peter Hutterer</author><dc:creator>Peter Hutterer</dc:creator><pubDate>Mon, 11 Aug 2025 11:44:00 GMT</pubDate><guid isPermaLink="true">http://who-t.blogspot.com/2025/08/xkeyboard-config-245-has-new-install.html</guid></item><item><title>Steven Deobald: 2025-08-08 Foundation Update</title><link>https://blogs.gnome.org/steven/2025/08/08/2025-08-08-foundation-update/</link><description>&lt;h2 id="opaque-things"&gt;## Opaque Things&lt;/h2&gt;
  2372. &lt;p&gt;Very unfortunately, most of my past two weeks have been spent on &amp;#8220;opaque things.&amp;#8221; Let&amp;#8217;s just label that whole bundle &amp;#8220;bureaucracy&amp;#8221; for now.&lt;/p&gt;
  2373. &lt;p&gt;&amp;nbsp;&lt;/p&gt;
  2374. &lt;h2 id="apology"&gt;## Apology&lt;/h2&gt;
  2375. &lt;p&gt;I owe a massive apology to the GIMP team. These folks have been incredibly patient while the GNOME Foundation, as their fiscal host, sets up a bunch of new paperwork for them. It&amp;#8217;s been a slow process.&lt;/p&gt;
  2376. &lt;p&gt;Due to the above bureaucracy, this process has been even slower than usual. Particularly on GIMP&amp;#8217;s big pearl anniversary year, this is especially frustrating for them and the timing is awful.&lt;/p&gt;
  2377. &lt;p&gt;As long as I am in this role, I accept responsibility for the Foundation&amp;#8217;s struggles in supporting the GIMP project. I&amp;#8217;m sorry, folks, and I hope we get past the current round of difficulties quickly so we can provide the fiscal hosting you deserve.&lt;/p&gt;
  2378. &lt;p&gt;&amp;nbsp;&lt;/p&gt;
  2379. &lt;h2&gt;## Advisory Board Room&lt;/h2&gt;
  2380. &lt;p&gt;One of my favourite parts of GUADEC was our Advisory Board day. I really enjoyed hearing what all our Advisory Board members are working on, their challenges, and how they might collaborate with one another. It was a &lt;em&gt;really&lt;/em&gt; productive day and we all agreed we&amp;#8217;d like to continue that feeling throughout the year. We&amp;#8217;ve started a new Advisory Board Room, as a result. The medium for this meeting place may change (as required), but it&amp;#8217;s my commitment to support it, since the Foundation is the town hall for these organizations. SUSE, Canonical, Red Hat, the Document Foundation, Endless, and Debian were all in attendance at GUADEC β€” it was an honour to bring these folks together. We recently had postmarketOS join our Advisory Board and, given how much progress they&amp;#8217;ve already made, I&amp;#8217;m excited to see them breathe new life into the GNOME ecosystem. The desktop is rock solid. Mobile is growing quickly. I can&amp;#8217;t wait to listen to more of these conversations.&lt;/p&gt;
  2381. &lt;p&gt;&amp;nbsp;&lt;/p&gt;
  2382. &lt;h2 id="draft-budget"&gt;## Draft Budget&lt;/h2&gt;
  2383. &lt;p&gt;Thanks to Deepa&amp;#8217;s tremendous work teasing apart our financial reporting, she and I have a draft budget to present to the Board on their August 12th regular meeting.&lt;/p&gt;
  2384. &lt;p&gt;The board has already seen a preliminary version of the budget as of their July 27th meeting and an early draft as of last week. Our schedule has an optional budget review August 26th, another required budget review on September 9th and, if we need a final meeting to pass the budget, we can do that at the September 23rd board meeting. Last year, Richard passed the first on-time GNOME Foundation budget in many years. I&amp;#8217;m optimistic we can pass a clear, uncomplicated budget a month early in 2025.&lt;/p&gt;
  2385. &lt;p&gt;(Our fiscal year is October &amp;#8211; September.)&lt;/p&gt;
  2386. &lt;p&gt;I&amp;#8217;m incredibly grateful to Deepa for all the manual work she&amp;#8217;s put into our finances during an already busy summer. Deepa&amp;#8217;s also started putting in place a tagging mechanism with our bookkeepers which will hopefully resolve this in a more automatic way in the future. The tagging mechanism will work in conjunction with our chart of accounts, which doesn&amp;#8217;t really represent the GNOME Foundation&amp;#8217;s operating capital at all, as a 501(c)(3)&amp;#8217;s chart of accounts is geared toward the picture we show to the IRS, not the picture we show to the Board or use to pass a budget. It&amp;#8217;s the same data, but two very different lenses on it.&lt;/p&gt;
  2387. &lt;p&gt;Understanding is the first step. After a month of wrestling with the data, we now understand it. Automation is the second step. The board should know exactly where the Foundation stands, financially, every year, every quarter, and every month&amp;#8230; without the need for manual reports.&lt;/p&gt;
  2388. &lt;p&gt;&amp;nbsp;&lt;/p&gt;
  2389. &lt;h2 id="501-c-3-structural-improvements"&gt;## 501(c)(3) Structural Improvements&lt;/h2&gt;
  2390. &lt;p&gt;As I mention in my &lt;a class="external" href="https://www.youtube.com/live/Z7F3fghCQB4?si=hRzQazbKPcKJugZs&amp;amp;t=210"&gt;GUADEC keynote&lt;/a&gt;, the GNOME Foundation has a long road ahead of it to become an ideal 501(c)(3) &amp;#8230; but we just need to make sure we&amp;#8217;re focused on continuous improvement. Other non-profits also struggle with their own structural challenges, since charities can take many different shapes and are often held together almost entirely by volunteers. Every organization is different, every organization wants to succeed.&lt;/p&gt;
  2391. &lt;p&gt;I had the pleasure of some consultation conversations this week with Amy Parker of the OpenSSL Foundation and Halle Baksh, a 501(c) expert from California. Delightful folks. Super helpful.&lt;/p&gt;
  2392. &lt;p&gt;One of the greatest things I&amp;#8217;m finding about working in the non-profit space is that we have a tremendous support network. Everyone wants us to succeed and every time we&amp;#8217;re ready to take the next step, there will be someone there to help us level up. Every time we do, the Foundation will get more mature and, as a result, more effective.&lt;/p&gt;
  2393. &lt;p&gt;&amp;nbsp;&lt;/p&gt;
  2394. &lt;h2&gt;## Travel Policy Freeze&lt;/h2&gt;
  2395. &lt;p&gt;I created some confusion by mentioning the travel policy freeze in my AGM slides. A group of hackers asked me at dinner on the last night in Brescia, &amp;#8220;does this mean that all travel for contributors is cancelled?&amp;#8221;&lt;/p&gt;
  2396. &lt;p&gt;No, absolutely not. The travel policy freeze means that every travel request must be authorized by the Executive Director and the President; we have temporarily suspended the Travel Committee&amp;#8217;s spending authority. We will of course still sponsor visas for interns and other program-related travel. The intention of the travel policy freeze is to reduce administrative travel costs and add them to program travel, not the other way around.&lt;/p&gt;
  2397. &lt;p&gt;Sorry if I freaked anyone out. The goal (as long as I&amp;#8217;m around) will always be to push Foundation resources toward programs like development, infrastructure, and events.&lt;/p&gt;
  2398. &lt;p&gt;&amp;nbsp;&lt;/p&gt;
  2399. &lt;h2 id="getting-back-to-work"&gt;## Getting Back To Work&lt;/h2&gt;
  2400. &lt;p&gt;Sorry again for the short update. I&amp;#8217;m optimistic that we&amp;#8217;ll get over this bureaucratic bump in the road soon enough. Thanks for your patience.&lt;/p&gt;</description><author>Steven Deobald</author><dc:creator>Steven Deobald</dc:creator><pubDate>Sat, 09 Aug 2025 01:02:32 GMT</pubDate><guid isPermaLink="true">https://blogs.gnome.org/steven/2025/08/08/2025-08-08-foundation-update/</guid></item><item><title>Richard Hughes: LVFS Sustainability Plan</title><link>https://blogs.gnome.org/hughsie/2025/08/08/lvfs-sustainability-plan/</link><description>&lt;p&gt;&lt;strong&gt;tl;dr:&lt;/strong&gt; I&amp;#8217;m asking the biggest users of the LVFS to sponsor the project.&lt;/p&gt;
  2401. &lt;p&gt;The Linux Foundation is kindly paying for all the hosting costs of the LVFS, and Red Hat pays for all my time &amp;#8212; but as LVFS grows and grows that&amp;#8217;s going to be less and less sustainable longer term. We’re trying to find funding to hire additional resources as a β€œme replacement” so that there is backup and additional attention to LVFS (and so that I can go on holiday for two weeks without needing to take a laptop with me).&lt;/p&gt;
  2402. &lt;p&gt;This year there will be a fair-use quota introduced, with different sponsorship levels having a different quota allowance. Nothing currently happens if the quota is exceeded, although there will be additional warnings asking the vendor to contribute. The β€œassociate” (free) quota is also generous, with 50,000 monthly downloads and 50 monthly uploads. This means that almost all the 140 vendors on the LVFS should expect no changes.&lt;/p&gt;
  2403. &lt;p&gt;Vendors providing millions of firmware files to end users (and deriving tremendous value from the LVFS…) should really either be providing a developer to help write shared code, design abstractions and review patches (like AMD does) or allocate some funding so that we can pay for resources to take action for them. So far no OEMs provide any financial help for the infrastructure itself, although two have recently offered &amp;#8212; and we&amp;#8217;re now in a position to &amp;#8220;&lt;em&gt;say yes&lt;/em&gt;&amp;#8221; to the offers of help.&lt;/p&gt;
  2404. &lt;p&gt;I&amp;#8217;ve written a &lt;a class="external" href="https://docs.google.com/presentation/d/1l_Rmkn2-UwOkS0XK5d6u9pi29TZpmG-upa4Lu5HuEy4/edit?usp=sharing"&gt;LVFS Project Sustainability Plan&lt;/a&gt; that explains the problem and how OEMs should work with the Linux Foundation to help fund the LVFS.&lt;/p&gt;
  2405. &lt;p&gt;I&amp;#8217;m aware funding open source software is a delicate matter and I certainly do not want to cause anyone worry. We need the LVFS to have strong foundations; it needs to grow, adapt, and be resilient – and it needs vendor support.&lt;/p&gt;
  2406. &lt;p&gt;Draft timeline, which is probably a little aggressive for the OEMs &amp;#8212; so the dates might be moved back in the future:&lt;/p&gt;
  2407. &lt;p&gt;&lt;strong&gt;APR 2025&lt;/strong&gt;: We started showing the historical percentage β€œfair use” download utilization graph in vendor pages. As time goes on this will also be recorded into per-protocol sections too.&lt;/p&gt;
  2408. &lt;p&gt;&lt;a href="https://blogs.gnome.org/hughsie/files/2025/08/Screenshot-2025-08-08-at-12-40-00-LVFS-Vendor-Quota.png"&gt;&lt;img alt="downloads over time" class="aligncenter size-large wp-image-9995" height="300" src="https://blogs.gnome.org/hughsie/files/2025/08/Screenshot-2025-08-08-at-12-40-00-LVFS-Vendor-Quota-1024x465.png" width="660" /&gt;&lt;/a&gt;&lt;/p&gt;
  2409. &lt;p&gt;&lt;strong&gt;JUL 2025&lt;/strong&gt;: We started showing the historical percentage β€œfair use” upload utilization, also broken into per-protocol sections:&lt;/p&gt;
  2410. &lt;p&gt;&lt;a href="https://blogs.gnome.org/hughsie/files/2025/08/Screenshot-2025-08-08-at-13-34-46-LVFS-Vendor-Quota.png"&gt;&lt;img alt="uploads over time" class="aligncenter size-large wp-image-9997" height="300" src="https://blogs.gnome.org/hughsie/files/2025/08/Screenshot-2025-08-08-at-13-34-46-LVFS-Vendor-Quota-1024x465.png" width="660" /&gt;&lt;/a&gt;&lt;/p&gt;
  2411. &lt;p&gt;&lt;strong&gt;JUL 2025&lt;/strong&gt;: We started restricting logos on the main index page to vendors joining as startup or above level &amp;#8212; note Red Hat isn&amp;#8217;t sponsoring the LVFS with money (but they do pay my salary!) &amp;#8212; I&amp;#8217;ve just used the logo as a placeholder to show what it would look like.&lt;/p&gt;
  2412. &lt;p&gt;&lt;a href="https://blogs.gnome.org/hughsie/files/2025/08/lvfs-index.png"&gt;&lt;img alt="" class="aligncenter size-full wp-image-9999" height="417" src="https://blogs.gnome.org/hughsie/files/2025/08/lvfs-index.png" width="799" /&gt;&lt;/a&gt;&lt;/p&gt;
  2413. &lt;p&gt;&lt;strong&gt;AUG 2025&lt;/strong&gt;: I created this blogpost and sent an email to the lvfs-announce mailing list.&lt;/p&gt;
  2414. &lt;p&gt;&lt;strong&gt;AUG 2025&lt;/strong&gt;: We allow vendors to join as startup or premier sponsors shown on the main page and show the badge on the vendor list&lt;/p&gt;
  2415. &lt;p&gt;&lt;a href="https://blogs.gnome.org/hughsie/files/2025/08/Screenshot-2025-07-23-at-16-27-15-LVFS-Vendor-Status.png"&gt;&lt;img alt="" class="aligncenter size-large wp-image-10000" height="64" src="https://blogs.gnome.org/hughsie/files/2025/08/Screenshot-2025-07-23-at-16-27-15-LVFS-Vendor-Status-1024x100.png" width="660" /&gt;&lt;/a&gt;&lt;/p&gt;
  2416. &lt;p&gt;&lt;strong&gt;DEC 2025&lt;/strong&gt;: Start showing over-quota warnings on the per-firmware pages&lt;/p&gt;
  2417. &lt;p&gt;&lt;a href="https://blogs.gnome.org/hughsie/files/2025/08/Screenshot-2025-08-08-at-13-42-04-LVFS-Firmware-Details.png"&gt;&lt;img alt="" class="aligncenter size-large wp-image-10002" height="133" src="https://blogs.gnome.org/hughsie/files/2025/08/Screenshot-2025-08-08-at-13-42-04-LVFS-Firmware-Details-1024x206.png" width="660" /&gt;&lt;/a&gt;&lt;/p&gt;
  2418. &lt;p&gt;&lt;strong&gt;DEC 2025&lt;/strong&gt;: Turn off detailed per-firmware analytics to vendors below startup sponsor level&lt;/p&gt;
  2419. &lt;p&gt;&lt;a href="https://blogs.gnome.org/hughsie/files/2025/08/Screenshot-2025-08-08-at-13-43-09-LVFS-Firmware-Details.png"&gt;&lt;img alt="" class="aligncenter size-large wp-image-10001" height="88" src="https://blogs.gnome.org/hughsie/files/2025/08/Screenshot-2025-08-08-at-13-43-09-LVFS-Firmware-Details-1024x136.png" width="660" /&gt;&lt;/a&gt;&lt;/p&gt;
  2420. &lt;p&gt;&lt;strong&gt;APR 2026&lt;/strong&gt;: Turn off access to custom LVFS API for vendors below Startup Sponsorship level, for instance:&lt;/p&gt;
  2421. &lt;ul&gt;
  2422. &lt;li&gt;&lt;code&gt;/lvfs/component/{}/modify/json&lt;/code&gt;&lt;/li&gt;
  2423. &lt;li&gt;&lt;code&gt;/lvfs/vendors/auth&lt;/code&gt;&lt;/li&gt;
  2424. &lt;li&gt;&lt;code&gt;/lvfs/firmware/auth&lt;/code&gt;&lt;/li&gt;
  2425. &lt;/ul&gt;
  2426. &lt;p&gt;&lt;strong&gt;APR 2026&lt;/strong&gt;: Limit the number of authenticated automated robot uploads for less than Startup Sponsorship levels.&lt;/p&gt;
  2427. &lt;p&gt;Comments welcome!&lt;/p&gt;</description><author>Richard Hughes</author><dc:creator>Richard Hughes</dc:creator><pubDate>Fri, 08 Aug 2025 13:28:48 GMT</pubDate><guid isPermaLink="true">https://blogs.gnome.org/hughsie/2025/08/08/lvfs-sustainability-plan/</guid></item><item><title>Andy Wingo: whippet hacklog: adding freelists to the no-freelist space</title><link>https://wingolog.org/archives/2025/08/07/whippet-hacklog-adding-freelists-to-the-no-freelist-space</link><description>&lt;div&gt;&lt;p&gt;August greetings, comrades!  Today I want to bookend some recent work on
  2428. &lt;a href="https://wingolog.org/archives/2025/07/08/guile-lab-notebook-on-the-move"&gt;my Immix-inspired garbage
  2429. collector&lt;/a&gt;:
  2430. firstly, an idea with muddled results, then a slog through heuristics.&lt;/p&gt;&lt;h3&gt;the big idea&lt;/h3&gt;&lt;p&gt;My mostly-marking collector’s main space is called the &lt;a href="https://arxiv.org/abs/2503.16971"&gt;β€œnofl space”&lt;/a&gt;.
  2431. Its name comes from its historical evolution from mark-sweep to mark-region:
  2432. instead of sweeping unused memory to freelists and allocating from those
  2433. freelists, sweeping is interleaved with allocation; β€œnofl” means
  2434. β€œno free-list”.  As it finds holes, the collector bump-pointer allocates into those
  2435. holes.  If an allocation doesn’t fit into the current hole, the collector sweeps
  2436. some more to find the next hole, possibly fetching another block.  Space
  2437. for holes that are too small is effectively wasted as fragmentation;
  2438. mutators will try again after the next GC.  Blocks with lots of
  2439. holes will be chosen for opportunistic evacuation, which is the heap
  2440. defragmentation mechanism.&lt;/p&gt;&lt;p&gt;Hole-too-small fragmentation has bothered me, because it presents a
  2441. potential pathology.  You don’t know how a GC will be used or what the
  2442. user’s allocation pattern will be; if it is a mix of medium (say, a
  2443. kilobyte) and small (say, 16 bytes) allocations, one could imagine a
  2444. medium allocation having to sweep over lots of holes, discarding them in
  2445. the process, which hastens the next collection.  Seems wasteful,
  2446. especially for non-moving configurations.&lt;/p&gt;&lt;p&gt;So I had a thought: why not collect those holes into a &lt;a href="https://github.com/wingo/whippet/blob/main/src/nofl-holeset.h"&gt;size-segregated
  2447. freelist&lt;/a&gt;?  We just cleared the hole, the memory is core-local, and we
  2448. might as well.  Then before fetching a new block, the allocator
  2449. slow-path can see if it can service an allocation from the second-chance
  2450. freelist of holes.  This decreases locality a bit, but maybe it’s worth
  2451. it.&lt;/p&gt;&lt;p&gt;Thing is, I implemented it, and I don’t know if it’s worth it!  It seems
  2452. to interfere with evacuation, in that the blocks that would otherwise be
  2453. most profitable to evacuate, because they contain many holes, are
  2454. instead filled up with junk due to second-chance allocation from the
  2455. freelist.  I need to do more measurements, but I think my big-brained
  2456. idea is a bit of a wash, at least if evacuation is enabled.&lt;/p&gt;&lt;h3&gt;heap growth&lt;/h3&gt;&lt;p&gt;When running the new collector in Guile, we have a performance oracle in
  2457. the form of BDW: it had better be faster for Guile to compile a Scheme
  2458. file with the new nofl-based collector than with BDW.  In this use case
  2459. we have an additional degree of freedom, in that unlike the lab tests of
  2460. nofl vs BDW, we don’t impose a fixed heap size, and instead allow
  2461. heuristics to determine the growth.&lt;/p&gt;&lt;p&gt;BDW’s built-in heap growth heuristics are very opaque.  You give it a
  2462. heap multiplier, but as a divisor truncated to an integer.  It’s very
  2463. imprecise.  Additionally, there are nonlinearities: BDW is relatively
  2464. more generous for smaller heaps, because attempts to model and amortize
  2465. tracing cost, and there are some fixed costs (thread sizes, static data
  2466. sizes) that don’t depend on live data size.&lt;/p&gt;&lt;p&gt;Thing is, BDW’s heuristics work pretty well.  For example, I had a
  2467. process that ended with a heap of about 60M, for a peak live data size
  2468. of 25M or so.  If I ran my collector with a fixed heap multiplier, it
  2469. wouldn’t do as well as BDW, because it collected much more frequently
  2470. when the heap was smaller.&lt;/p&gt;&lt;p&gt;I ended up switching from the primitive
  2471. β€œsize the heap as a multiple of live data” strategy to live data plus a
  2472. square root factor; this is like what Racket ended up doing in its
  2473. simple implementation of &lt;a href="https://marisa.moe/balancer.html"&gt;MemBalancer&lt;/a&gt;.  (I do have a proper implementation
  2474. of MemBalancer, with time measurement and shrinking and all, but I
  2475. haven’t put it through its paces yet.)  With this fix I can meet BDW’s performance
  2476. for my Guile-compiling-Guile-with-growable-heap workload.  It would be
  2477. nice to exceed BDW of course!&lt;/p&gt;&lt;h3&gt;parallel worklist tweaks&lt;/h3&gt;&lt;p&gt;Previously, in parallel configurations, trace workers would each have a
  2478. &lt;a href="https://inria.hal.science/hal-00802885/document"&gt;Chase-Lev deque&lt;/a&gt; to which they could publish objects needing tracing.
  2479. Any worker could steal an object from the top of a worker’s public
  2480. deque.  Also, each worker had a local, unsynchronized FIFO worklist,
  2481. some 1000 entries in length; when this worklist filled up, the worker
  2482. would publish its contents.&lt;/p&gt;&lt;p&gt;There is a pathology for this kind of setup, in which one worker can end
  2483. up with a lot of work that it never publishes.  For example, if there
  2484. are 100 long singly-linked lists on the heap, and the worker happens to have them all on
  2485. its local FIFO, then perhaps they never get published, because the FIFO
  2486. never overflows; you end up not parallelising.  This seems to be the case
  2487. in one microbenchmark.  I switched to not have local worklists at all;
  2488. perhaps this was not the right thing, but who knows.  Will poke in
  2489. future.&lt;/p&gt;&lt;h3&gt;a hilarious bug&lt;/h3&gt;&lt;p&gt;Sometimes you need to know whether a given address is in an object
  2490. managed by the garbage collector.  For the nofl space it’s pretty easy,
  2491. as we have big slabs of memory; bisecting over the array of slabs is
  2492. fast.  But for large objects whose memory comes from the kernel, we
  2493. don’t have that.  (Yes, you can reserve a big ol’ region with
  2494. &lt;tt&gt;PROT_NONE&lt;/tt&gt; and such, and then allocate into that region; I don’t do
  2495. that currently.)&lt;/p&gt;&lt;p&gt;Previously I had a splay tree for lookup.  Splay trees are great but not
  2496. so amenable to concurrent access, and parallel marking is one place where we need to do this lookup.  So I prepare a sorted array before marking, and then
  2497. bisect over that array.&lt;/p&gt;&lt;p&gt;Except a funny thing happened: I switched the bisect routine to return
  2498. the start address if an address is in a region.  Suddenly, weird
  2499. failures started happening randomly.  Turns out, in some places I was
  2500. testing if bisection succeeded with an &lt;tt&gt;int&lt;/tt&gt;; if the region happened to
  2501. be 32-bit-aligned, then the nonzero 64-bit &lt;tt&gt;uintptr_t&lt;/tt&gt; got truncated to its low 32
  2502. bits, which were zero.  Yes, crusty reader, Rust would have caught this!&lt;/p&gt;&lt;h3&gt;fin&lt;/h3&gt;&lt;p&gt;I want this new collector to work.  Getting the growth heuristic good
  2503. enough is a step forward.  I am annoyed that second-chance allocation
  2504. didn’t work out as well as I had hoped; perhaps I will find some time
  2505. this fall to give a proper evaluation.  In any case, thanks for reading,
  2506. and hack at you later!&lt;/p&gt;&lt;/div&gt;</description><author>Andy Wingo</author><dc:creator>Andy Wingo</dc:creator><pubDate>Thu, 07 Aug 2025 15:02:04 GMT</pubDate><guid isPermaLink="true">https://wingolog.org/archives/2025/08/07/whippet-hacklog-adding-freelists-to-the-no-freelist-space</guid></item><item><title>Jussi Pakkanen: Let's properly analyze an AI article for once</title><link>https://nibblestew.blogspot.com/2025/08/lets-properly-analyze-ai-article-for.html</link><description>&lt;p&gt;Recently the CEO of Github wrote a blog post called &lt;a href="https://ashtom.github.io/developers-reinvented"&gt;Developers reinvented&lt;/a&gt;. It was reposted with various clickbait headings like&amp;nbsp;&lt;a href="https://www.finalroundai.com/blog/github-ceo-thomas-dohmke-warns-developers-embrace-ai-or-quit"&gt;GitHub CEO Thomas Dohmke Warns Developers: "Either Embrace AI or Get Out of This Career"&lt;/a&gt;&amp;nbsp;(that one feels like an LLM generated summary of the actual post, which would be ironic if it wasn't awful). To my great misfortune I read both of these. Even if we ignore whether AI is useful or not, the writings contain some of the absolute worst reasoning and stretched logical leaps I have seen in years, maybe decades. If you are ever in the need of finding out how not to write a "scientific" text on any given subject, this is the disaster area for you.&lt;/p&gt;&lt;p&gt;But before we begin, a detour to the east.&lt;/p&gt;&lt;h1 style="text-align: left;"&gt;Statistics and the Soviet Union&lt;/h1&gt;&lt;p style="text-align: left;"&gt;One of the great wonders of statistical science of the previous century was without a doubt the Soviet Union. They managed to invent and perfect dozens of ways to turn data to your liking, no matter the reality. Almost every official statistic issued by USSR was a lie. Most people know this. But even most of those do not grasp &lt;i&gt;just how much&lt;/i&gt; the stats differed from reality. I sure didn't until I read &lt;a href="https://www.goodreads.com/book/show/39679779-empire-of-the-absurd"&gt;this book&lt;/a&gt;. Let's look at some examples.&lt;/p&gt;&lt;h2 style="text-align: left;"&gt;Only ever report percentages&lt;/h2&gt;&lt;p style="text-align: left;"&gt;The USSR's glorious statistics tended to be of the type "manufacturing of shoes grew over 600% this five year period". That certainly sounds a lot better than "In the last five years our factory made 700 pairs of shoes as opposed to 100" or even "7 shoes instead of 1". If you are really forward thinking, you can even cut down shoe production on those five year periods when you are not being measured. It makes the stats even more impressive, even though in reality many people have no shoes at all.&lt;/p&gt;&lt;p style="text-align: left;"&gt;The USSR classified the real numbers as state secrets because the truth would have made them look bad. If a corporation only gives you percentages, they may be doing the same thing. Apply skepticism as needed.&lt;/p&gt;&lt;h2 style="text-align: left;"&gt;Creative comparisons&lt;/h2&gt;&lt;p style="text-align: left;"&gt;The previous section said the manufacturing of shoes has grown. Can you tell what it is not saying? That's right, growth over what? It is implied that the comparison is to the previous five year plan. But it is not. Apparently a common comparison in these cases was the production amounts of the year 1913. This "best practice" was not only used in the early part of the 1900s, it was used far into the 1980s.&lt;/p&gt;&lt;p style="text-align: left;"&gt;Some of you might wonder why 1913 and not 1916, which was the last year before the bolsheviks took over? Simply because that was the century's worst year for Russia as a whole. So if you encounter a claim that "car manufacturing was up 3700%" some year in 1980s Soviet Union, now you know what that actually meant.&lt;/p&gt;&lt;h2 style="text-align: left;"&gt;"Better" measurements&lt;/h2&gt;&lt;p style="text-align: left;"&gt;According to official propaganda, the USSR was the world's leading country in wheat production. In this case they even listed out the production in absolute tonnes. In reality it was all fake. The established way of measuring wheat yields is to measure the "dry weight", that is, the mass of final processed grains. When it became apparent that the USSR could not compete with imperial scum, they changed their measurements to "wet weight". This included the mass of &lt;i&gt;everything&lt;/i&gt; that came out from the nozzle of a harvester, such as stalks, rats, mud, rain water, dissidents and so on.&lt;/p&gt;&lt;p style="text-align: left;"&gt;Some people outside the iron curtain even believed those numbers. Add your own analogy between those people and modern VC investors here.&lt;/p&gt;&lt;h1 style="text-align: left;"&gt;To business then&lt;/h1&gt;&lt;p style="text-align: left;"&gt;The actual blog post starts with this thing that can be considered a picture.&lt;/p&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjnvKIDpXIXw4sobIuJol13Nyvo2k3lS465_e8v-HNDYAR0pMz653j2pKgenz8pJodfFpqdJaAT-N68z-M_REejgPWmv9z0j87EqFsQ6prRY_u8AezZKHjpvbvXvMx2fgOd3B9HqUeIumtRiaZFDRZJwERKKr0Q-zHq83XD6T2nrPbo1VwyTLPlBqzg01Q/s720/reinvented.png" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="213" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjnvKIDpXIXw4sobIuJol13Nyvo2k3lS465_e8v-HNDYAR0pMz653j2pKgenz8pJodfFpqdJaAT-N68z-M_REejgPWmv9z0j87EqFsQ6prRY_u8AezZKHjpvbvXvMx2fgOd3B9HqUeIumtRiaZFDRZJwERKKr0Q-zHq83XD6T2nrPbo1VwyTLPlBqzg01Q/s320/reinvented.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;p style="text-align: left;"&gt;What message would this choice of image tell about the person using it in their blog post?&lt;/p&gt;&lt;p style="text-align: left;"&gt;&lt;/p&gt;&lt;ol style="text-align: left;"&gt;&lt;li&gt;Said person does not have sufficient technical understanding to grasp the fact that children's toy blocks should, in fact, be affected by gravity (or that perspective is a thing, but we'll let that pass).&lt;/li&gt;&lt;li&gt;Said person does not give a shit about whether things are correct or could even work, as long as they look "somewhat plausible".&lt;/li&gt;&lt;/ol&gt;Are these the sort of traits a person in charge of &lt;i&gt;the largest software development platform on Earth&lt;/i&gt;&amp;nbsp;should have? No, they are not.&lt;p&gt;&lt;/p&gt;&lt;p style="text-align: left;"&gt;To add insult to injury, the image seems to have been created with the Studio Ghibli image generator, which Hayao Miyazaki described as an abomination on art itself. Cultural misappropriation is high on the list of core values at Github HQ it seems.&lt;/p&gt;&lt;p style="text-align: left;"&gt;With that let's move on to the actual content, which is this post from Twitter (to quote Matthew Garrett, I will respect their name change once Elon Musk starts respecting his child's).&lt;/p&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgt9Ybd7z0kDtsXvmnc398YA5WEL1wzhJm7zvZMY8N68ms8UfUBkIvrz0upLIlZzPAAYytcOyE84bEoCCFA_lxafqrOTDKpGZtQD9IqYeIZsBgj_F_YBCtW6MEsZsfemMvAb4B2shrZHywPf5da8cyB3MYuYJF4EcFhqu_1j4nAhvanCQPi4fE5Is7mNCg/s808/evidence.png" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="160" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgt9Ybd7z0kDtsXvmnc398YA5WEL1wzhJm7zvZMY8N68ms8UfUBkIvrz0upLIlZzPAAYytcOyE84bEoCCFA_lxafqrOTDKpGZtQD9IqYeIZsBgj_F_YBCtW6MEsZsfemMvAb4B2shrZHywPf5da8cyB3MYuYJF4EcFhqu_1j4nAhvanCQPi4fE5Is7mNCg/s320/evidence.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;p style="text-align: left;"&gt;Oh, wow! A field study. That makes things clear. With evidence and all! How can we possibly argue against that?&lt;/p&gt;&lt;p style="text-align: left;"&gt;Easily. As with a child.&lt;/p&gt;&lt;p style="text-align: left;"&gt;Let's look at this "study" (and I'm using the word in its loosest possible term here) and its details with an actual critical eye. The first thing is statistical representativeness. The sample size is 22. According to &lt;a href="http://www.raosoft.com/samplesize.html"&gt;this sample size calculator&lt;/a&gt; I found, a required sample size for just one thousand people would be 278, but, you know, one order of magnitude one way or another, who cares about those? Certainly not business big shot movers and shakers. Like Stockton Rush for example.&lt;/p&gt;&lt;p style="text-align: left;"&gt;The math above assumes an unbiased sampling. The post does not even attempt to answer whether that is the case. It would mean getting answers to questions like:&lt;/p&gt;&lt;p style="text-align: left;"&gt;&lt;/p&gt;&lt;ul style="text-align: left;"&gt;&lt;li&gt;How were the 22 people chosen?&lt;/li&gt;&lt;li&gt;How many different companies, skill levels, nationalities, genders, age groups etc were represented?&lt;/li&gt;&lt;li&gt;Did they have any personal financial incentive on making their new AI tools look good?&lt;/li&gt;&lt;li&gt;Were they under any sort of duress to produce the "correct" answers?&lt;/li&gt;&lt;li&gt;What was/were the exact phrase(s) that was asked?&lt;/li&gt;&lt;li&gt;Were they the same for all participants?&lt;/li&gt;&lt;li&gt;Was the test run multiple times until it produced the desired result?&lt;/li&gt;&lt;/ul&gt;The latter is an age old trick where you run a test with random results over and over on small groups. Eventually you will get a run that points the way you want. Then you drop the earlier measurements and publish the last one. In "the circles" this is known as &lt;i&gt;data set selection&lt;/i&gt;.&lt;p&gt;&lt;/p&gt;&lt;p style="text-align: left;"&gt;Just to be sure, I'm &lt;i&gt;not&lt;/i&gt; saying that is what they did. But if someone drove a dump truck full of money to my house and asked me to create a "study" that produced these results, that is exactly how I would&amp;nbsp;do it. (I would not actually do it because I have a spine.)&lt;/p&gt;&lt;p style="text-align: left;"&gt;Moving on. The main headline grabber is "Either you embrace AI or get out of this career". If you actually read the post (I know), what you find is that this is actually a quote from one of the participants. It's a bit difficult to decipher from the phrasing but my reading is that this is not a grandstanding hurrah of all things AI, but more of a "I guess this is something I'll have to get used to" kind of submission. That is not evidence, certainly not of the clear type. It is an opinion.&lt;/p&gt;&lt;p style="text-align: left;"&gt;The post then goes on a buzzwordsalad tour of statements that range from the incomprehensible to the puzzling. Perhaps the weirdest is this nugget on education:&lt;/p&gt;&lt;blockquote&gt;&lt;p style="text-align: left;"&gt;Teaching [programming] in a way that evaluates rote syntax or memorization of APIs is becoming obsolete.&lt;/p&gt;&lt;/blockquote&gt;&lt;p style="text-align: left;"&gt;It is not "becoming obsolete". It has been considered the wrong thing to do for as long as computer science has existed. Learning the syntax of most programming languages takes a few lessons, the rest of the semester is spent on actually using the language to solve problems. Any curriculum not doing that is just plain bad. Even worse than CS education in Russia in 1913.&lt;/p&gt;&lt;p style="text-align: left;"&gt;You might also ponder that if the author is &lt;i&gt;so&lt;/i&gt; out of touch with reality in this simple issue, how completely off base the rest of his statements might be. In fact the statement is so wrong at such a fundamental level that it has probably been generated with an LLM.&lt;/p&gt;&lt;h1 style="text-align: left;"&gt;A magician's shuffle&lt;/h1&gt;&lt;p style="text-align: left;"&gt;As nonsensical as the Twitter post is, we have not yet even mentioned the biggest misdirection in it. You might not even have noticed it yet. I certainly did not until I read the actual post. Try if you can spot it.&lt;/p&gt;&lt;p style="text-align: left;"&gt;Ready? Let's go.&lt;/p&gt;&lt;p style="text-align: left;"&gt;The actual fruit of this "study" boils down to this snippet.&lt;/p&gt;&lt;blockquote&gt;&lt;p style="text-align: left;"&gt;Developers rarely mentioned β€œtime saved” as the core benefit of working in this new way with agents. They were all about increasing ambition.&lt;/p&gt;&lt;/blockquote&gt;&lt;p style="text-align: left;"&gt;Let that sink in. For the last several years the main supposed advantage of AI tools has been the fact that they save massive amounts of developer time. This has lead to the "fire all your developers and replace them with AI bots" trend sweeping the nation. Now even this AI advertisement of a "study" can not find any such advantages and starts backpedaling into something completely different. Just like we have always been at war with Eastasia, AI has never been about "productivity". No. No. It is all about "increased ambition", whatever that is. The post then carries on with this even more baffling statement.&lt;/p&gt;&lt;blockquote&gt;&lt;p style="text-align: left;"&gt;When you move from thinking about reducing effort to expanding scope, only the most advanced agentic capabilities will do.&lt;/p&gt;&lt;/blockquote&gt;&lt;p style="text-align: left;"&gt;Really? Only the most advanced agentics you say? That is a bold statement to make given that the leading reason for software project failure is scope creep. This is the one area where human beings have decades long track record for beating any artificial system. Even if machines were able to do it better, "Make your project failures more probable! Faster! Spectacularer!" is a tough rallying cry to sell.&amp;nbsp;&lt;/p&gt;&lt;p style="text-align: left;"&gt;&amp;nbsp;To conclude, the actual findings of this "study" seem to be that:&lt;/p&gt;&lt;p style="text-align: left;"&gt;&lt;/p&gt;&lt;ol style="text-align: left;"&gt;&lt;li&gt;AI does not improve developer productivity or skills&lt;/li&gt;&lt;li&gt;AI does increase developer ambition&lt;/li&gt;&lt;/ol&gt;This is strictly worse than the current state of affairs.</description><author>Jussi Pakkanen</author><dc:creator>Jussi Pakkanen</dc:creator><pubDate>Wed, 06 Aug 2025 02:41:00 GMT</pubDate><guid isPermaLink="true">https://nibblestew.blogspot.com/2025/08/lets-properly-analyze-ai-article-for.html</guid></item><item><title>Georges Basile Stavracas Neto: GUADEC 2025</title><link>https://feaneron.com/2025/08/05/guadec-2025/</link><description>&lt;p&gt;I&amp;#8217;m back from GUADEC 2025. I&amp;#8217;m still super tired, but I wanted to write down my thoughts before they vanish into the eternal void.&lt;/p&gt;
  2507.  
  2508.  
  2509.  
  2510. &lt;p&gt;First let me start with a massive thank you for everyone that helped organize the event. It looked extremely well rounded, the kind of well rounded that can only be explained by &lt;em&gt;a lot&lt;/em&gt; of work from the organizers. Thank you all!&lt;/p&gt;
  2511.  
  2512.  
  2513.  
  2514. &lt;div class="wp-block-spacer"&gt;&lt;/div&gt;
  2515.  
  2516.  
  2517.  
  2518. &lt;h2 class="wp-block-heading"&gt;Preparations&lt;/h2&gt;
  2519.  
  2520.  
  2521.  
  2522. &lt;p&gt;For this GUADEC I did something special: little calendars!&lt;/p&gt;
  2523.  
  2524.  
  2525.  
  2526. &lt;figure class="wp-block-image alignwide size-full is-style-rounded is-style-rounded--1"&gt;&lt;img alt="3D printed calendars on the table" class="wp-image-11213" src="https://feaneron.com/wp-content/uploads/2025/08/DSC00509Editado.jpg" /&gt;&lt;/figure&gt;
  2527.  
  2528.  
  2529.  
  2530. &lt;p&gt;These were 3D printed from a custom model I&amp;#8217;ve made, based on the app icon of GNOME Calendar. I brought a small batch to GUADEC, and to my surprise &amp;#8211; and joy &amp;#8211; they vanished in just a couple of hours in the first day! It was very cool to see the calendars around after that:&lt;/p&gt;
  2531.  
  2532.  
  2533.  
  2534. &lt;figure class="wp-block-gallery alignwide has-nested-images columns-2 is-cropped is-style-rectangular wp-block-gallery-2 is-layout-flex wp-block-gallery-is-layout-flex"&gt;
  2535. &lt;figure class="wp-block-image size-large has-custom-border"&gt;&lt;img alt="A hand holding a 3D printed calendar" class="wp-image-11211" height="683" src="https://feaneron.com/wp-content/uploads/2025/08/DSC00510-1024x683.jpg" style="border-radius: 12px;" width="1024" /&gt;&lt;/figure&gt;
  2536.  
  2537.  
  2538.  
  2539. &lt;figure class="wp-block-image size-large has-custom-border"&gt;&lt;img alt="Someone using a smartphone, with a 3D printed calendar visible" class="wp-image-11216" height="1920" src="https://feaneron.com/wp-content/uploads/2025/08/DSC00545-edited-scaled.jpg" style="border-radius: 12px;" width="2560" /&gt;&lt;/figure&gt;
  2540.  
  2541.  
  2542.  
  2543. &lt;figure class="wp-block-image size-large has-custom-border"&gt;&lt;img alt="Someone walking with a backpack with a 3D printed calendar" class="wp-image-11215" src="https://feaneron.com/wp-content/uploads/2025/08/DSC00632.jpg" style="border-radius: 12px;" /&gt;&lt;/figure&gt;
  2544.  
  2545.  
  2546.  
  2547. &lt;figure class="wp-block-image size-large has-custom-border"&gt;&lt;img alt="A backpack with a 3D printed calendar hanging out" class="wp-image-11214" src="https://feaneron.com/wp-content/uploads/2025/08/DSC00407.jpg" style="border-radius: 12px;" /&gt;&lt;/figure&gt;
  2548. &lt;/figure&gt;
  2549.  
  2550.  
  2551.  
  2552. &lt;h2 class="wp-block-heading"&gt;Talks&lt;/h2&gt;
  2553.  
  2554.  
  2555.  
  2556. &lt;p&gt;This year I gave two talks:&lt;/p&gt;
  2557.  
  2558.  
  2559.  
  2560. &lt;ul class="wp-block-list"&gt;
  2561. &lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=4BsL0VyCoWs&amp;amp;t=1949s"&gt;GNOME Live Streaming &amp;amp; Outreach&lt;/a&gt;&lt;/li&gt;
  2562.  
  2563.  
  2564.  
  2565. &lt;li&gt;&lt;a href="https://www.youtube.com/live/18Ir6RXkIeA?si=MWu34g4mBlt46bcw&amp;amp;t=10805"&gt;State of Portals&lt;/a&gt;&lt;/li&gt;
  2566. &lt;/ul&gt;
  2567.  
  2568.  
  2569.  
  2570. &lt;p&gt;The first one was rather difficult. On the one hand, streaming GNOME development and interacting with people online for more than 6 years has given me many anecdotal insights about the social dynamics of free software. On the other hand, it was very difficult to materialize these insights and summarize them in form of a talk.&lt;/p&gt;
  2571.  
  2572.  
  2573.  
  2574. &lt;p&gt;I&amp;#8217;ve received good feedback about this talk, but for some reason I still left it feeling like it missed &lt;em&gt;something&lt;/em&gt;. I don&amp;#8217;t know what, exactly. But if anyone felt energized to try some streaming, goal accomplished I guess? &lt;img alt="πŸ™‚" class="wp-smiley" src="https://s.w.org/images/core/emoji/16.0.1/72x72/1f642.png" style="height: 1em;" /&gt;&lt;/p&gt;
  2575.  
  2576.  
  2577.  
  2578. &lt;p&gt;The second talk was just a regular update on the XDG Desktop Portal project. It was made with the sole intention of preparing territory for the Flatpak &amp;amp; Portals BoF that occurred later. Not much to say about it, it was a technical talk, with some good questions and discussions after that.&lt;/p&gt;
  2579.  
  2580.  
  2581.  
  2582. &lt;p&gt;As for the talks that I&amp;#8217;ve watched, to me there is one big highlight for this GUADEC: &lt;a href="https://www.youtube.com/watch?v=18Ir6RXkIeA&amp;amp;t=3378s"&gt;Emmanuelle&amp;#8217;s &amp;#8220;Getting Things Done in GNOME&amp;#8221;&lt;/a&gt;.&lt;/p&gt;
  2583.  
  2584.  
  2585.  
  2586. &lt;figure class="wp-block-image alignwide size-large is-style-rounded is-style-rounded--3"&gt;&lt;img alt="Emmanuele on stage with &amp;quot;How do things happen in GNOME?&amp;quot; on the projector" class="wp-image-11219" height="769" src="https://feaneron.com/wp-content/uploads/2025/08/DSC00542-1024x769.jpg" width="1024" /&gt;&lt;/figure&gt;
  2587.  
  2588.  
  2589.  
  2590. &lt;p&gt;Emmanuele published the contents of this talk &lt;a href="https://www.bassi.io/articles/2025/08/03/governance-in-gnome/"&gt;in article form&lt;/a&gt; recently.&lt;/p&gt;
  2591.  
  2592.  
  2593.  
  2594. &lt;p&gt;Sometimes, when we&amp;#8217;re in the inflection point towards something, the right person with the right sensitivities can say the right things. I think that&amp;#8217;s what Emmanuele did here. I think myself and others have already been feeling that the &amp;#8220;maintainer&amp;#8221;, in the traditional sense of the word, wasn&amp;#8217;t a fitting description of how things have been working lately. Emmanuele gifted us with new vocabulary for that: &amp;#8220;special interest group&amp;#8221;. By the end of GUADEC, we were comfortably using this new descriptor.&lt;/p&gt;
  2595.  
  2596.  
  2597.  
  2598. &lt;p&gt;&lt;/p&gt;
  2599.  
  2600.  
  2601.  
  2602. &lt;div class="wp-block-spacer"&gt;&lt;/div&gt;
  2603.  
  2604.  
  2605.  
  2606. &lt;h2 class="wp-block-heading"&gt;Photography&lt;/h2&gt;
  2607.  
  2608.  
  2609.  
  2610. &lt;p&gt;It&amp;#8217;s not exactly a secret that I started dipping my toes towards a long admired hobby: photography. This GUADEC was the first time I traveled with a camera and a lens, and actually attempted to document the highlights and impressions.&lt;/p&gt;
  2611.  
  2612.  
  2613.  
  2614. &lt;p&gt;I&amp;#8217;m not going to dump all of it here, but I found Brescia absolutely &lt;em&gt;fascinating&lt;/em&gt; and fell in love with the colors and urban vision there. It&amp;#8217;s not the most walkable or car-free city I&amp;#8217;ve ever been, but there are certain aspects to it that caught my attention!&lt;/p&gt;
  2615.  
  2616.  
  2617.  
  2618. &lt;p&gt;The buildings had a lovely color palette, sometimes bright, sometimes pastel:&lt;/p&gt;
  2619.  
  2620.  
  2621.  
  2622. &lt;div class="wp-block-jetpack-tiled-gallery alignwide is-style-rectangular"&gt;&lt;div class="has-rounded-corners-12"&gt;&lt;div class="tiled-gallery__gallery"&gt;&lt;div class="tiled-gallery__row"&gt;&lt;div class="tiled-gallery__col"&gt;&lt;figure class="tiled-gallery__item"&gt;&lt;a href="https://feaneron.com/wp-content/uploads/2025/08/DSC00457-1024x683.jpg"&gt;&lt;img alt="" src="https://i0.wp.com/feaneron.com/wp-content/uploads/2025/08/DSC00457-1024x683.jpg?ssl=1" /&gt;&lt;/a&gt;&lt;/figure&gt;&lt;/div&gt;&lt;div class="tiled-gallery__col"&gt;&lt;figure class="tiled-gallery__item"&gt;&lt;a href="https://feaneron.com/wp-content/uploads/2025/08/DSC00466-1024x683.jpg"&gt;&lt;img alt="" src="https://i0.wp.com/feaneron.com/wp-content/uploads/2025/08/DSC00466-1024x683.jpg?ssl=1" /&gt;&lt;/a&gt;&lt;/figure&gt;&lt;/div&gt;&lt;div class="tiled-gallery__col"&gt;&lt;figure class="tiled-gallery__item"&gt;&lt;a href="https://feaneron.com/wp-content/uploads/2025/08/DSC00917-1-768x1024.jpg"&gt;&lt;img alt="" src="https://i0.wp.com/feaneron.com/wp-content/uploads/2025/08/DSC00917-1-768x1024.jpg?ssl=1" /&gt;&lt;/a&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="tiled-gallery__row"&gt;&lt;div class="tiled-gallery__col"&gt;&lt;figure class="tiled-gallery__item"&gt;&lt;a href="https://feaneron.com/wp-content/uploads/2025/08/DSC00527-1024x768.jpg"&gt;&lt;img alt="" src="https://i0.wp.com/feaneron.com/wp-content/uploads/2025/08/DSC00527-1024x768.jpg?ssl=1" /&gt;&lt;/a&gt;&lt;/figure&gt;&lt;/div&gt;&lt;div class="tiled-gallery__col"&gt;&lt;figure class="tiled-gallery__item"&gt;&lt;a href="https://feaneron.com/wp-content/uploads/2025/08/DSC01100-768x1024.jpg"&gt;&lt;img alt="" src="https://i0.wp.com/feaneron.com/wp-content/uploads/2025/08/DSC01100-768x1024.jpg?ssl=1" /&gt;&lt;/a&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
  2623.  
  2624.  
  2625.  
  2626. &lt;div class="wp-block-spacer"&gt;&lt;/div&gt;
  2627.  
  2628.  
  2629.  
  2630. &lt;p&gt;Some of the textures of the material of walls were intriguing:&lt;/p&gt;
  2631.  
  2632.  
  2633.  
  2634. &lt;div class="wp-block-jetpack-tiled-gallery alignwide is-style-rectangular"&gt;&lt;div class="has-rounded-corners-12"&gt;&lt;div class="tiled-gallery__gallery"&gt;&lt;div class="tiled-gallery__row"&gt;&lt;div class="tiled-gallery__col"&gt;&lt;figure class="tiled-gallery__item"&gt;&lt;a href="https://feaneron.com/wp-content/uploads/2025/08/DSC00591-768x1024.jpg"&gt;&lt;img alt="" src="https://i0.wp.com/feaneron.com/wp-content/uploads/2025/08/DSC00591-768x1024.jpg?ssl=1" /&gt;&lt;/a&gt;&lt;/figure&gt;&lt;/div&gt;&lt;div class="tiled-gallery__col"&gt;&lt;figure class="tiled-gallery__item"&gt;&lt;a href="https://feaneron.com/wp-content/uploads/2025/08/DSC01088-768x1024.jpg"&gt;&lt;img alt="" src="https://i0.wp.com/feaneron.com/wp-content/uploads/2025/08/DSC01088-768x1024.jpg?ssl=1" /&gt;&lt;/a&gt;&lt;/figure&gt;&lt;/div&gt;&lt;div class="tiled-gallery__col"&gt;&lt;figure class="tiled-gallery__item"&gt;&lt;a href="https://feaneron.com/wp-content/uploads/2025/08/DSC01087-1024x768.jpg"&gt;&lt;img alt="" src="https://i0.wp.com/feaneron.com/wp-content/uploads/2025/08/DSC01087-1024x768.jpg?ssl=1" /&gt;&lt;/a&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="tiled-gallery__row"&gt;&lt;div class="tiled-gallery__col"&gt;&lt;figure class="tiled-gallery__item"&gt;&lt;a href="https://feaneron.com/wp-content/uploads/2025/08/DSC00548.jpg"&gt;&lt;img alt="" src="https://i0.wp.com/feaneron.com/wp-content/uploads/2025/08/DSC00548.jpg?ssl=1" /&gt;&lt;/a&gt;&lt;/figure&gt;&lt;/div&gt;&lt;div class="tiled-gallery__col"&gt;&lt;figure class="tiled-gallery__item"&gt;&lt;a href="https://feaneron.com/wp-content/uploads/2025/08/DSC01033-1024x768.jpg"&gt;&lt;img alt="" src="https://i0.wp.com/feaneron.com/wp-content/uploads/2025/08/DSC01033-1024x768.jpg?ssl=1" /&gt;&lt;/a&gt;&lt;/figure&gt;&lt;/div&gt;&lt;div class="tiled-gallery__col"&gt;&lt;figure class="tiled-gallery__item"&gt;&lt;a href="https://feaneron.com/wp-content/uploads/2025/08/DSC00855-768x1024.jpg"&gt;&lt;img alt="" src="https://i0.wp.com/feaneron.com/wp-content/uploads/2025/08/DSC00855-768x1024.jpg?ssl=1" /&gt;&lt;/a&gt;&lt;/figure&gt;&lt;/div&gt;&lt;div class="tiled-gallery__col"&gt;&lt;figure class="tiled-gallery__item"&gt;&lt;a href="https://feaneron.com/wp-content/uploads/2025/08/DSC00590-768x1024.jpg"&gt;&lt;img alt="" src="https://i0.wp.com/feaneron.com/wp-content/uploads/2025/08/DSC00590-768x1024.jpg?ssl=1" /&gt;&lt;/a&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
  2635.  
  2636.  
  2637.  
  2638. &lt;div class="wp-block-spacer"&gt;&lt;/div&gt;
  2639.  
  2640.  
  2641.  
  2642. &lt;p&gt;I fell in love with how Brescia lights itself:&lt;/p&gt;
  2643.  
  2644.  
  2645.  
  2646. &lt;div class="wp-block-jetpack-tiled-gallery alignwide is-style-rectangular"&gt;&lt;div class="has-rounded-corners-12"&gt;&lt;div class="tiled-gallery__gallery"&gt;&lt;div class="tiled-gallery__row"&gt;&lt;div class="tiled-gallery__col"&gt;&lt;figure class="tiled-gallery__item"&gt;&lt;img alt="" src="https://i0.wp.com/feaneron.com/wp-content/uploads/2025/08/image-16-1024x768.png?ssl=1" /&gt;&lt;/figure&gt;&lt;/div&gt;&lt;div class="tiled-gallery__col"&gt;&lt;figure class="tiled-gallery__item"&gt;&lt;img alt="" src="https://i0.wp.com/feaneron.com/wp-content/uploads/2025/08/image-6-1024x768.png?ssl=1" /&gt;&lt;/figure&gt;&lt;/div&gt;&lt;div class="tiled-gallery__col"&gt;&lt;figure class="tiled-gallery__item"&gt;&lt;img alt="" src="https://i0.wp.com/feaneron.com/wp-content/uploads/2025/08/image-15-768x1024.png?ssl=1" /&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="tiled-gallery__row"&gt;&lt;div class="tiled-gallery__col"&gt;&lt;figure class="tiled-gallery__item"&gt;&lt;img alt="" src="https://i0.wp.com/feaneron.com/wp-content/uploads/2025/08/image-7-1024x768.png?ssl=1" /&gt;&lt;/figure&gt;&lt;/div&gt;&lt;div class="tiled-gallery__col"&gt;&lt;figure class="tiled-gallery__item"&gt;&lt;img alt="" src="https://i0.wp.com/feaneron.com/wp-content/uploads/2025/08/image-10-1024x768.png?ssl=1" /&gt;&lt;/figure&gt;&lt;figure class="tiled-gallery__item"&gt;&lt;img alt="" src="https://i0.wp.com/feaneron.com/wp-content/uploads/2025/08/image-14-1024x768.png?ssl=1" /&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="tiled-gallery__row"&gt;&lt;div class="tiled-gallery__col"&gt;&lt;figure class="tiled-gallery__item"&gt;&lt;img alt="" src="https://i0.wp.com/feaneron.com/wp-content/uploads/2025/08/image-17-1024x768.png?ssl=1" /&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
  2647.  
  2648.  
  2649.  
  2650. &lt;div class="wp-block-spacer"&gt;&lt;/div&gt;
  2651.  
  2652.  
  2653.  
  2654. &lt;p&gt;I was fascinated by how many Vespas (and similar) could be found around the city, and decided to photograph them all! Some of the prettiest ones:&lt;/p&gt;
  2655.  
  2656.  
  2657.  
  2658. &lt;div class="wp-block-jetpack-tiled-gallery alignwide is-style-rectangular"&gt;&lt;div class="has-rounded-corners-12"&gt;&lt;div class="tiled-gallery__gallery"&gt;&lt;div class="tiled-gallery__row"&gt;&lt;div class="tiled-gallery__col"&gt;&lt;figure class="tiled-gallery__item"&gt;&lt;img alt="" src="https://i0.wp.com/feaneron.com/wp-content/uploads/2025/08/image-21-768x1024.png?ssl=1" /&gt;&lt;/figure&gt;&lt;/div&gt;&lt;div class="tiled-gallery__col"&gt;&lt;figure class="tiled-gallery__item"&gt;&lt;img alt="" src="https://i0.wp.com/feaneron.com/wp-content/uploads/2025/08/image-19-1024x768.png?ssl=1" /&gt;&lt;/figure&gt;&lt;figure class="tiled-gallery__item"&gt;&lt;img alt="" src="https://i0.wp.com/feaneron.com/wp-content/uploads/2025/08/image-20-1024x682.png?ssl=1" /&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
  2659.  
  2660.  
  2661.  
  2662. &lt;div class="wp-block-spacer"&gt;&lt;/div&gt;
  2663.  
  2664.  
  2665.  
  2666. &lt;p&gt;The prize for best modeling goes to Sri!&lt;/p&gt;
  2667.  
  2668.  
  2669.  
  2670. &lt;figure class="wp-block-image alignwide size-large is-style-rounded is-style-rounded--4"&gt;&lt;img alt="" class="wp-image-11249" height="768" src="https://feaneron.com/wp-content/uploads/2025/08/DSC00661-1024x768.jpg" width="1024" /&gt;&lt;/figure&gt;
  2671.  
  2672.  
  2673.  
  2674. &lt;div class="wp-block-spacer"&gt;&lt;/div&gt;
  2675.  
  2676.  
  2677.  
  2678. &lt;h2 class="wp-block-heading"&gt;Conclusion&lt;/h2&gt;
  2679.  
  2680.  
  2681.  
  2682. &lt;p&gt;Massive thanks to everyone that helped organize GUADEC this year. It was a fantastic event. It was great to see old friends, and meet new people in there. There were many newcomers attending this GUADEC!&lt;/p&gt;
  2683.  
  2684.  
  2685.  
  2686. &lt;p&gt;And here&amp;#8217;s the GUADEC dinner group photo!&lt;/p&gt;
  2687.  
  2688.  
  2689.  
  2690. &lt;div class="wp-block-spacer"&gt;&lt;/div&gt;
  2691.  
  2692.  
  2693.  
  2694. &lt;figure class="wp-block-image alignfull size-full"&gt;&lt;img alt="Group photo during the GUADEC dinner party" class="wp-image-11250" src="https://feaneron.com/wp-content/uploads/2025/08/GUADEC-2025-dinner-team-photo.png" /&gt;&lt;/figure&gt;</description><author>Georges Basile Stavracas Neto</author><dc:creator>Georges Basile Stavracas Neto</dc:creator><pubDate>Tue, 05 Aug 2025 18:46:49 GMT</pubDate><guid isPermaLink="true">https://feaneron.com/2025/08/05/guadec-2025/</guid></item><item><title>Thibault Martin: TIL that You can spot base64 encoded JSON, certificates, and private keys</title><link>https://ergaster.org/til/base64-encoded-json/</link><description>&lt;p&gt;I was working on my homelab and examined a file that was supposed to contain encrypted content that I could safely commit on a Github repository. The file looked like this&lt;/p&gt;
  2695. &lt;pre&gt;&lt;code&gt;{
  2696.  "serial": 13,
  2697.  "lineage": "24d431ee-3da9-4407-b649-b0d2c0ca2d67",
  2698.  "meta": {
  2699.    "key_provider.pbkdf2.password_key": "eyJzYWx0IjoianpHUlpMVkFOZUZKcEpSeGo4UlhnNDhGZk9vQisrR0YvSG9ubTZzSUY5WT0iLCJpdGVyYXRpb25zIjo2MDAwMDAsImhhc2hfZnVuY3Rpb24iOiJzaGE1MTIiLCJrZXlfbGVuZ3RoIjozMn0="
  2700.  },
  2701.  "encrypted_data": "ONXZsJhz37eJA[...]",
  2702.  "encryption_version": "v0"
  2703. }
  2704. &lt;/code&gt;&lt;/pre&gt;
  2705. &lt;p&gt;Hm, key provider? Password key? In an encrypted file? That doesn't sound right. The problem is that this file is generated by taking a password, deriving a key from it, and encrypting the content with that key. I don't know what the derived key could look like, but it could be that long indecipherable string.&lt;/p&gt;
  2706. &lt;p&gt;I asked a colleague to have a look and he said "Oh that? It looks like a base64 encoded JSON. Give it a go to see what's inside."&lt;/p&gt;
  2707. &lt;p&gt;I was incredulous but gave it a go, and it worked!!&lt;/p&gt;
  2708. &lt;pre&gt;&lt;code&gt;$ echo "eyJzYW[...]" | base64 -d
  2709. {"salt":"jzGRZLVANeFJpJRxj8RXg48FfOoB++GF/Honm6sIF9Y=","iterations":600000,"hash_function":"sha512","key_length":32}
  2710. &lt;/code&gt;&lt;/pre&gt;
  2711. &lt;p&gt;I couldn't believe my colleague had decoded the base64 string on the fly, so I asked. "What gave it away? Was it the trailing equal signs at the end for padding? But how did you know it was base64 encoded &lt;em&gt;JSON&lt;/em&gt; and not just a base64 string?"&lt;/p&gt;
  2712. &lt;p&gt;He replied,&lt;/p&gt;
  2713. &lt;blockquote&gt;
  2714. &lt;p&gt;Whenever you seeΒ &lt;code&gt;ey&lt;/code&gt;, that'sΒ &lt;code&gt;{"&lt;/code&gt;Β and then if it's followed by a letter, you'll getΒ &lt;code&gt;J&lt;/code&gt;Β followed by a letter.&lt;/p&gt;
  2715. &lt;/blockquote&gt;
  2716. &lt;p&gt;I did a few tests in my terminal, and he was right! You can spot base64 json with your naked eye, and you don't need to decode it on the fly!&lt;/p&gt;
  2717. &lt;pre&gt;&lt;code&gt;$ echo "{" | base64
  2718. ewo=
  2719. $ echo "{\"" | base64
  2720. eyIK
  2721. $ echo "{\"s" | base64
  2722. eyJzCg==
  2723. $ echo "{\"a" | base64
  2724. eyJhCg==
  2725. $ echo "{\"word\"" | base64
  2726. eyJ3b3JkIgo=
  2727. &lt;/code&gt;&lt;/pre&gt;
  2728. &lt;p&gt;But there's even better! As &lt;a href="https://toot.now/@tyzbit"&gt;tyzbit&lt;/a&gt; reported on the fediverse, you can even spot base64 encoded certificates and private keys! They all start with &lt;code&gt;LS&lt;/code&gt;, which reminds of the LS in "TLS certificate."&lt;/p&gt;
  2729. &lt;pre&gt;&lt;code&gt;$ echo -en "-----BEGIN CERTIFICATE-----" | base64
  2730. LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0t
  2731. &lt;/code&gt;&lt;/pre&gt;
  2732. &lt;blockquote&gt;
  2733. &lt;p&gt;[!warning] Errata&lt;/p&gt;
  2734. &lt;p&gt;As pointed out by &lt;a href="https://news.ycombinator.com/item?id=44803260"&gt;gnabgib&lt;/a&gt; and &lt;a href="https://news.ycombinator.com/item?id=44803259"&gt;athorax&lt;/a&gt; on Hacker News, this actually detects the leading dashes of the PEM format, commonly used for certificates, and a YAML file that starts with &lt;code&gt;---&lt;/code&gt; will yield the same result&lt;/p&gt;
  2735. &lt;pre&gt;&lt;code&gt;$ echo "---\n" | base64
  2736. LS0tXG4K
  2737. &lt;/code&gt;&lt;/pre&gt;
  2738. &lt;p&gt;This is not a silver bullet!&lt;/p&gt;
  2739. &lt;/blockquote&gt;
  2740. &lt;p&gt;&lt;em&gt;Thanks Davide and Denis for showing me this simple but pretty useful trick, and thanks tyzbit for completing it with certs and private keys!&lt;/em&gt;&lt;/p&gt;</description><author>Thibault Martin</author><dc:creator>Thibault Martin</dc:creator><pubDate>Tue, 05 Aug 2025 13:00:00 GMT</pubDate><guid isPermaLink="true">https://ergaster.org/til/base64-encoded-json/</guid></item><item><title>Matthew Garrett: Cordoomceps - replacing an Amiga's brain with Doom</title><link>https://mjg59.dreamwidth.org/73001.html</link><description>There's a lovely device called a &lt;a href="https://github.com/captain-amygdala/pistorm"&gt;pistorm&lt;/a&gt;, an adapter board that glues a Raspberry Pi GPIO bus to a Motorola 68000 bus. The intended use case is that you plug it into a 68000 device and then run an emulator that reads instructions from hardware (ROM or RAM) and emulates them. You're still limited by the ~7MHz bus that the hardware is running at, but you can run the instructions as fast as you want.&lt;br /&gt;&lt;br /&gt;These days you're supposed to run a custom built OS on the Pi that just does 68000 emulation, but initially it ran Linux on the Pi and a userland 68000 emulator process. And, well, that got me thinking. The emulator takes 68000 instructions, emulates them, and then talks to the hardware to implement the effects of those instructions. What if we, well, just don't? What if we just run all of our code in Linux on an ARM core and then talk to the Amiga hardware?&lt;br /&gt;&lt;br /&gt;We're going to ignore x86 here, because it's weird - but most hardware that wants software to be able to communicate with it maps itself into the same address space that RAM is in. You can write to a byte of RAM, or you can write to a piece of hardware that's effectively pretending to be RAM[1]. The Amiga wasn't unusual in this respect in the 80s, and to talk to the graphics hardware you speak to a special address range that gets sent to that hardware instead of to RAM. The CPU knows nothing about this. It just indicates it wants to write to an address, and then sends the data.&lt;br /&gt;&lt;br /&gt;So, if we are the CPU, we can just indicate that we want to write to an address, and provide the data. And those addresses can correspond to the hardware. So, we can write to the RAM that belongs to the Amiga, and we can write to the hardware that isn't RAM but pretends to be. And that means we can run whatever we want on the Pi and then access Amiga hardware.&lt;br /&gt;&lt;br /&gt;And, obviously, the thing we want to run is Doom, because that's what everyone runs in fucked up hardware situations.&lt;br /&gt;&lt;br /&gt;Doom was Amiga kryptonite. Its entire graphical model was based on memory directly representing the contents of your display, and being able to modify that by just moving pixels around. This worked because at the time VGA displays supported having a memory layout where each pixel on your screen was represented by a byte in memory containing an 8 bit value that corresponded to a lookup table containing the RGB value for that pixel.&lt;br /&gt;&lt;br /&gt;The Amiga was, well, not good at this. Back in the 80s, when the Amiga hardware was developed, memory was expensive. Dedicating that much RAM to the video hardware was unthinkable - the Amiga 1000 initially shipped with only 256K of RAM, and you could fill all of that with a sufficiently colourful picture. So instead of having the idea of each pixel being associated with a specific area of memory, the Amiga used bitmaps. A bitmap is an area of memory that represents the screen, but only represents one bit of the colour depth. If you have a black and white display, you only need one bitmap. If you want to display four colours, you need two. More colours, more bitmaps. And each bitmap is stored in an independent area of RAM. You never use more memory than you need to display the number of colours you want to.&lt;br /&gt;&lt;br /&gt;But that means that each bitplane contains packed information - every byte of data in a bitplane contains the bit value for 8 different pixels, because each bitplane contains one bit of information per pixel. To update one pixel on screen, you need to read from every bitmap, update one bit, and write it back, and that's a lot of additional memory accesses. Doom, but on the Amiga, was slow not just because the CPU was slow, but because there was a lot of manipulation of data to turn it into the format the Amiga wanted and then push that over a fairly slow memory bus to have it displayed.&lt;br /&gt;&lt;br /&gt;The &lt;a href="https://en.wikipedia.org/wiki/CDTV"&gt;CDTV&lt;/a&gt; was an aesthetically pleasing piece of hardware that absolutely sucked. It was an Amiga 500 in a hi-fi box with a caddy-loading CD drive, and it ran software that was just awful. There's no path to remediation here. No compelling apps were ever released. It's a terrible device. I love it. I bought one in 1996 because a local computer store had one and I pointed out that the company selling it had gone bankrupt some years earlier and literally nobody in my farming town was ever going to have any interest in buying a CD player that made a whirring noise when you turned it on because it had a fan and eventually they just sold it to me for not much money, and ever since then I wanted to have a CD player that ran Linux and well spoiler 30 years later I'm nearly there. That CDTV is going to be our test subject. We're going to try to get Doom running on it without executing any 68000 instructions.&lt;br /&gt;&lt;br /&gt;We're facing two main problems here. The first is that all Amigas have a firmware ROM called Kickstart that runs at powerup. No matter how little you care about using any OS functionality, you can't start running your code until Kickstart has run. This means even documentation describing bare metal Amiga programming assumes that the hardware is already in the state that Kickstart left it in. This will become important later. The second is that we're going to need to actually write the code to use the Amiga hardware.&lt;br /&gt;&lt;br /&gt;First, &lt;a href="https://www.bonequest.com/150"&gt;let's talk about Amiga graphics&lt;/a&gt;. We've already covered bitmaps, but for anyone used to modern hardware that's not the weirdest thing about what we're dealing with here. The CDTV's chipset supports a maximum of 64 colours in a mode called "Extra Half-Brite", or EHB, where you have 32 colours arbitrarily chosen from a palette and then 32 more colours that are identical but with half the intensity. For 64 colours we need 6 bitplanes, each of which can be located arbitrarily in the region of RAM accessible to the chipset ("chip RAM", distinguished from "fast ram" that's only accessible to the CPU). We tell the chipset where our bitplanes are and it displays them. Or, well, it does for a frame - after that the registers that pointed at our bitplanes no longer do, because when the hardware was DMAing through the bitplanes to display them it was incrementing those registers to point at the next address to DMA from. Which means that every frame we need to set those registers back.&lt;br /&gt;&lt;br /&gt;Making sure you have code that's called every frame just to make your graphics work sounds intensely irritating, so Commodore gave us a way to avoid doing that. The chipset includes a coprocessor called "copper". Copper doesn't have a large set of features - in fact, it only has three. The first is that it can program chipset registers. The second is that it can wait for a specific point in screen scanout. The third (which we don't care about here) is that it can optionally skip an instruction if a certain point in screen scanout has already been reached. We can write a program (a "copper list") for the copper that tells it to program the chipset registers with the locations of our bitplanes and then wait until the end of the frame, at which point it will repeat the process. Now our bitplane pointers are always valid at the start of a frame.&lt;br /&gt;&lt;br /&gt;Ok! We know how to display stuff. Now we just need to deal with not having 256 colours, and the whole "Doom expects pixels" thing. For the first of these, I stole code from &lt;a href="https://doomwiki.org/wiki/ADoom"&gt;ADoom&lt;/a&gt;, the only Amiga doom port I could easily find source for. This looks at the 256 colour palette loaded by Doom and calculates the closest approximation it can within the constraints of EHB. ADoom also includes a bunch of CPU-specific assembly optimisation for converting the "chunky" Doom graphic buffer into the "planar" Amiga bitplanes, none of which I used because (a) it's all for 68000 series CPUs and we're running on ARM, and (b) I have a quad core CPU running at 1.4GHz and I'm going to be pushing all the graphics over a 7.14MHz bus, the graphics mode conversion is &lt;em&gt;not&lt;/em&gt; going to be the bottleneck here. Instead I just wrote a series of nested for loops that iterate through each pixel and update each bitplane and called it a day. The set of bitplanes I'm operating on here is allocated on the Linux side so I can read and write to them without being restricted by the speed of the Amiga bus (remember, each byte in each bitplane is going to be updated 8 times per frame, because it holds bits associated with 8 pixels), and then copied over to the Amiga's RAM once the frame is complete.&lt;br /&gt;&lt;br /&gt;And, kind of astonishingly, this works! Once I'd figured out where I was going wrong with RGB ordering and which order the bitplanes go in, I had a recognisable copy of Doom running. Unfortunately there were weird graphical glitches - sometimes blocks would be entirely the wrong colour. It took me a while to figure out what was going on and then I felt stupid. Recording the screen and watching in slow motion revealed that the glitches often showed parts of two frames displaying at once. The Amiga hardware is taking responsibility for scanning out the frames, and the code on the Linux side isn't synchronised with it at all. That means I could update the bitplanes while the Amiga was scanning them out, resulting in a mashup of planes from two different Doom frames being used as one Amiga frame. One approach to avoid this would be to tie the Doom event loop to the Amiga, blocking my writes until the end of scanout. The other is to use double-buffering - have two sets of bitplanes, one being displayed and the other being written to. This consumes more RAM but since I'm not using the Amiga RAM for anything else that's not a problem. With this approach I have two copper lists, one for each set of bitplanes, and switch between them on each frame. This improved things a lot but not entirely, and there's still glitches when the palette is being updated (because there's only one set of colour registers), something Doom does rather a lot, so I'm going to need to implement proper synchronisation.&lt;br /&gt;&lt;br /&gt;Except. This was only working if I ran a 68K emulator first in order to run Kickstart. If I tried accessing the hardware without doing that, things were in a weird state. I could update the colour registers, but accessing RAM didn't work - I could read stuff out, but anything I wrote vanished. Some more digging cleared that up. When you turn on a CPU it needs to start executing code from somewhere. On modern x86 systems it starts from a hardcoded address of 0xFFFFFFF0, which was traditionally a long way any RAM. The 68000 family instead reads its start address from address 0x00000004, which overlaps with where the Amiga chip RAM is. We can't write anything to RAM until we're executing code, and we can't execute code until we tell the CPU where the code is, which seems like a problem. This is solved on the Amiga by powering up in a state where the Kickstart ROM is "overlayed" onto address 0. The CPU reads the start address from the ROM, which causes it to jump into the ROM and start executing code there. Early on, the code tells the hardware to stop overlaying the ROM onto the low addresses, and now the RAM is available. This is poorly documented because it's not something you need to care if you execute Kickstart which every actual Amiga does and I'm only in this position because I've made poor life choices, but ok that explained things. To turn off the overlay you write to a register in one of the Complex Interface Adaptor (CIA) chips, and things start working like you'd expect.&lt;br /&gt;&lt;br /&gt;Except, they don't. Writing to that register did nothing for me. I assumed that there was some other register I needed to write to first, and went to the extent of tracing every register access that occurred when running the emulator and replaying those in my code. Nope, still broken. What I finally discovered is that you need to pulse the reset line on the board before some of the hardware starts working - powering it up doesn't put you in a well defined state, but resetting it does.&lt;br /&gt;&lt;br /&gt;So, I now have a slightly graphically glitchy copy of Doom running without any sound, displaying on an Amiga whose brain has been replaced with a parasitic Linux. Further updates will likely make things even worse. Code is, of course, &lt;a href="https://gitlab.com/mjg59/cordoomceps"&gt;available&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;[1] This is why we had trouble with late era 32 bit systems and 4GB of RAM - a bunch of your hardware wanted to be in the same address space and so you couldn't put RAM there so you ended up with less than 4GB of RAM&lt;br /&gt;&lt;br /&gt;&lt;img alt="comment count unavailable" height="12" src="https://www.dreamwidth.org/tools/commentcount?user=mjg59&amp;amp;ditemid=73001" style="vertical-align: middle;" width="30" /&gt; comments</description><author>Matthew Garrett</author><dc:creator>Matthew Garrett</dc:creator><pubDate>Tue, 05 Aug 2025 00:30:19 GMT</pubDate><guid isPermaLink="true">https://mjg59.dreamwidth.org/73001.html</guid></item><item><title>Victor Ma: It's alive!</title><link>https://victorma.ca/posts/gsoc-6/</link><description>&lt;p&gt;In the last two weeks, I&amp;rsquo;ve been working on my lookahead-based word suggestion algorithm. And it&amp;rsquo;s finally functional! There&amp;rsquo;s still a lot more work to be done, but it&amp;rsquo;s great to see that the &lt;a href="http://localhost:1313/posts/gsoc-5/#design-doc"&gt;original problem&lt;/a&gt; I set out to solve is now solved by my new algorithm.&lt;/p&gt;
  2741. &lt;h2 id="without-my-changes"&gt;Without my changes&lt;/h2&gt;
  2742. &lt;p&gt;Here&amp;rsquo;s what the upstream Crosswords Editor looks like, with a problematic grid:&lt;/p&gt;
  2743. &lt;p&gt;
  2744. &lt;figure&gt;
  2745. &lt;img alt="Broken behaviour" src="https://victorma.ca/posts/gsoc-6/broken.png" /&gt;
  2746. &lt;/figure&gt;
  2747.  
  2748.  
  2749. &lt;/p&gt;
  2750. &lt;p&gt;The editor suggests words like &lt;em&gt;WORD&lt;/em&gt; and &lt;em&gt;WORM&lt;/em&gt;, for the 4-Across slot. But none of the suggestions are valid, because the grid is actually unfillable. This means that there are no possible word suggestions for the grid.&lt;/p&gt;
  2751. &lt;p&gt;The words that the editor suggests do work for 4-Across. But they do not work for 4-Down. They all cause 4-Down to become a nonsensical word.&lt;/p&gt;
  2752. &lt;p&gt;The problem here is that the current word suggestion algorithm only looks at the row and column where the cursor is. So it sees 4-Across and 1-Down&amp;mdash;but it has no idea about 4-Down. If it could see 4-Down, then it would realise that no word that fits in 4-Across also fits in 4-Down&amp;mdash;and it would return an empty word suggestion list.&lt;/p&gt;
  2753. &lt;h2 id="with-my-changes"&gt;With my changes&lt;/h2&gt;
  2754. &lt;p&gt;My algorithm fixes the problem by considering &lt;em&gt;every&lt;/em&gt; intersecting slot of the current slot. In the example grid, the current slot is 4-Across. So, my algorithm looks at 1-Down, 2-Down, 3-Down, and 4-Down. When it reaches 4-Down, it sees that no letter fits in the empty cell. Every possible letter leads to either 4-Across or 4-Down or both slots to contain an invalid word. So, my algorithm correctly returns an empty list of word suggestions.&lt;/p&gt;
  2755. &lt;p&gt;
  2756. &lt;figure&gt;
  2757. &lt;img alt="Fixed behaviour" src="https://victorma.ca/posts/gsoc-6/fixed.png" /&gt;
  2758. &lt;/figure&gt;
  2759.  
  2760.  
  2761. &lt;/p&gt;</description><author>Victor Ma</author><dc:creator>Victor Ma</dc:creator><pubDate>Tue, 05 Aug 2025 00:00:00 GMT</pubDate><guid isPermaLink="true">https://victorma.ca/posts/gsoc-6/</guid></item><item><title>Julian Hofer: Git Forges Made Simple: gh &amp; glab</title><link>https://julianhofer.eu/blog/2025/git-forges/</link><description>&lt;p&gt;When I set the goal for myself to contribute to open source back in 2018,
  2762. I mostly struggled with two technical challenges:&lt;/p&gt;
  2763. &lt;ul&gt;
  2764. &lt;li&gt;Python virtual environments, and&lt;/li&gt;
  2765. &lt;li&gt;Git together with GitHub.&lt;/li&gt;
  2766. &lt;/ul&gt;
  2767. &lt;p&gt;Solving the former is nowadays my &lt;a href="https://prefix.dev/"&gt;job&lt;/a&gt;,
  2768. so let me write up my current workflow for the latter.&lt;/p&gt;
  2769.  
  2770. &lt;p&gt;Most people use Git in combination with modern Git forges like GitHub and GitLab.
  2771. Git doesn’t know anything about these forges, which is why CLI tools exist to close that gap.
  2772. It’s still good to know how to handle things without them, so I will also explain how to do things with only Git.
  2773. For GitHub there’s &lt;a href="https://cli.github.com/"&gt;&lt;code dir="auto"&gt;gh&lt;/code&gt;&lt;/a&gt; and for GitLab there’s &lt;a href="https://docs.gitlab.com/editor_extensions/gitlab_cli/"&gt;&lt;code dir="auto"&gt;glab&lt;/code&gt;&lt;/a&gt;.
  2774. Both of them are Go binaries without any dependencies that work on Linux, macOS and Windows.
  2775. If you don’t like any of the provided installation methods, you can simply download the binary, make it executable and put it in your &lt;code dir="auto"&gt;PATH&lt;/code&gt;.&lt;/p&gt;
  2776. &lt;p&gt;Luckily, they also have mostly the same command line interface.
  2777. First, you have to login with the command that corresponds to your git forge:&lt;/p&gt;
  2778.  
  2779. &lt;p&gt;In the case of &lt;code dir="auto"&gt;gh&lt;/code&gt; this even authenticates Git with GitHub.
  2780. With GitLab, you still have to set up authentication via &lt;a href="https://docs.gitlab.com/user/ssh/"&gt;SSH&lt;/a&gt;.&lt;/p&gt;
  2781. &lt;div&gt;&lt;h2 id="working-solo"&gt;Working Solo&lt;/h2&gt;&lt;a href="https://julianhofer.eu/blog/rss.xml#working-solo"&gt;&lt;span&gt;&lt;svg height="16" viewBox="0 0 24 24" width="16" xmlns="http://www.w3.org/2000/svg"&gt;&lt;path d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z" fill="currentcolor"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span&gt;Section titled β€œWorking Solo”&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
  2782. &lt;p&gt;The simplest way to use Git is to use it like a backup system.
  2783. First, you create a new repository on either &lt;a href="https://docs.github.com/en/repositories/creating-and-managing-repositories/creating-a-new-repository"&gt;Github&lt;/a&gt; or &lt;a href="https://docs.gitlab.com/user/project/"&gt;GitLab&lt;/a&gt;.
  2784. Then you clone the repository:&lt;/p&gt;
  2785. &lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;clone&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;#x3c;REPO&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;button title="Copy to clipboard"&gt;"&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
  2786. &lt;p&gt;From that point on, all you have to do is:&lt;/p&gt;
  2787. &lt;ul&gt;
  2788. &lt;li&gt;do some work&lt;/li&gt;
  2789. &lt;li&gt;commit&lt;/li&gt;
  2790. &lt;li&gt;push&lt;/li&gt;
  2791. &lt;li&gt;repeat&lt;/li&gt;
  2792. &lt;/ul&gt;
  2793. &lt;p&gt;On its own there aren’t a lot of reasons to choose this approach over a file syncing service like &lt;a href="https://nextcloud.com/sign-up/"&gt;Nextcloud&lt;/a&gt;.
  2794. No, the main reason you do this, is because you are either already familiar with the git workflow, or want to get used to it.&lt;/p&gt;
  2795. &lt;div&gt;&lt;h2 id="contributing"&gt;Contributing&lt;/h2&gt;&lt;a href="https://julianhofer.eu/blog/rss.xml#contributing"&gt;&lt;span&gt;&lt;svg height="16" viewBox="0 0 24 24" width="16" xmlns="http://www.w3.org/2000/svg"&gt;&lt;path d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z" fill="currentcolor"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span&gt;Section titled β€œContributing”&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
  2796. &lt;p&gt;Git truly shines as soon as you start collaborating with others.
  2797. On a high level this works like this:&lt;/p&gt;
  2798. &lt;ul&gt;
  2799. &lt;li&gt;You modify some files in a Git repository,&lt;/li&gt;
  2800. &lt;li&gt;you propose your changes via the Git forge,&lt;/li&gt;
  2801. &lt;li&gt;maintainers of the repository review your changes, and&lt;/li&gt;
  2802. &lt;li&gt;as soon as they are happy with your changes, they will integrate your changes into the main branch of the repository.&lt;/li&gt;
  2803. &lt;/ul&gt;
  2804. &lt;div&gt;&lt;h3 id="starting-out-with-a-fresh-branch"&gt;Starting Out With a Fresh Branch&lt;/h3&gt;&lt;a href="https://julianhofer.eu/blog/rss.xml#starting-out-with-a-fresh-branch"&gt;&lt;span&gt;&lt;svg height="16" viewBox="0 0 24 24" width="16" xmlns="http://www.w3.org/2000/svg"&gt;&lt;path d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z" fill="currentcolor"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span&gt;Section titled β€œStarting Out With a Fresh Branch”&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
  2805. &lt;p&gt;Let’s go over the exact commands.&lt;/p&gt;
  2806. &lt;ol&gt;
  2807. &lt;li&gt;
  2808. &lt;p&gt;You will want to start out with the latest upstream changes in the default branch.
  2809. You can find out its name by running the following command:&lt;/p&gt;
  2810. &lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ls-remote&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--symref&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;origin&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;HEAD&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
  2811. &lt;/li&gt;
  2812. &lt;li&gt;
  2813. &lt;p&gt;Chances are it displays either &lt;code dir="auto"&gt;ref/heads/main&lt;/code&gt; or &lt;code dir="auto"&gt;refs/heads/master&lt;/code&gt;.
  2814. The last component is the branch, so the default branch will be called either &lt;code dir="auto"&gt;main&lt;/code&gt; or &lt;code dir="auto"&gt;master&lt;/code&gt;.
  2815. Before you start a new branch, you will run the following two commands to make sure you start with the latest state of the repository:&lt;/p&gt;
  2816. &lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;switch&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;#x3c;DEFAULT-BRANCH&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pull&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;button title="Copy to clipboard"&gt;git pull"&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
  2817. &lt;/li&gt;
  2818. &lt;li&gt;
  2819. &lt;p&gt;You switch and create a new branch with:&lt;/p&gt;
  2820. &lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;switch&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--create&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;#x3c;BRANCH&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;button title="Copy to clipboard"&gt;"&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
  2821. &lt;p&gt;That way you can work on multiple features at the same time and easily keep your default branch synchronized with the remote repository.&lt;/p&gt;
  2822. &lt;/li&gt;
  2823. &lt;/ol&gt;
  2824. &lt;div&gt;&lt;h3 id="open-a-pull-request"&gt;Open a Pull Request&lt;/h3&gt;&lt;a href="https://julianhofer.eu/blog/rss.xml#open-a-pull-request"&gt;&lt;span&gt;&lt;svg height="16" viewBox="0 0 24 24" width="16" xmlns="http://www.w3.org/2000/svg"&gt;&lt;path d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z" fill="currentcolor"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span&gt;Section titled β€œOpen a Pull Request”&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
  2825. &lt;p&gt;The next step is to open a pull request on GitHub or merge request on GitLab.
  2826. Even though they are named differently, they are exactly the same thing.
  2827. Therefore, I will call both of them pull requests from now on.
  2828. The idea of a pull request is to integrate the changes from one branch into another branch (typically the default branch).
  2829. However, you don’t necessarily want to give every potential contributor the power to create new branches on your repository.
  2830. That is why the concept of forks exists.
  2831. Forks are copies of a repository that are hosted on the same Git forge.
  2832. Contributors can now create branches on their own forks and open pull requests based on these branches.&lt;/p&gt;
  2833. &lt;ol&gt;
  2834. &lt;li&gt;
  2835. &lt;p&gt;If you don’t have push access to the repository, now it’s time to create your own fork.&lt;/p&gt;
  2836.  
  2837. &lt;/li&gt;
  2838. &lt;li&gt;
  2839. &lt;p&gt;Then, you open the Pull Request&lt;/p&gt;
  2840.  
  2841. &lt;/li&gt;
  2842. &lt;/ol&gt;
  2843. &lt;div&gt;&lt;h2 id="checking-out-pull-requests"&gt;Checking out Pull Requests&lt;/h2&gt;&lt;a href="https://julianhofer.eu/blog/rss.xml#checking-out-pull-requests"&gt;&lt;span&gt;&lt;svg height="16" viewBox="0 0 24 24" width="16" xmlns="http://www.w3.org/2000/svg"&gt;&lt;path d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z" fill="currentcolor"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span&gt;Section titled β€œChecking out Pull Requests”&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
  2844. &lt;p&gt;Often, you want to check out a pull request on your own machine to verify that it works as expected.&lt;/p&gt;</description><author>Julian Hofer</author><dc:creator>Julian Hofer</dc:creator><pubDate>Mon, 04 Aug 2025 00:00:00 GMT</pubDate><guid isPermaLink="true">https://julianhofer.eu/blog/2025/git-forges/</guid></item><item><title>Emmanuele Bassi: Governance in GNOME</title><link>https://www.bassi.io/articles/2025/08/03/governance-in-gnome/</link><description>&lt;h2&gt;How do things happen in &lt;span class="caps"&gt;GNOME&lt;/span&gt;?&lt;/h2&gt;
  2845. &lt;p&gt;Things happen in &lt;span class="caps"&gt;GNOME&lt;/span&gt;? Could have fooled me,&amp;nbsp;right?&lt;/p&gt;
  2846. &lt;p&gt;Of course, things happen in &lt;span class="caps"&gt;GNOME&lt;/span&gt;. After all, we have been releasing every
  2847. six months, on the dot, for nearly 25 years. Assuming we&amp;#8217;re not constantly
  2848. re-releasing the same source files, then we have to come to the conclusion
  2849. that things change inside each project that makes &lt;span class="caps"&gt;GNOME&lt;/span&gt;, and thus things
  2850. happen that involve more than one&amp;nbsp;project.&lt;/p&gt;
  2851. &lt;p&gt;So let&amp;#8217;s roll back a&amp;nbsp;bit.&lt;/p&gt;
  2852. &lt;h2&gt;&lt;span class="caps"&gt;GNOME&lt;/span&gt;&amp;#8217;s original&amp;nbsp;sin&lt;/h2&gt;
  2853. &lt;p&gt;We all know Havoc Pennington&amp;#8217;s &lt;a href="https://ometer.com/preferences.html"&gt;essay on
  2854. preferences&lt;/a&gt;; it&amp;#8217;s one of &lt;span class="caps"&gt;GNOME&lt;/span&gt;&amp;#8217;s
  2855. foundational texts, we refer to it pretty much constantly both inside and
  2856. outside the contributors community. It has guided our decisions and taste
  2857. for over 20 years. As far as foundational text goes, though, it applies to
  2858. design philosophy, not to project&amp;nbsp;governance.&lt;/p&gt;
  2859. &lt;p&gt;When talking about the inception and technical direction of the &lt;span class="caps"&gt;GNOME&lt;/span&gt; project
  2860. there are really two foundational texts that describe the goals of &lt;span class="caps"&gt;GNOME&lt;/span&gt;, as
  2861. well as the mechanisms that are employed to achieve those&amp;nbsp;goals.&lt;/p&gt;
  2862. &lt;p&gt;The first one is, of course, Miguel&amp;#8217;s announcement of the &lt;span class="caps"&gt;GNOME&lt;/span&gt; project
  2863. itself, sent to the &lt;span class="caps"&gt;GTK&lt;/span&gt;, Guile, and (for good measure) the &lt;span class="caps"&gt;KDE&lt;/span&gt; mailing&amp;nbsp;lists:&lt;/p&gt;
  2864. &lt;blockquote&gt;
  2865. &lt;p&gt;We will try to reuse the existing code for &lt;span class="caps"&gt;GNU&lt;/span&gt; programs as
  2866. much as possible, while adhering to the guidelines of the project.
  2867. Putting nice and consistent user interfaces over all-time
  2868. favorites will be one of the projects.
  2869.    &amp;#8212; Miguel de Icaza, &lt;a href="https://mail.gnome.org/archives/gtk-list/1997-August/msg00123.html"&gt;&amp;#8220;The &lt;span class="caps"&gt;GNOME&lt;/span&gt; Desktop project.&amp;#8221; announcement&amp;nbsp;email&lt;/a&gt;&lt;/p&gt;
  2870. &lt;/blockquote&gt;
  2871. &lt;p&gt;Once again, everyone related to the &lt;span class="caps"&gt;GNOME&lt;/span&gt; project is (or should be) familiar
  2872. with this&amp;nbsp;text.&lt;/p&gt;
  2873. &lt;p&gt;The second foundational text is not as familiar, outside of the core group
  2874. of people that were around at the time. I am referring to Derek Glidden&amp;#8217;s
  2875. description of the differences between &lt;span class="caps"&gt;GNOME&lt;/span&gt; and &lt;span class="caps"&gt;KDE&lt;/span&gt;, written five years
  2876. after the inception of the project. I isolated a small fragment of&amp;nbsp;it:&lt;/p&gt;
  2877. &lt;blockquote&gt;
  2878. &lt;p&gt;Development strategies are generally determined by whatever light show happens
  2879. to be going on at the moment, when one of the developers will leap up and scream
  2880. β€œI &lt;span class="caps"&gt;WANT&lt;/span&gt; &lt;span class="caps"&gt;IT&lt;/span&gt; &lt;span class="caps"&gt;TO&lt;/span&gt; &lt;span class="caps"&gt;LOOK&lt;/span&gt; &lt;span class="caps"&gt;JUST&lt;/span&gt; &lt;span class="caps"&gt;LIKE&lt;/span&gt; &lt;span class="caps"&gt;THAT&lt;/span&gt;” and then straight-arm his laptop against the
  2881. wall in an hallucinogenic frenzy before vomiting copiously, passing out and
  2882. falling face-down in the middle of the dance floor.
  2883.    &amp;#8212; Derek Glidden, &lt;a href="https://www.bassi.io/~ebassi/GNOMEvKDE.md.html"&gt;&amp;#8220;&lt;span class="caps"&gt;GNOME&lt;/span&gt; vs &lt;span class="caps"&gt;KDE&lt;/span&gt;&amp;#8221;&lt;/a&gt;&lt;/p&gt;
  2884. &lt;/blockquote&gt;
  2885. &lt;p&gt;What both texts have in common is subtle, but explains the origin of the
  2886. project. You may not notice it immediately, but once you see it you can&amp;#8217;t
  2887. unsee it: it&amp;#8217;s the over-reliance on personal projects and taste, to be
  2888. sublimated into a shared vision. A &amp;#8220;bottom up&amp;#8221; approach, with &amp;#8220;nice and
  2889. consistent user interfaces&amp;#8221; bolted on top of &amp;#8220;all-time favorites&amp;#8221;, with zero
  2890. indication of how those nice and consistent UIs would work on extant code
  2891. bases, all driven by somebody&amp;#8217;s with a visionβ€”drug induced or otherwiseβ€”that
  2892. decides to lead the project towards its&amp;nbsp;implementation.&lt;/p&gt;
  2893. &lt;p&gt;It&amp;#8217;s been nearly 30 years, but &lt;span class="caps"&gt;GNOME&lt;/span&gt; still works that&amp;nbsp;way.&lt;/p&gt;
  2894. &lt;p&gt;Sure, we&amp;#8217;ve had a &lt;span class="caps"&gt;HIG&lt;/span&gt; for 25 years, and the shared development resources
  2895. that the project provides tend to mask this, to the point that everyone
  2896. outside the project assumes that all people with access to the &lt;span class="caps"&gt;GNOME&lt;/span&gt; commit
  2897. bit work on the whole project, as a single unit. If you are here, listening
  2898. (or reading) to this, you know it&amp;#8217;s not true. In fact, it is so comically
  2899. removed from the lived experience of everyone involved in the project that
  2900. we generally joke about&amp;nbsp;it.&lt;/p&gt;
  2901. &lt;h2&gt;Herding cats and vectors&amp;nbsp;sum&lt;/h2&gt;
  2902. &lt;p&gt;During my first &lt;span class="caps"&gt;GUADEC&lt;/span&gt;, back in 2005, I saw a great slide from Seth Nickell,
  2903. one of the original &lt;span class="caps"&gt;GNOME&lt;/span&gt; designers. It showed &lt;span class="caps"&gt;GNOME&lt;/span&gt; contributors
  2904. represented as a jumble of vectors going in all directions, cancelling each
  2905. component out; and the occasional movement in the project was the result of
  2906. somebody pulling/pushing harder in their&amp;nbsp;direction.&lt;/p&gt;
  2907. &lt;p&gt;Of course, this is not the exclusive province of &lt;span class="caps"&gt;GNOME&lt;/span&gt;: you could take most
  2908. complex free and open source software projects and draw a similar diagram. I
  2909. contend, though, that when it comes to &lt;span class="caps"&gt;GNOME&lt;/span&gt; this is not emergent behaviour
  2910. but it&amp;#8217;s baked into the project from its very inception: a loosey-goosey
  2911. collection of cats, herded together by whoever shows up with &amp;#8220;a vision&amp;#8221;,
  2912. but, also, a collection of loosely coupled projects. Over the years we tried
  2913. to put a rest to the notion that &lt;span class="caps"&gt;GNOME&lt;/span&gt; is a box of &lt;span class="caps"&gt;LEGO&lt;/span&gt;, meant to be
  2914. assembled together by distributors and users in the way they most like it;
  2915. while our software stack has graduated from the &amp;#8220;thrown together at the last
  2916. minute&amp;#8221; quality of its first decade, our community is still very much
  2917. following that very same model; the only way it &lt;strong&gt;seems&lt;/strong&gt; to work is because
  2918. we have a few people maintaining a lot of&amp;nbsp;components.&lt;/p&gt;
  2919. &lt;h2&gt;On&amp;nbsp;maintainers&lt;/h2&gt;
  2920. &lt;p&gt;I am a software nerd, and one of the side effects of this terminal condition
  2921. is that I like optimisation problems. Optimising software is inherently
  2922. boring, though, so I end up trying to optimise processes and people. The
  2923. fundamental truth of process optimisation, just like software, is to avoid
  2924. unnecessary workβ€”which, in some cases, means optimising away the people&amp;nbsp;involved.&lt;/p&gt;
  2925. &lt;p&gt;I am afraid I will have to be blunt, here, so I am going to ask for your
  2926. forgiveness in&amp;nbsp;advance.&lt;/p&gt;
  2927. &lt;p&gt;Let&amp;#8217;s say you are a maintainer inside a community of maintainers. Dealing
  2928. with people is hard, and the lord forbid you talk to other people about what
  2929. you&amp;#8217;re doing, what they are doing, and what you can do together, so you only
  2930. have a few options&amp;nbsp;available.&lt;/p&gt;
  2931. &lt;p&gt;The first one is: you carve out your niche. You start, or take over, a
  2932. project, or an aspect of a project, and you try very hard to make yourself
  2933. indispensable, so that everything ends up passing through you, and everyone
  2934. has to defer to your taste, opinion, or&amp;nbsp;edict.&lt;/p&gt;
  2935. &lt;p&gt;Another option: &lt;span class="caps"&gt;API&lt;/span&gt; design is opinionated, and reflects the thoughts of the
  2936. person behind it. By designing platform &lt;span class="caps"&gt;API&lt;/span&gt;, you try to replicate your
  2937. toughts, taste, and opinions into the minds of the people using it, like the
  2938. eggs of parasitic wasp; because if everybody thinks like you, then there
  2939. won&amp;#8217;t be conflicts, and you won&amp;#8217;t have to deal with details, like &amp;#8220;how to
  2940. make this application work&amp;#8221;, or &amp;#8220;how to share functionality&amp;#8221;; or, you know,
  2941. having to develop a theory of mind for relating to other&amp;nbsp;people.&lt;/p&gt;
  2942. &lt;p&gt;Another option: you try to reimplement the entirety of a platform by
  2943. yourself. You start a bunch of projects, which require starting a bunch of
  2944. dependencies, which require refactoring a bunch of libraries, which ends up
  2945. cascading into half of the stack. Of course, since you&amp;#8217;re by yourself, you
  2946. end up with a consistent approach to everything. Everything is as it ought
  2947. to be: fast, lean, efficient, a reflection of your taste, commitment, and
  2948. &lt;em&gt;ethos&lt;/em&gt;. You made everyone else redundant, which means people depend on you,
  2949. but also nobody is interested in helping you out, because you are now taken
  2950. for granted, on the one hand, and nobody is able to get a word edgewise into
  2951. what you made on the&amp;nbsp;other.&lt;/p&gt;
  2952. &lt;p&gt;I purposefully did not name names, even though we can all recognise somebody
  2953. in these examples. For instance, I recognise myself. I have been all of
  2954. these examples, at one point or another over the past 20&amp;nbsp;years.&lt;/p&gt;
  2955. &lt;h2&gt;Painting a target on your&amp;nbsp;back&lt;/h2&gt;
  2956. &lt;p&gt;But if this is what it looks like from within a project, what it looks like
  2957. from the outside is even&amp;nbsp;worse.&lt;/p&gt;
  2958. &lt;p&gt;Once you start dragging other people, you raise your visibility; people
  2959. start learning your name, because you appear in the issue tracker, on
  2960. Matrix/&lt;span class="caps"&gt;IRC&lt;/span&gt;, on Discourse and Planet &lt;span class="caps"&gt;GNOME&lt;/span&gt;. Youtubers and journalists start
  2961. asking you questions about the project. Randos on web forums start
  2962. associating you to everything &lt;span class="caps"&gt;GNOME&lt;/span&gt; does, or does not; to features, design,
  2963. and bugs. You become responsible for every decision, whether you are or not,
  2964. and this leads to being the embodiment of all evil the project does. You&amp;#8217;ll
  2965. get hate mail, you&amp;#8217;ll be harrassed, your words will be used against you and
  2966. the project for ever and&amp;nbsp;ever.&lt;/p&gt;
  2967. &lt;h2&gt;Burnout and&amp;nbsp;you&lt;/h2&gt;
  2968. &lt;p&gt;Of course, that ends up burning people out; it would be absurd if it didn&amp;#8217;t.
  2969. Even in the best case possible, you&amp;#8217;ll end up burning out just by reaching
  2970. empathy fatigue, because everyone has access to you, and everyone has their
  2971. own problems and bugs and features and wouldn&amp;#8217;t it be great to solve every
  2972. problem in the world? This is similar to working for non profits as opposed
  2973. to the typical corporate burnout: you get into a feedback loop where you
  2974. don&amp;#8217;t want to distance yourself from the work you do because the work you do
  2975. gives meaning to yourself and to the people that use it; and yet working on
  2976. it hurts you. It also empowers bad faith actors to hound you down to the
  2977. ends of the earth, until you realise that turning sand into computers was a
  2978. terrible mistake, and we should have torched the first personal computer
  2979. down on&amp;nbsp;sight.&lt;/p&gt;
  2980. &lt;h2&gt;Governance&lt;/h2&gt;
  2981. &lt;p&gt;We want to have structure, so that people know what to expect and how to
  2982. navigate the decision making process inside the project; we also want to
  2983. avoid having a sacrificial lamb that takes on all the problems in the world
  2984. on their shoulders until we burn them down to a cinder and they have to
  2985. leave. We&amp;#8217;re 28 years too late to have a benevolent dictator, self-appointed
  2986. or otherwise, and we don&amp;#8217;t want to have a public consultation every time we
  2987. want to deal with a systemic feature. What do we&amp;nbsp;do?&lt;/p&gt;
  2988. &lt;h3&gt;Examples&lt;/h3&gt;
  2989. &lt;p&gt;What do other projects have to teach us about governance? We are not the
  2990. only complex free software project in existence, and it would be an appaling
  2991. measure of narcissism to believe that we&amp;#8217;re special in any way, shape or&amp;nbsp;form.&lt;/p&gt;
  2992. &lt;h3&gt;Python&lt;/h3&gt;
  2993. &lt;p&gt;We should all know what a &lt;a href="https://peps.python.org"&gt;Python &lt;span class="caps"&gt;PEP&lt;/span&gt;&lt;/a&gt; is, but if
  2994. you are not familiar with the process I strongly recommend going through it.
  2995. It&amp;#8217;s well documented, and pretty much the de facto standard for any complex
  2996. free and open source project that has achieved escape velocity from a
  2997. centralised figure in charge of the whole decision making process. The real
  2998. achievement of the Python community is that it adopted this policy long
  2999. before their centralised figure called it quits. The interesting thing of
  3000. the &lt;span class="caps"&gt;PEP&lt;/span&gt; process is that it is used to codify the governance of the project
  3001. itself; the &lt;span class="caps"&gt;PEP&lt;/span&gt; template is a &lt;span class="caps"&gt;PEP&lt;/span&gt;; teams are defined through PEPs; target
  3002. platforms are defined through PEPs; deprecations are defined through PEPs;
  3003. all project-wide processes are defined through&amp;nbsp;PEPs.&lt;/p&gt;
  3004. &lt;h3&gt;Rust&lt;/h3&gt;
  3005. &lt;p&gt;Rust has a similar process for language, tooling, and standard library
  3006. changes, called &lt;a href="https://github.com/rust-lang/rfcs"&gt;&amp;#8220;&lt;span class="caps"&gt;RFC&lt;/span&gt;&amp;#8221;&lt;/a&gt;. The &lt;span class="caps"&gt;RFC&lt;/span&gt; process
  3007. is more lightweight on the formalities than Python&amp;#8217;s PEPs, but it&amp;#8217;s still
  3008. very well defined. Rust, being a project that came into existence in a
  3009. Post-&lt;span class="caps"&gt;PEP&lt;/span&gt; world, adopted the same type of process, and used it to codify
  3010. teams, governance, and any and all project-wide&amp;nbsp;processes.&lt;/p&gt;
  3011. &lt;h3&gt;Fedora&lt;/h3&gt;
  3012. &lt;p&gt;Fedora change proposals exist to discuss and document both self-contained
  3013. changes (usually fairly uncontroversial, given that they are proposed by the
  3014. same owners of module being changed) and system-wide changes. The main
  3015. difference between them is that most of the elements of a system-wide change
  3016. proposal are required, wheres for self-contained proposals they can be
  3017. optional; for instance, a system-wide change must have a contingency plan, a
  3018. way to test it, and the impact on documentation and release notes, whereas
  3019. as self-contained change does&amp;nbsp;not.&lt;/p&gt;
  3020. &lt;h3&gt;&lt;span class="caps"&gt;GNOME&lt;/span&gt;&lt;/h3&gt;
  3021. &lt;p&gt;Turns out that we once did have &lt;a href="https://gitlab.gnome.org/Archive/gep"&gt;&amp;#8220;&lt;span class="caps"&gt;GNOME&lt;/span&gt; Enhancement
  3022. Proposals&amp;#8221;&lt;/a&gt; (&lt;span class="caps"&gt;GEP&lt;/span&gt;), mainly modelled on
  3023. Python&amp;#8217;s &lt;span class="caps"&gt;PEP&lt;/span&gt; from 2002. If this comes as a surprise, that&amp;#8217;s because they
  3024. lasted for about a year, mainly because it was a reactionary process to try
  3025. and funnel some of the large controversies of the 2.0 development cycle into
  3026. a productive outlet that didn&amp;#8217;t involve flames and people dramatically
  3027. quitting the project. GEPs failed once the community fractured, and people
  3028. started working in silos, either under their own direction or, more likely,
  3029. under their management&amp;#8217;s direction. What&amp;#8217;s the point of discussing a
  3030. project-wide change, when that change was going to be implemented by people
  3031. already working&amp;nbsp;together?&lt;/p&gt;
  3032. &lt;p&gt;The &lt;span class="caps"&gt;GEP&lt;/span&gt; process mutated into the lightweight &amp;#8220;module proposal&amp;#8221; process,
  3033. where people discussed adding and removing dependencies on the desktop
  3034. development mailing listβ€”something we also lost over the 2.x cycle, mainly
  3035. because the amount of discussions over time tended towards zero. The people
  3036. involved with the change knew what those modules brought to the release, and
  3037. people unfamiliar with them were either giving out unsolicited advice, or
  3038. were simply not reached by the desktop development mailing list. The
  3039. discussions turned into external dependencies notifications, which also died
  3040. up because apparently asking to compose an email to notify the release team
  3041. that a new dependency was needed to build a core module was far too much of
  3042. a bother for project&amp;nbsp;maintainers.&lt;/p&gt;
  3043. &lt;p&gt;The creation and failure of &lt;span class="caps"&gt;GEP&lt;/span&gt; and module proposals is both an indication
  3044. of the need for structure inside &lt;span class="caps"&gt;GNOME&lt;/span&gt;, and how this need collides with the
  3045. expectation that project maintainers have not just complete control over
  3046. every aspect of their domain, but that they can also drag out the process
  3047. until all the energy behind it has dissipated. Being in charge for the long
  3048. run allows people to just run out the clock on everybody&amp;nbsp;else.&lt;/p&gt;
  3049. &lt;h2&gt;Goals&lt;/h2&gt;
  3050. &lt;p&gt;So, what should be the goal of a proper technical governance model for the
  3051. &lt;span class="caps"&gt;GNOME&lt;/span&gt;&amp;nbsp;project?&lt;/p&gt;
  3052. &lt;h3&gt;Diffusing&amp;nbsp;responsibilities&lt;/h3&gt;
  3053. &lt;p&gt;This should be goal zero of any attempt at structuring the technical
  3054. governance of &lt;span class="caps"&gt;GNOME&lt;/span&gt;. We have too few people in too many critical positions.
  3055. We can call it &amp;#8220;efficiency&amp;#8221;, we can call it &amp;#8220;bus factor&amp;#8221;, we can call it
  3056. &amp;#8220;bottleneck&amp;#8221;, but the result is the same: the responsibility for anything is
  3057. too concentrated. This is how you get conflict. This is how you get burnout.
  3058. This is how you paralise a whole project. By having too few people in
  3059. positions of responsibility, we don&amp;#8217;t have enough slack in the governance
  3060. model; it&amp;#8217;s an illusion of&amp;nbsp;efficiency.&lt;/p&gt;
  3061. &lt;p&gt;Responsibility is not something to hoard: it&amp;#8217;s something to&amp;nbsp;distribute.&lt;/p&gt;
  3062. &lt;h3&gt;Empowering the&amp;nbsp;community&lt;/h3&gt;
  3063. &lt;p&gt;The community of contributors should be able to know when and how a decision
  3064. is made; it should be able to know what to do once a decision is made. Right
  3065. now, the process is opaque because it&amp;#8217;s done inside a million different
  3066. rooms, and, more importantly, it is not recorded for posterity. Random
  3067. GitLab issues should not be the only place where people can be informed that
  3068. some decision was&amp;nbsp;taken.&lt;/p&gt;
  3069. &lt;h3&gt;Empowering&amp;nbsp;individuals&lt;/h3&gt;
  3070. &lt;p&gt;Individuals should be able to contribute to a decision without necessarily
  3071. becoming responsible for a whole project. It&amp;#8217;s daunting, and requires a
  3072. measure of &lt;em&gt;hubris&lt;/em&gt; that cannot be allowed to exist in a shared space. In a
  3073. similar fashion, we should empower people that want to contribute to the
  3074. project by reducing the amount of fluff coming from people with zero stakes
  3075. in it, and are interested only in giving out an opinion on their perfectly
  3076. spherical, frictionless desktop&amp;nbsp;environment.&lt;/p&gt;
  3077. &lt;p&gt;It is &lt;em&gt;free and open source software&lt;/em&gt;, not &lt;em&gt;free and open mic night down at
  3078. the pub&lt;/em&gt;.&lt;/p&gt;
  3079. &lt;h3&gt;Actual decision making&amp;nbsp;process&lt;/h3&gt;
  3080. &lt;p&gt;We say we work by rough consensus, but if a single person is responsible for
  3081. multiple modules inside the project, we&amp;#8217;re just deceiving ourselves. I
  3082. should not be able to design something on my own, commit it to all projects
  3083. I maintain, and then go home, regardless of whether what I designed is good
  3084. or&amp;nbsp;necessary.&lt;/p&gt;
  3085. &lt;h2&gt;Proposed &lt;span class="caps"&gt;GNOME&lt;/span&gt;&amp;nbsp;Changes✝&lt;/h2&gt;
  3086. &lt;p&gt;✝ Name subject to&amp;nbsp;change&lt;/p&gt;
  3087. &lt;h3&gt;PGCs&lt;/h3&gt;
  3088. &lt;p&gt;We have better tools than what the &lt;span class="caps"&gt;GEP&lt;/span&gt; used to use and be. We have better
  3089. communication venues in 2025; we have better validation; we have better
  3090. publishing&amp;nbsp;mechanisms.&lt;/p&gt;
  3091. &lt;p&gt;We can take a lightweight approach, with a well-defined process, and use it
  3092. not for actual design or decision-making, but for discussion and
  3093. documentation. If you are trying to design something and you use this
  3094. process, you are by definition Doing It Wrongβ„’. You should have a design
  3095. ready, and series of steps to achieve it, as part of a proposal. You should
  3096. already know the projects involved, and already have an idea of the effort
  3097. needed to make something&amp;nbsp;happen.&lt;/p&gt;
  3098. &lt;p&gt;Once you have a formal proposal, you present it to the various stakeholders,
  3099. and iterate over it to improve it, clarify it, and amend it, until you have
  3100. something that has a rough consensus among all the parties involved. Once
  3101. that&amp;#8217;s done, the proposal is now in effect, and people can refer to it
  3102. during the implementation, and in the future. This way, we don&amp;#8217;t have to ask
  3103. people to remember a decision made six months, two years, ten years ago:
  3104. it&amp;#8217;s already&amp;nbsp;available.&lt;/p&gt;
  3105. &lt;h3&gt;Editorial&amp;nbsp;team&lt;/h3&gt;
  3106. &lt;p&gt;Proposals need to be valid, in order to be presented to the community at
  3107. large; that validation comes from an editorial team. The editors of the
  3108. proposals are not there to evaluate its contents: they are there to ensure
  3109. that the proposal is going through the expected steps, and that discussions
  3110. related to it remain relevant and constrained within the accepted period and
  3111. scope. They are there to steer the discussion, and avoid architecture
  3112. astronauts parachuting into the issue tracker or Discourse to give their
  3113. unwarranted&amp;nbsp;opinion.&lt;/p&gt;
  3114. &lt;p&gt;Once the proposal is open, the editorial team is responsible for its
  3115. inclusion in the public website, and for keeping track of its&amp;nbsp;state.&lt;/p&gt;
  3116. &lt;h3&gt;Steering&amp;nbsp;group&lt;/h3&gt;
  3117. &lt;p&gt;The steering group is the final arbiter of a proposal. They are responsible
  3118. for accepting it, or rejecting it, depending on the feedback from the
  3119. various stakeholders. The steering group does not design or direct &lt;span class="caps"&gt;GNOME&lt;/span&gt; as
  3120. a whole: they are the ones that ensure that communication between the parts
  3121. happens in a meaningful manner, and that rough consensus is&amp;nbsp;achieved.&lt;/p&gt;
  3122. &lt;p&gt;The steering group is also, by design, not the release team: it is made of
  3123. representatives from all the teams related to technical&amp;nbsp;matters.&lt;/p&gt;
  3124. &lt;h2&gt;Is this&amp;nbsp;enough?&lt;/h2&gt;
  3125. &lt;p&gt;Sadly,&amp;nbsp;no.&lt;/p&gt;
  3126. &lt;p&gt;Reviving a process for proposing changes in &lt;span class="caps"&gt;GNOME&lt;/span&gt; without addressing the
  3127. shortcomings of its first iteration would inevitably lead to a repeat of its&amp;nbsp;results.&lt;/p&gt;
  3128. &lt;p&gt;We have better tooling, but the problem is still that we&amp;#8217;re demanding that
  3129. each project maintainer gets on board with a process that has no mechanism
  3130. to enforce&amp;nbsp;compliance.&lt;/p&gt;
  3131. &lt;p&gt;Once again, the problem is that we have a bunch of fiefdoms that need to be
  3132. opened up to ensure that more people can work on&amp;nbsp;them.&lt;/p&gt;
  3133. &lt;h2&gt;Whither&amp;nbsp;maintainers&lt;/h2&gt;
  3134. &lt;p&gt;In what was, in retrospect, possibly one of my least gracious and yet most
  3135. prophetic moments on the desktop development mailing list, I once said that,
  3136. if it were possible, I would have already replaced all &lt;span class="caps"&gt;GNOME&lt;/span&gt; maintainers
  3137. with a shell script. Turns out that we did replace a lot of what maintainers
  3138. used to do, and we used &lt;a href="https://gitlab.gnome.org/Infrastructure/openshift-images/gnome-release-service"&gt;a large Python
  3139. service&lt;/a&gt;
  3140. to do&amp;nbsp;that.&lt;/p&gt;
  3141. &lt;p&gt;Individual maintainers should not exist in a complex projectβ€”for both the
  3142. project&amp;#8217;s and the contributors&amp;#8217; sake. They are inefficiency made manifest, a
  3143. bottleneck, a point of contention in a distributed environment like &lt;span class="caps"&gt;GNOME&lt;/span&gt;.
  3144. Luckily for us, we almost made them entirely redundant already! Thanks to
  3145. the release service and &lt;span class="caps"&gt;CI&lt;/span&gt; pipelines, we don&amp;#8217;t need a person spinning up a
  3146. release archive and uploading it into a file server. We just need somebody
  3147. to tag the source code repository, and anybody with the right permissions
  3148. could do&amp;nbsp;that.&lt;/p&gt;
  3149. &lt;p&gt;We need people to review contributions; we need people to write release
  3150. notes; we need people to triage the issue tracker; we need people to
  3151. contribute features and bug fixes. None of those tasks require the
  3152. &amp;#8220;maintainer&amp;#8221;&amp;nbsp;role.&lt;/p&gt;
  3153. &lt;p&gt;So, let&amp;#8217;s get rid of maintainers once and for all. We can delegate the
  3154. actual release tagging of core projects and applications to the &lt;span class="caps"&gt;GNOME&lt;/span&gt;
  3155. release team; they are already releasing &lt;span class="caps"&gt;GNOME&lt;/span&gt; anyway, so what&amp;#8217;s the point
  3156. in having them wait every time for somebody else to do individual releases?
  3157. All people need to do is to write down what changed in a release, and that
  3158. should be part of a change itself; we have centralised release notes, and we
  3159. can easily extract the list of bug fixes from the commit log. If you can
  3160. ensure that a commit message is correct, you can also get in the habit of
  3161. updating the &lt;code&gt;NEWS&lt;/code&gt; file as part of a merge&amp;nbsp;request.&lt;/p&gt;
  3162. &lt;p&gt;Additional benefits of having all core releases done by a central authority
  3163. are that we get people to update the release notes every time something
  3164. changes; and that we can sign all releases with a &lt;span class="caps"&gt;GNOME&lt;/span&gt; key that downstreams
  3165. can rely&amp;nbsp;on.&lt;/p&gt;
  3166. &lt;h2&gt;Embracing special interest&amp;nbsp;groups&lt;/h2&gt;
  3167. &lt;p&gt;But it&amp;#8217;s still not&amp;nbsp;enough.&lt;/p&gt;
  3168. &lt;p&gt;Especially when it comes to the application development platform, we have
  3169. already a bunch of components with an informal scheme of shared
  3170. responsibility. Why not make that scheme&amp;nbsp;official?&lt;/p&gt;
  3171. &lt;p&gt;Let&amp;#8217;s create the &lt;span class="caps"&gt;SDK&lt;/span&gt; special interest group; take all the developers for
  3172. the base libraries that are part of &lt;span class="caps"&gt;GNOME&lt;/span&gt;β€”GLib, Pango, &lt;span class="caps"&gt;GTK&lt;/span&gt;, libadwaitaβ€”and
  3173. formalise the group of people that currently does things like development,
  3174. review, bug fixing, and documentation writing. Everyone in the group should
  3175. feel empowered to work on all the projects that belong to that group. We
  3176. already are, except we end up deferring to somebody that is usually too busy
  3177. to cover every single&amp;nbsp;module.&lt;/p&gt;
  3178. &lt;p&gt;Other special interest groups should be formed around the desktop, the core
  3179. applications, the development tools, the &lt;span class="caps"&gt;OS&lt;/span&gt; integration, the accessibility
  3180. stack, the local search engine, the system&amp;nbsp;settings.&lt;/p&gt;
  3181. &lt;p&gt;Adding more people to these groups is not going to be complicated, or
  3182. introduce instability, because the responsibility is now shared; we would
  3183. not be taking somebody that is already overworked, or even potentially new
  3184. to the community, and plopping them into the hot seat, ready for a&amp;nbsp;burnout.&lt;/p&gt;
  3185. &lt;p&gt;Each special interest group would have a representative in the steering
  3186. group, alongside teams like documentation, design, and localisation, thus
  3187. ensuring that each aspect of the project technical direction is included in
  3188. any discussion. Each special interest group could also have additional
  3189. sub-groups, like a web services group in the system settings group; or a
  3190. networking group in the &lt;span class="caps"&gt;OS&lt;/span&gt; integration&amp;nbsp;group.&lt;/p&gt;
  3191. &lt;h2&gt;What happens if I say&amp;nbsp;no?&lt;/h2&gt;
  3192. &lt;p&gt;I get it. You like being in charge. You want to be the one calling the
  3193. shots. You feel responsible for your project, and you don&amp;#8217;t want other
  3194. people to tell you what to&amp;nbsp;do.&lt;/p&gt;
  3195. &lt;p&gt;If this is how you feel, then there&amp;#8217;s nothing wrong with parting ways with
  3196. the &lt;span class="caps"&gt;GNOME&lt;/span&gt;&amp;nbsp;project.&lt;/p&gt;
  3197. &lt;p&gt;&lt;span class="caps"&gt;GNOME&lt;/span&gt; depends on a ton of projects hosted outside &lt;span class="caps"&gt;GNOME&lt;/span&gt;&amp;#8217;s own
  3198. infrastructure, and we communicate with people maintaining those projects
  3199. every day. It&amp;#8217;s 2025, not 1997: there&amp;#8217;s no shortage of code hosting services
  3200. in the world, we don&amp;#8217;t need to have them all on &lt;span class="caps"&gt;GNOME&lt;/span&gt;&amp;nbsp;infrastructure.&lt;/p&gt;
  3201. &lt;p&gt;If you want to play with the other children, if you want to be part of
  3202. &lt;span class="caps"&gt;GNOME&lt;/span&gt;, you get to play with a shared set of rules; and that means sharing
  3203. all the toys, and not hoarding them for&amp;nbsp;yourself.&lt;/p&gt;
  3204. &lt;h2&gt;Civil&amp;nbsp;service&lt;/h2&gt;
  3205. &lt;p&gt;What we really want &lt;span class="caps"&gt;GNOME&lt;/span&gt; to be is a group of people working together. We
  3206. already are, somewhat, but we can be better at it. We don&amp;#8217;t want rule and
  3207. design by committee, but we do need structure, and we need that structure to
  3208. be based on expertise; to have distinct sphere of competence; to have
  3209. continuity across time; and to be based on rules. We need something
  3210. flexible, to take into account the needs of &lt;span class="caps"&gt;GNOME&lt;/span&gt; as a project, and be
  3211. capable of growing in complexity so that nobody can be singled out, brigaded
  3212. on, or burnt to a cinder on the sacrificial&amp;nbsp;altar.&lt;/p&gt;
  3213. &lt;p&gt;Our days of passing out in the middle of the dance floor are long gone. We
  3214. might not all be oldβ€”actually, I&amp;#8217;m fairly sure we aren&amp;#8217;tβ€”but &lt;span class="caps"&gt;GNOME&lt;/span&gt; has long
  3215. ceased to be something we can throw together at the last minute just because
  3216. somebody assumed the mantle of a protean ruler, and managed to involve
  3217. themselves with every single project until they are the literal embodiement
  3218. of an autocratic force capable of dragging everybody else towards a goal,
  3219. until the burn out and have to leave for their own&amp;nbsp;sake.&lt;/p&gt;
  3220. &lt;p&gt;We &lt;strong&gt;can&lt;/strong&gt; do better than this. We &lt;strong&gt;must&lt;/strong&gt; do&amp;nbsp;better.&lt;/p&gt;
  3221. &lt;h2&gt;To sum&amp;nbsp;up&lt;/h2&gt;
  3222. &lt;p&gt;Stop releasing individual projects, and let the release team do it when&amp;nbsp;needed.&lt;/p&gt;
  3223. &lt;p&gt;Create teams to manage areas of interest, instead of single&amp;nbsp;projects.&lt;/p&gt;
  3224. &lt;p&gt;Create a steering group from representatives of those&amp;nbsp;teams.&lt;/p&gt;
  3225. &lt;p&gt;Every change that affects one or more teams has to be discussed and
  3226. documented in a public setting among contributors, and then published for
  3227. future&amp;nbsp;reference.&lt;/p&gt;
  3228. &lt;p&gt;None of this should be controversial because, outside of the publishing bit,
  3229. it&amp;#8217;s how we are already doing things. This proposal aims at making it
  3230. official so that people can actually rely on it, instead of having to divine
  3231. the process out of thin&amp;nbsp;air.&lt;/p&gt;
  3232. &lt;hr /&gt;
  3233. &lt;h2&gt;The next&amp;nbsp;steps&lt;/h2&gt;
  3234. &lt;p&gt;We&amp;#8217;re close to the &lt;span class="caps"&gt;GNOME&lt;/span&gt; 49 release, now that &lt;span class="caps"&gt;GUADEC&lt;/span&gt; 2025 has ended, so
  3235. people are busy working on tagging releases, fixing bugs, and the work on
  3236. the release notes has started. Nevertheless, we can already start planning
  3237. for an implementation of a new governance model for &lt;span class="caps"&gt;GNOME&lt;/span&gt; for the next&amp;nbsp;cycle.&lt;/p&gt;
  3238. &lt;p&gt;First of all, we need to create teams and special interest groups. We don&amp;#8217;t
  3239. have a formal process for that, so this is also a great chance at
  3240. introducing the change proposal process as a mechanism for structuring the
  3241. community, just like the Python and Rust communities do. Teams will need
  3242. their own space for discussing issues, and share the load. The first team
  3243. I&amp;#8217;d like to start is an &amp;#8220;introspection and language bindings&amp;#8221; group, for all
  3244. bindings hosted on &lt;span class="caps"&gt;GNOME&lt;/span&gt; infrastructure; it would act as a point of
  3245. reference for all decisions involving projects that consume the &lt;span class="caps"&gt;GNOME&lt;/span&gt;
  3246. software development platform through its machine-readable &lt;span class="caps"&gt;ABI&lt;/span&gt; description.
  3247. Another group I&amp;#8217;d like to create is an editorial group for the developer and
  3248. user documentation; documentation benefits from a consistent editorial
  3249. voice, while the process of writing documentation should be open to
  3250. everybody in the&amp;nbsp;community.&lt;/p&gt;
  3251. &lt;p&gt;A very real issue that was raised during &lt;span class="caps"&gt;GUADEC&lt;/span&gt; is bootstrapping the
  3252. steering committee; who gets to be on it, what is the committee&amp;#8217;s remit, how
  3253. it works. There are options, but if we want the steering committee to be a
  3254. representation of the technical expertise of the &lt;span class="caps"&gt;GNOME&lt;/span&gt; community, it also
  3255. has to be established by the very same community; in this sense, the board
  3256. of directors, as representatives of the community, could work on
  3257. defining the powers and compositions of this&amp;nbsp;committee.&lt;/p&gt;
  3258. &lt;p&gt;There are many more issues we are going to face, but I think we can start
  3259. from these and evaluate our own version of a technical governance model that
  3260. works for &lt;span class="caps"&gt;GNOME&lt;/span&gt;, and that can grow with the project. In the next couple of
  3261. weeks I&amp;#8217;ll start publishing drafts for team governance and the
  3262. power/composition/procedure of the steering committee, mainly for iteration
  3263. and&amp;nbsp;comments.&lt;/p&gt;</description><author>Emmanuele Bassi</author><dc:creator>Emmanuele Bassi</dc:creator><pubDate>Sun, 03 Aug 2025 20:48:00 GMT</pubDate><guid isPermaLink="true">https://www.bassi.io/articles/2025/08/03/governance-in-gnome/</guid></item><item><title>Tobias Bernard: GUADEC 2025</title><link>https://blogs.gnome.org/tbernard/2025/08/03/guadec-2025/</link><description>&lt;p&gt;Last week was this year’s GUADEC, the first ever in Italy! Here are a few impressions.&lt;/p&gt;
  3264. &lt;h3 id="local-first"&gt;Local-First&lt;/h3&gt;
  3265. &lt;p&gt;One of my main focus areas this year was local-first, since that’s what we’re working on right now with the &lt;a class="external" href="https://github.com/p2panda/reflection"&gt;Reflection&lt;/a&gt; project (see the &lt;a href="https://blogs.gnome.org/tbernard/2025/06/30/aardvark-summer-2025-update"&gt;previous blog post&lt;/a&gt;). Together with Julian and Andreas we did two lightning talks (one on local-first generally, and one on Reflection in particular), and two BoF sessions.&lt;/p&gt;
  3266. &lt;figure class="wp-caption alignnone" id="attachment_10611" style="width: 660px;"&gt;&lt;img alt="" class="size-large wp-image-10611" height="389" src="https://blogs.gnome.org/tbernard/files/2025/08/21-1024x604.jpg" width="660" /&gt;&lt;figcaption class="wp-caption-text" id="caption-attachment-10611"&gt;Local-First BoF&lt;/figcaption&gt;&lt;/figure&gt;
  3267. &lt;p&gt;At the BoFs we did a bit of Reflection testing, and reached a new record of people simultaneously trying the app:&lt;/p&gt;
  3268. &lt;figure class="wp-caption alignnone" id="attachment_10620" style="width: 660px;"&gt;&lt;img alt="" class="size-large wp-image-10620" height="453" src="https://blogs.gnome.org/tbernard/files/2025/08/32-1024x703.png" width="660" /&gt;&lt;figcaption class="wp-caption-text" id="caption-attachment-10620"&gt;This also uncovered a padding bug in the users popover :)&lt;/figcaption&gt;&lt;/figure&gt;
  3269. &lt;p&gt;Andreas also explained the p2anda stack in detail using a new diagram we made a few weeks back, which visualizes how the various components fit together in a real app.&lt;/p&gt;
  3270. &lt;figure class="wp-caption alignnone" id="attachment_10621" style="width: 500px;"&gt;&lt;img alt="" class="size-full wp-image-10621" height="694" src="https://blogs.gnome.org/tbernard/files/2025/08/diagram.png" width="500" /&gt;&lt;figcaption class="wp-caption-text" id="caption-attachment-10621"&gt;p2panda stack diagram&lt;/figcaption&gt;&lt;/figure&gt;
  3271. &lt;p&gt;We also discussed some longer-term plans, particularly around having a system-level sync service. The motivation for this is twofold: We want to make it as easy as possible for app developers to add sync to their app. It’s never going to be β€œfree”, but if we can at least abstract some of this in a useful way that’s a big win for developer experience. More importantly though, from a security/privacy point of view we really don’t want every app to have unrestricted access to network, Bluetooth, etc. which would be required if every app does its own p2p sync.&lt;/p&gt;
  3272. &lt;p&gt;One option being discussed is taking the networking part of p2panda (including iroh for p2p networking) and making it a portal API which apps can use to talk to other instances of themselves on other devices.&lt;/p&gt;
  3273. &lt;p&gt;Another idea was a more high-level portal that works more like a file β€œshare” system that can sync arbitary files by just attaching the sync context to files as xattrs and having a centralized service handle all the syncing. This would have the advantage of not requiring special UI in apps, just a portal and some integration in Files. Real-time collaboration would of course not be possible without actual app integration, but for many use cases that’s not needed anyway, so perhaps we could have both a high- and low-level API to cover different scenarios?&lt;/p&gt;
  3274. &lt;p&gt;There are still a &lt;em&gt;lot&lt;/em&gt; of open questions here, but it’s cool to see how things get a little bit more concrete every time :)&lt;/p&gt;
  3275. &lt;p&gt;If you&amp;#8217;re interested in the details, check out the &lt;a class="external" href="https://pad.gnome.org/localfirstbof2025"&gt;full notes from both BoF sessions&lt;/a&gt;.&lt;/p&gt;
  3276. &lt;h3 id="design"&gt;Design&lt;/h3&gt;
  3277. &lt;p&gt;Jakub and I gave the traditional design team talk &amp;#8211; a bit underprepared and last-minute (thanks Jakub for doing most of the heavy lifting), but it was cool to see in retrospect how much we got done in the past year despite how much energy unfortunately went into unrelated things. The all-new slate of websites is especially cool after so many years of gnome.org et al looking very stale. You can &lt;a class="external" href="https://teams.pages.gitlab.gnome.org/Design/guadec-talk-2025/talk.pdf"&gt;find the slides here&lt;/a&gt;.&lt;/p&gt;
  3278. &lt;figure class="wp-caption alignnone" id="attachment_10612" style="width: 660px;"&gt;&lt;img alt="" class="size-large wp-image-10612" height="447" src="https://blogs.gnome.org/tbernard/files/2025/08/1-1024x694.jpg" width="660" /&gt;&lt;figcaption class="wp-caption-text" id="caption-attachment-10612"&gt;Jakub giving the design talk&lt;/figcaption&gt;&lt;/figure&gt;
  3279. &lt;p&gt;Inspired by the &lt;a href="https://blogs.gnome.org/tbernard/2025/06/01/summer-of-gnome-os"&gt;&lt;em&gt;Summer of GNOME OS&lt;/em&gt;&lt;/a&gt; challenge many of us are doing, we worked on concepts for a &lt;a class="external" href="https://gitlab.gnome.org/Teams/Design/other-app-mockups/-/blob/master/test-ride/testride.png"&gt;new app to make testing sysexts&lt;/a&gt; of merge requests (and nightly Flatpaks) easier. The working title is β€œTest Ride” (a more sustainable version of Apple’s β€œTest Flight” :P) and we had fun &lt;a class="external" href="https://mastodon.social/@jimmac/114932258937001384"&gt;drawing bicycles&lt;/a&gt; for the icon.&lt;/p&gt;
  3280. &lt;figure class="wp-caption alignnone" id="attachment_10626" style="width: 660px;"&gt;&lt;img alt="" class="size-large wp-image-10626" height="479" src="https://blogs.gnome.org/tbernard/files/2025/08/testride-1024x743.png" width="660" /&gt;&lt;figcaption class="wp-caption-text" id="caption-attachment-10626"&gt;Test Ride app mockup&lt;/figcaption&gt;&lt;/figure&gt;
  3281. &lt;p&gt;Jakub and I also worked on &lt;a class="external" href="https://gitlab.gnome.org/Teams/Design/other-app-mockups/-/blob/master/podium/podium.png"&gt;new designs for Georges’ presentation app&lt;/a&gt; Spiel (which is being rebranded to β€œPodium” to avoid the name clash with &lt;a class="external" href="https://github.com/project-spiel/libspiel"&gt;the a11y project&lt;/a&gt;). The idea is to make the app more resilient and data more future-proof, by going with a file-based approach and simple syntax on top of Markdown for (limited) layout customization.&lt;/p&gt;
  3282. &lt;figure class="wp-caption alignnone" id="attachment_10613" style="width: 660px;"&gt;&lt;img alt="" class="wp-image-10613 size-large" height="497" src="https://blogs.gnome.org/tbernard/files/2025/08/23-1024x771.jpg" width="660" /&gt;&lt;figcaption class="wp-caption-text" id="caption-attachment-10613"&gt;Georges and Jakub discussing Podium designs&lt;/figcaption&gt;&lt;/figure&gt;
  3283. &lt;h3 id="miscellaneous"&gt;Miscellaneous&lt;/h3&gt;
  3284. &lt;ul&gt;
  3285. &lt;li&gt;There was a lot of energy and excitement around GNOME OS. It feels like we’ve turned a corner, finally leaving the β€œscience experiment” stage and moving towards β€œdaily-drivable beta”.&lt;/li&gt;
  3286. &lt;li&gt;I was very happy that the community appreciation award went to Alice Mikhaylenko this year. The impact her libadwaita work has had on the growth of the app ecosystem over the past 5 years can not be overstated. Not only did she build dozens of useful and beautiful new adaptive widgets, she also has a great sense for designing APIs in a way that will get people to adopt them, which is no small thing. Kudos, very well deserved!&lt;/li&gt;
  3287. &lt;li&gt;While some of the Foundation conflicts of the past year remain unresolved, I was happy to see that Steven’s and the board’s plans are going in the right direction.&lt;/li&gt;
  3288. &lt;/ul&gt;
  3289. &lt;h3 id="brescia"&gt;Brescia&lt;/h3&gt;
  3290. &lt;p&gt;The conference was really well-organized (thanks to Pietro and the local team!), and the venue and city of Brescia had a number of advantages that were not always present at previous GUADECs:&lt;/p&gt;
  3291. &lt;ul&gt;
  3292. &lt;li&gt;The city center is small and walkable, and everyone was staying relatively close by&lt;/li&gt;
  3293. &lt;li&gt;The university is 20 min by metro from the city center, so it didn’t feel like a huge ordeal to go back and forth&lt;/li&gt;
  3294. &lt;li&gt;Multiple vegan lunch options within a few minutes walk from the university&lt;/li&gt;
  3295. &lt;li&gt;Lots of tables (with electrical outlets!) for hacking at the venue&lt;/li&gt;
  3296. &lt;li&gt;Lots of nice places for dinner/drinks outdoors in the city center&lt;/li&gt;
  3297. &lt;li&gt;Many dope ice cream places&lt;/li&gt;
  3298. &lt;/ul&gt;
  3299. &lt;figure class="wp-caption alignnone" id="attachment_10622" style="width: 660px;"&gt;&lt;img alt="" class="size-large wp-image-10622" height="495" src="https://blogs.gnome.org/tbernard/files/2025/08/12-1024x768.jpg" width="660" /&gt;&lt;figcaption class="wp-caption-text" id="caption-attachment-10622"&gt;Piazza della Loggia at sunset&lt;/figcaption&gt;&lt;/figure&gt;
  3300. &lt;p&gt;A few (minor) points that could be improved next time:&lt;/p&gt;
  3301. &lt;ul&gt;
  3302. &lt;li&gt;The timetable started veeery early every day, which contributed to a general lack of sleep. Realistically people are not going to sleep before 02:00, so starting the program at 09:00 is just too early. My experience from multi-day events in Berlin is that 12:00 is a good time to start if you want everyone to be awake :)&lt;/li&gt;
  3303. &lt;li&gt;The BoFs could have been spread out a bit more over the two days, there were slots with three parallel ones and times with nothing on the program.&lt;/li&gt;
  3304. &lt;li&gt;The venue closing at 19:00 is not ideal when people are in the zone hacking. Doesn’t have to be all night, but the option to hack until after dinner (e.g.Β 22:00) would be nice.&lt;/li&gt;
  3305. &lt;li&gt;Since that the conference is a week long accommodation can get a bit expensive, which is not ideal since most people are paying for their own travel and accommodation nowadays. It’d have been great if there was a more affordable option for accommodation, e.g. at student dorms, like at previous GUADECs.&lt;/li&gt;
  3306. &lt;li&gt;A frequent topic was how it&amp;#8217;s not ideal to have everyone be traveling and mostly unavailable for reviews a week before feature freeze. It&amp;#8217;s also not ideal because any plans you make at GUADEC are not going to make it into the September release, but will have to wait for March. What if the conference was closer to the beginning of the cycle, e.g. in May or June?&lt;/li&gt;
  3307. &lt;/ul&gt;
  3308. &lt;p&gt;A few more random photos:&lt;/p&gt;
  3309. &lt;figure class="wp-caption alignnone" id="attachment_10615" style="width: 660px;"&gt;&lt;img alt="" class="size-large wp-image-10615" height="497" src="https://blogs.gnome.org/tbernard/files/2025/08/2-1024x771.jpg" width="660" /&gt;&lt;figcaption class="wp-caption-text" id="caption-attachment-10615"&gt;Matthias showing us fancy new dynamic icon stuff&lt;/figcaption&gt;&lt;/figure&gt;
  3310. &lt;figure class="wp-caption alignnone" id="attachment_10616" style="width: 660px;"&gt;&lt;img alt="" class="wp-image-10616 size-large" height="495" src="https://blogs.gnome.org/tbernard/files/2025/08/5-1024x768.jpg" width="660" /&gt;&lt;figcaption class="wp-caption-text" id="caption-attachment-10616"&gt;Dinner on the first night feat. yours truly, Robert, Jordan, Antonio, Maximiliano, Sam, Javier, Julian, Adrian, Markus, Adrien, and Andreas&lt;/figcaption&gt;&lt;/figure&gt;
  3311. &lt;figure class="wp-caption alignnone" id="attachment_10625" style="width: 660px;"&gt;&lt;img alt="" class="size-large wp-image-10625" height="877" src="https://blogs.gnome.org/tbernard/files/2025/08/7-771x1024.jpg" width="660" /&gt;&lt;figcaption class="wp-caption-text" id="caption-attachment-10625"&gt;Adrian and Javier having an ad-hoc Buildstream BoF at the pizzeria&lt;/figcaption&gt;&lt;/figure&gt;
  3312. &lt;figure class="wp-caption alignnone" id="attachment_10617" style="width: 660px;"&gt;&lt;img alt="" class="wp-image-10617 size-large" height="497" src="https://blogs.gnome.org/tbernard/files/2025/08/15-1024x771.jpg" width="660" /&gt;&lt;figcaption class="wp-caption-text" id="caption-attachment-10617"&gt;Robert and Maximiliano hacking on Snapshot&lt;/figcaption&gt;&lt;/figure&gt;</description><author>Tobias Bernard</author><dc:creator>Tobias Bernard</dc:creator><pubDate>Sun, 03 Aug 2025 10:27:14 GMT</pubDate><guid isPermaLink="true">https://blogs.gnome.org/tbernard/2025/08/03/guadec-2025/</guid></item><item><title>Daiki Ueno: Optimizing CI resource usage in upstream projects</title><link>https://blogs.gnome.org/dueno/optimizing-ci-resource-usage-in-upstream-projects/</link><description>&lt;p&gt;At &lt;a class="external" href="https://www.gnutls.org/"&gt;GnuTLS&lt;/a&gt;, our journey into optimizing GitLab CI began when we faced a significant challenge: we lost our GitLab.com Open Source Program subscription. While we are still hoping that this limitation is &lt;a class="external" href="https://gitlab.com/gitlab-org/gitlab/-/issues/543742"&gt;temporary&lt;/a&gt;, this meant our available CI/CD resources became considerably lower. We took this opportunity to find smarter ways to manage our pipelines and reduce our footprint.&lt;/p&gt;
  3313. &lt;p&gt;This blog post shares the strategies we employed to optimize our GitLab CI usage, focusing on reducing running time and network resources, which are crucial for any open-source project operating with constrained resources.&lt;/p&gt;
  3314. &lt;p&gt;&lt;span id="more-719"&gt;&lt;/span&gt;&lt;/p&gt;
  3315. &lt;h3&gt;CI on every PR: a best practice, but not cheap&lt;/h3&gt;
  3316. &lt;p&gt;While running CI on every commit is considered a best practice for secure software development, our experience &lt;a class="external" href="https://opensource.com/article/23/3/podman-gitlab-runners"&gt;setting up&lt;/a&gt; a self-hosted GitLab runner on a modest Virtual Private Server (VPS) highlighted its cost implications, especially with limited resources. We provisioned a VPS with 2GB of memory and 3 CPU cores, intending to support our GnuTLS CI pipelines.&lt;/p&gt;
  3317. &lt;p&gt;The reality, however, was a stark reminder of the resource demands. A single CI pipeline for GnuTLS took an excessively long time to complete, often stretching beyond acceptable durations. Furthermore, the extensive data transfer involved in fetching container images, dependencies, building artifacts, and pushing results quickly led us to reach the bandwidth limits imposed by our VPS provider, resulting in throttled connections and further delays.&lt;/p&gt;
  3318. &lt;p&gt;This experience underscored the importance of balancing CI best practices with available infrastructure and budget, particularly for resource-intensive projects.&lt;/p&gt;
  3319. &lt;h3&gt;Reducing CI running time&lt;/h3&gt;
  3320. &lt;p&gt;Efficient CI pipeline execution is paramount, especially when resources are scarce. GitLab provides an excellent &lt;a class="external" href="https://docs.gitlab.com/ci/pipelines/pipeline_efficiency/"&gt;article&lt;/a&gt; on pipeline efficiency, though in practice, project specific optimization is needed. We focused on three key areas to achieve faster pipelines:&lt;/p&gt;
  3321. &lt;ul&gt;
  3322. &lt;li&gt;Tiering tests&lt;/li&gt;
  3323. &lt;li&gt;Layering container images&lt;/li&gt;
  3324. &lt;li&gt;De-duplicating build artifacts&lt;/li&gt;
  3325. &lt;/ul&gt;
  3326. &lt;h4&gt;Tiering tests&lt;/h4&gt;
  3327. &lt;p&gt;Not all tests need to run on every PR. For more exotic or costly tasks, such as extensive fuzzing, generating documentation, or large-scale integration tests, we adopted a tiering approach. These types of tests are resource-intensive and often provide value even when run less frequently. Instead of scheduling them for every PR, they are triggered manually or on a periodic basis (e.g., nightly or weekly builds). This ensures that critical daily development workflows remain fast and efficient, while still providing comprehensive testing coverage for the project without incurring excessive resource usage on every minor change.&lt;/p&gt;
  3328. &lt;h4&gt;Layering container images&lt;/h4&gt;
  3329. &lt;p&gt;The tiering of tests gives us an idea which CI images are more commonly used in the pipeline. For those common CI images, we transitioned to using a more minimal base container image, such as &lt;code&gt;fedora-minimal&lt;/code&gt; or &lt;code&gt;debian:&amp;lt;flavor&amp;gt;-slim&lt;/code&gt;. This reduced the initial download size and the overall footprint of our build environment.&lt;/p&gt;
  3330. &lt;p&gt;For specialized tasks, such as generating documentation or running cross-compiled tests that require additional tools, we adopted a layering approach. Instead of building a monolithic image with all possible dependencies, we created dedicated, smaller images for these specific purposes and layered them on top of our minimal base image as needed within the CI pipeline. This modular approach ensures that only the necessary tools are present for each job, minimizing unnecessary overhead.&lt;/p&gt;
  3331. &lt;h4&gt;De-duplicating build artifacts&lt;/h4&gt;
  3332. &lt;p&gt;Historically, our CI pipelines involved many &amp;#8220;configure &amp;amp;&amp;amp; make&amp;#8221; steps for various options. One of the major culprits of long build times is repeatedly compiling source code, oftentimes resulting in almost identical results.&lt;/p&gt;
  3333. &lt;p&gt;We realized that many of these compile-time options could be handled at runtime. By moving configurations that didn&amp;#8217;t fundamentally alter the core compilation process to runtime, we simplified our build process and reduced the number of compilation steps required. This approach transforms a lengthy compile-time dependency into a quicker runtime check.&lt;/p&gt;
  3334. &lt;p&gt;Of course, this approach cuts both ways: while it simplifies the compilation process, it could increase the code size and attack surface. For example, support for legacy protocol features such as SSL 3.0 or SHA-1 that may lower the entire security should still be able to be switched off at the compile time.&lt;/p&gt;
  3335. &lt;p&gt;Another caveat is that some compilation options are inherently incompatible with each other. One example is that thread sanitizer cannot be enabled with address sanitizer at the same time. In such cases a separate build artifact is still needed.&lt;/p&gt;
  3336. &lt;h3&gt;The impact: tangible results&lt;/h3&gt;
  3337. &lt;p&gt;The efforts put into optimizing our GitLab CI configuration yielded significant benefits:&lt;/p&gt;
  3338. &lt;ul&gt;
  3339. &lt;li&gt;The size of the container image used for our standard build jobs is now &lt;strong&gt;2.5GB&lt;/strong&gt; smaller than before. This substantial reduction in image size translates to faster job startup times and reduced storage consumption on our runners.&lt;/li&gt;
  3340. &lt;li&gt;&lt;strong&gt;9 &amp;#8220;configure &amp;amp;&amp;amp; make&amp;#8221;&lt;/strong&gt; steps were removed from our standard build jobs. This streamlined the build process and directly contributed to faster execution times.&lt;/li&gt;
  3341. &lt;/ul&gt;
  3342. &lt;p&gt;By implementing these strategies, we not only adapted to our reduced resources but also built a more efficient, cost-effective, and faster CI/CD pipeline for the GnuTLS project. These optimizations highlight that even small changes can lead to substantial improvements, especially in the context of open-source projects with limited resources.&lt;/p&gt;
  3343. &lt;p&gt;For further information on this, please consult the actual &lt;a class="external" href="https://gitlab.com/gnutls/gnutls/-/merge_requests/1991"&gt;changes&lt;/a&gt;.&lt;/p&gt;
  3344. &lt;h3&gt;Next steps&lt;/h3&gt;
  3345. &lt;p&gt;While the current optimizations have significantly improved our CI efficiency, we are continuously exploring further enhancements. Our future plans include:&lt;/p&gt;
  3346. &lt;ul&gt;
  3347. &lt;li&gt;&lt;strong&gt;Distributed GitLab runners with external cache:&lt;/strong&gt; To further scale and improve resource utilization, we are considering running GitLab runners on multiple VPS instances. To coordinate these distributed runners and avoid redundant data transfers, we could set up an external cache, potentially using a solution like &lt;a class="external" href="https://www.min.io/"&gt;MinIO&lt;/a&gt;. This would allow shared access to build artifacts, reducing bandwidth consumption and build times.&lt;/li&gt;
  3348. &lt;li&gt;&lt;strong&gt;Addressing flaky tests:&lt;/strong&gt; Flaky tests, which intermittently pass or fail without code changes, are a major bottleneck in any CI pipeline. They not only consume valuable CI resources by requiring entire jobs to be rerun but also erode developer confidence in the test suite. In TLS testing, it is common to write a test script that sets up a server and a client as a separate process, let the server bind a unique port to which the client connects, and instruct the client to initiate a certain event through a control channel. This kind of test could fail in many ways regardless of the test itself, e.g., the port might be already used by other tests. Therefore, rewriting tests without requiring a complex setup would be a good first step.&lt;/li&gt;
  3349. &lt;/ul&gt;</description><author>Daiki Ueno</author><dc:creator>Daiki Ueno</dc:creator><pubDate>Sun, 03 Aug 2025 02:22:45 GMT</pubDate><guid isPermaLink="true">https://blogs.gnome.org/dueno/optimizing-ci-resource-usage-in-upstream-projects/</guid></item><item><title>Jonathan Blandford: GUADEC 2025: Thoughts and Reflections</title><link>https://blogs.gnome.org/jrb/2025/08/02/guadec-2025-thoughts-and-reflections/</link><description>&lt;p style="text-align: left;"&gt;Another year, another GUADEC. This was the 25th anniversary of the first GUADEC, and the 25th one I&amp;#8217;ve gone to. Although there have been multiple bids for Italy during the past quarter century, this was the first successful one. It was definitely worth the wait, as it was one of the best GUADECs in recent memory.&lt;/p&gt;
  3350. &lt;figure class="wp-caption aligncenter" id="attachment_7612" style="width: 300px;"&gt;&lt;a href="https://blogs.gnome.org/jrb/files/2025/08/PXL_20250726_2029029132-scaled.jpg"&gt;&lt;img alt="A birthday cake with sparklers stands next to a sign saying GUADEC 2025" class="wp-image-7612 size-medium" height="218" src="https://blogs.gnome.org/jrb/files/2025/08/PXL_20250726_2029029132-300x218.jpg" width="300" /&gt;&lt;/a&gt;&lt;figcaption class="wp-caption-text" id="caption-attachment-7612"&gt;GUADEC&amp;#8217;s 25th anniversary cake&lt;/figcaption&gt;&lt;/figure&gt;
  3351. &lt;p&gt;This was an extremely smooth conference β€” way smoother than previous years. The staff and volunteers really came through in a big way and did heroic work! I watched Deepesha, Asmit, Aryan, Maria, Zana, Kristi, Anisa, and especially Pietro all running around making this conference happen. I&amp;#8217;m super grateful for their continued hard work in the project. GNOME couldn&amp;#8217;t happen without their effort.&lt;/p&gt;
  3352. &lt;figure class="wp-caption aligncenter" id="attachment_7620" style="width: 300px;"&gt;&lt;a href="https://blogs.gnome.org/jrb/files/2025/08/IMG-20250801-WA0006.jpg"&gt;&lt;img alt="A brioche and espresso cup sit on a table" class="wp-image-7620 size-medium" height="177" src="https://blogs.gnome.org/jrb/files/2025/08/IMG-20250801-WA0006-300x177.jpg" width="300" /&gt;&lt;/a&gt;&lt;figcaption class="wp-caption-text" id="caption-attachment-7620"&gt;La GNOME vita&lt;/figcaption&gt;&lt;/figure&gt;
  3353. &lt;h2&gt;Favorite Talks&lt;/h2&gt;
  3354. &lt;p&gt;I commented on some talks as they happened (&lt;a class="external" href="https://mastodon.cloud/@blandford/114906925353105047"&gt;Day 1&lt;/a&gt;, &lt;a class="external" href="https://mastodon.cloud/@blandford/114912680108928267"&gt;Day 2&lt;/a&gt;, &lt;a class="external" href="https://mastodon.cloud/@blandford/114918662794657572"&gt;Day 3&lt;/a&gt;). I could only attend one track at a time so missed a lot of them, but the talks I saw were fabulous. They were much higher quality than usual this year, and I&amp;#8217;m really impressed at this community&amp;#8217;s creativity and knowledge. As I said, it really was a strong conference.&lt;/p&gt;
  3355. &lt;p&gt;I did an informal poll of assorted attendees I ran into on the streets of Brescia on the last night, asking what their favorite talks were. Here are the results:&lt;/p&gt;
  3356. &lt;figure class="wp-caption aligncenter" id="attachment_7613" style="width: 282px;"&gt;&lt;a href="https://blogs.gnome.org/jrb/files/2025/08/whither-maintainers.jpg"&gt;&lt;img alt="Emmanuele standing in front of a slide that says &amp;quot;Whither Maintainers&amp;quot;" class="wp-image-7613 size-medium" height="300" src="https://blogs.gnome.org/jrb/files/2025/08/whither-maintainers-282x300.jpg" width="282" /&gt;&lt;/a&gt;&lt;figcaption class="wp-caption-text" id="caption-attachment-7613"&gt;Whither Maintainers&lt;/figcaption&gt;&lt;/figure&gt;
  3357. &lt;ul&gt;
  3358. &lt;li&gt;&lt;a class="external" href="https://www.youtube.com/watch?v=18Ir6RXkIeA&amp;amp;t=3378s"&gt;&lt;strong&gt;Emmanuele&amp;#8217;s talk on &amp;#8220;Getting Things Done In GNOME&amp;#8221;:&lt;/strong&gt;&lt;/a&gt; This talk clearly struck a chord amongst attendees. He proposed a path forward on the technical governance of the project. It also had a &amp;#8220;Wither Maintainers&amp;#8221; slide that lead to a lot of great conversations.&lt;/li&gt;
  3359. &lt;li&gt;&lt;a class="external" href="https://www.youtube.com/watch?v=4BsL0VyCoWs&amp;amp;t=1949s"&gt;&lt;strong&gt;George&amp;#8217;s talk on streaming:&lt;/strong&gt;&lt;/a&gt; This was very personal, very brave, and extremely inspiring. I left the talk wanting to try my hand at live streaming my coding sessions, and I&amp;#8217;m not the only one.&lt;/li&gt;
  3360. &lt;li&gt;&lt;a class="external" href="https://youtu.be/Z7F3fghCQB4?t=27367"&gt;&lt;strong&gt;The poop talk, by Niels:&lt;/strong&gt;&lt;/a&gt; This was a very entertaining lightning talk with a really important message at the end.&lt;/li&gt;
  3361. &lt;li&gt;&lt;a class="external" href="https://www.youtube.com/watch?v=-xEhHObnCug&amp;amp;t=170s"&gt;&lt;strong&gt;Enhancing Screen Reader Functionality in Modern Gnome by Lukas:&lt;/strong&gt;&lt;/a&gt; Unfortunately, I was at the other track when this one happened so I don&amp;#8217;t know much about it. I&amp;#8217;ll have to go back and watch it! That being said, it was so inspiring to see how many people at GUADEC were working on accessibility, and how much progress has been made across the board. I&amp;#8217;m in awe of everyone that works in this space.&lt;/li&gt;
  3362. &lt;/ul&gt;
  3363. &lt;h3&gt;Honorable mentions&lt;/h3&gt;
  3364. &lt;ul&gt;
  3365. &lt;li&gt;&lt;a class="external" href="https://www.youtube.com/watch?v=4BsL0VyCoWs&amp;amp;t=0s"&gt;&lt;strong&gt;Emmanuele on Fixing GObject: &lt;/strong&gt;&lt;/a&gt;This was an unexpected talk. He started by proposing something pretty outlandish, and by the end of the talk it seemed not only plausible, but likely.&lt;/li&gt;
  3366. &lt;li&gt;&lt;a class="external" href="https://youtu.be/18Ir6RXkIeA?t=26797"&gt;&lt;strong&gt;Logan&amp;#8217;s lightning talk about writing apps using GTK Inspector:&lt;/strong&gt;&lt;/a&gt; Just plain fun.&lt;/li&gt;
  3367. &lt;li&gt;&lt;strong&gt;Martin&amp;#8217;s BOF on &lt;a class="external" href="https://github.com/tchx84/Gameeky"&gt;Gameeky&lt;/a&gt;:&lt;/strong&gt; Totally unhinged, totally out of control, totally entertaining. I have no idea what this game&amp;#8217;s final form will be, but I&amp;#8217;m enjoying watching it evolve.&lt;/li&gt;
  3368. &lt;li&gt;&lt;a class="external" href="https://www.youtube.com/watch?v=4BsL0VyCoWs&amp;amp;t=10292s"&gt;&lt;strong&gt;Florian on hacking the shell: &lt;/strong&gt;&lt;/a&gt;This presented a nice setup to make it easier to develop against the shell. Given how important extensions are to our ecosystem, this could help a lot.&lt;/li&gt;
  3369. &lt;/ul&gt;
  3370. &lt;p&gt;In reality, there were many amazing talks beyond the ones I listed. I highly recommend you go back and see them. I know I&amp;#8217;m planning on it!&lt;/p&gt;
  3371. &lt;h2&gt;Crosswords at GUADEC&lt;/h2&gt;
  3372. &lt;figure class="wp-caption aligncenter" id="attachment_7615" style="width: 300px;"&gt;&lt;a href="https://blogs.gnome.org/jrb/files/2025/08/crosswords-guadec-2025.jpg"&gt;&lt;img alt="" class="size-medium wp-image-7615" height="300" src="https://blogs.gnome.org/jrb/files/2025/08/crosswords-guadec-2025-300x300.jpg" width="300" /&gt;&lt;/a&gt;&lt;figcaption class="wp-caption-text" id="caption-attachment-7615"&gt;Refactoring gnome-crosswords&lt;/figcaption&gt;&lt;/figure&gt;
  3373. &lt;p&gt;We didn&amp;#8217;t have a Crosswords update talk this cycle. However we still had some appearances worth mentioning:&lt;/p&gt;
  3374. &lt;ul&gt;
  3375. &lt;li&gt;Federico &lt;a class="external" href="https://www.youtube.com/watch?v=18Ir6RXkIeA&amp;amp;t=20285s&amp;amp;pp=0gcJCTAAlc8ueATH"&gt;gave a talk about how to use unidirectional programming to add tests to your application&lt;/a&gt;, and used Crosswords as his example. This probably the 5th time one of the two of us have talked about this topic. This was the best one to date, though we keep giving it because we don&amp;#8217;t think we&amp;#8217;ve gotten the explanation right. It&amp;#8217;s a complex architectural change which has a lot of nuance, and is hard for us to explain succinctly. Nevertheless, we keep trying, as we see how this could lead to a big revolution in the quality of GNOME applications. Crosswords is pushing 80KLOC, and this architecture is the only thing allowing us to keep it at a reasonable quality.&lt;/li&gt;
  3376. &lt;li&gt;People keep thinking this is &amp;#8220;just&amp;#8221; MVC, or a minor variation thereof, but it&amp;#8217;s different enough that it requires a new mindset, a disciplined approach, and a good data model. As an added twist, Federico sent an &lt;a class="external" href="https://gitlab.gnome.org/GNOME/gnome-calendar/-/merge_requests/550"&gt;MR to GNOME Calendar&lt;/a&gt; to add initial unidirectional support to that application. If crosswords are too obscure a subject for you, then maybe the calendar section of the talk will help you understand it.&lt;/li&gt;
  3377. &lt;li&gt;&lt;a class="external" href="https://youtu.be/Z7F3fghCQB4?t=26213"&gt;I gave a lighting talk&lt;/a&gt; about some of the awesome work that our GSoC (and prospective GSoC) students are doing.&lt;/li&gt;
  3378. &lt;li&gt;I gave a BOF on Words. It was lightly attended, but led to a good conversation with the always-helpful Martin.&lt;/li&gt;
  3379. &lt;li&gt;Finally, I sat down with Tobias to do a UX review of the crossword game, with an eye to getting it into GNOME Circle. This has been in the works for a long-time and I&amp;#8217;m really grateful for the chance to do it in person. We identified many &lt;a class="external" href="https://gitlab.gnome.org/jrb/crosswords/-/issues/?sort=created_date&amp;amp;state=opened&amp;amp;label_name%5B%5D=GNOME%20Circle%20Blocker"&gt;papercuts to fix&lt;/a&gt; of course, but Tobias was also able to provide a suggestion to improve a long-standing problem with the interface. We sketched out a &lt;a class="external" href="https://gitlab.gnome.org/jrb/crosswords/-/issues/304"&gt;potential redesign&lt;/a&gt; that I&amp;#8217;m really happy with. I hope the Circle team is able to continue to do reviews across the GNOME ecosystem as it provides so much value.&lt;/li&gt;
  3380. &lt;/ul&gt;
  3381. &lt;h2&gt;Personal&lt;/h2&gt;
  3382. &lt;p&gt;A final comment: I&amp;#8217;m reminded again of how much of a family the GNOME community is. For personal reasons, it was a very tough GUADEC for Zana and I. It was objectively a fabulous GUADEC and we really wanted to enjoy it, but couldn&amp;#8217;t. We&amp;#8217;re humbled at the support and love this community is capable of. Thank you.&lt;/p&gt;</description><author>Jonathan Blandford</author><dc:creator>Jonathan Blandford</dc:creator><pubDate>Sat, 02 Aug 2025 19:35:25 GMT</pubDate><guid isPermaLink="true">https://blogs.gnome.org/jrb/2025/08/02/guadec-2025-thoughts-and-reflections/</guid></item><item><title>Hubert Figuière: Dev Log July 2025</title><link>https://www.figuiere.net/hub/wlog/dev-log-july-2025/</link><description>&lt;h2 id="abiword"&gt;AbiWord&lt;/h2&gt;
  3383. &lt;p&gt;Working on rebasing and finishing an &amp;quot;old&amp;quot; patch from Michael Gorse
  3384. that implement accessibility in AbiWord. While the patch is a few
  3385. years old, it's perfectly rebasable.&lt;/p&gt;
  3386. &lt;p&gt;Pushed a lot of code modernisation on master, as well as various
  3387. memory leaks and crashes on stable.&lt;/p&gt;
  3388. &lt;p&gt;Released 3.0.7 (tag, and flatpak build). 3.0.8 might come soon as I'm
  3389. backporting more crashers that are already fixed on master. Until I
  3390. have a 3.1.90 version ready for testing.&lt;/p&gt;
  3391. &lt;h2 id="libopenraw"&gt;libopenraw&lt;/h2&gt;
  3392. &lt;p&gt;Finally I have Fujifilm X-Trans demosaicking, which needs more work as
  3393. it is still a crude port of the dcraw C code. A also apply the white
  3394. balance. Added few new cameras and some benchmarks.&lt;/p&gt;
  3395. &lt;p&gt;Finally I released 0.4.0-alpha.11.&lt;/p&gt;
  3396. &lt;p&gt;Also I did update libopenraw-view which is my GUI for testing
  3397. libopenraw. It now renders asynchronously.&lt;/p&gt;
  3398. &lt;h2 id="supporting-cast"&gt;Supporting cast&lt;/h2&gt;
  3399. &lt;p&gt;Some other various stuff.&lt;/p&gt;
  3400. &lt;h3 id="glycin"&gt;glycin&lt;/h3&gt;
  3401. &lt;p&gt;The &lt;a href="https://gitlab.gnome.org/GNOME/glycin/-/merge_requests/230"&gt;merge request to update
  3402. libopenraw&lt;/a&gt;
  3403. was merged. Thanks to Sophie!&lt;/p&gt;
  3404. &lt;h3 id="gegl-rs"&gt;gegl-rs&lt;/h3&gt;
  3405. &lt;p&gt;Updated gegl-rs to the new glib-rs.&lt;/p&gt;
  3406. &lt;h3 id="lrcat-extractor"&gt;lrcat-extractor&lt;/h3&gt;
  3407. &lt;p&gt;Released a new version after updating rusqlite.&lt;/p&gt;
  3408. &lt;h2 id="niepce"&gt;Niepce&lt;/h2&gt;
  3409. &lt;p&gt;Ported to the latest gtk4-rs, hence the update to gegl-rs. Some small
  3410. API changes in the object subclassing needed to be handled.&lt;/p&gt;</description><author>Hubert Figuière</author><dc:creator>Hubert Figuière</dc:creator><pubDate>Fri, 01 Aug 2025 00:00:00 GMT</pubDate><guid isPermaLink="true">https://www.figuiere.net/hub/wlog/dev-log-july-2025/</guid></item><item><title>Matthew Garrett: Secure boot certificate rollover is real but probably won't hurt you</title><link>https://mjg59.dreamwidth.org/72892.html</link><description>LWN wrote &lt;a href="https://lwn.net/Articles/1029767/"&gt;an article&lt;/a&gt; which opens with the assertion "Linux users who have Secure Boot enabled on their systems knowingly or unknowingly rely on a key from Microsoft that is set to expire in September". This is, depending on interpretation, either misleading or just plain wrong, but also there's not a good source of truth here, so.&lt;br /&gt;&lt;br /&gt;First, how does secure boot signing work? Every system that supports UEFI secure boot ships with a set of trusted certificates in a database called "db". Any binary signed with a chain of certificates that chains to a root in db is trusted, unless either the binary (via hash) or an intermediate certificate is added to "dbx", a separate database of things whose trust has been revoked[1]. But, in general, the firmware doesn't care about the intermediate or the number of intermediates or whatever - as long as there's a valid chain back to a certificate that's in db, it's going to be happy.&lt;br /&gt;&lt;br /&gt;That's the conceptual version. What about the real world one? Most x86 systems that implement UEFI secure boot have at least two root certificates in db - one called "Microsoft Windows Production PCA 2011", and one called "Microsoft Corporation UEFI CA 2011". The former is the root of a chain used to sign the Windows bootloader, and the latter is the root used to sign, well, everything else.&lt;br /&gt;&lt;br /&gt;What is "everything else"? For people in the Linux ecosystem, the most obvious thing is the Shim bootloader that's used to bridge between the Microsoft root of trust and a given Linux distribution's root of trust[2]. But that's not the only third party code executed in the UEFI environment. Graphics cards, network cards, RAID and iSCSI cards and so on all tend to have their own unique initialisation process, and need board-specific drivers. Even if you added support for everything on the market to your system firmware, a system built last year wouldn't know how to drive a graphics card released this year. Cards need to provide their own drivers, and these drivers are stored in flash on the card so they can be updated. But since UEFI doesn't have any sandboxing environment, those drivers could do pretty much anything they wanted to. Someone could compromise the UEFI secure boot chain by just plugging in a card with a malicious driver on it, and have that hotpatch the bootloader and introduce a backdoor into your kernel.&lt;br /&gt;&lt;br /&gt;This is avoided by enforcing secure boot for these drivers as well. Every plug-in card that carries its own driver has it signed by Microsoft, and up until now that's been a certificate chain going back to the same "Microsoft Corporation UEFI CA 2011" certificate used in signing Shim. This is important for reasons we'll get to.&lt;br /&gt;&lt;br /&gt;The "Microsoft Windows Production PCA 2011" certificate expires in October 2026, and the "Microsoft Corporation UEFI CA 2011" one in June 2026. These dates are not that far in the future! Most of you have probably at some point tried to visit a website and got an error message telling you that the site's certificate had expired and that it's no longer trusted, and so it's natural to assume that the outcome of time's arrow marching past those expiry dates would be that systems will stop booting. Thankfully, that's not what's going to happen.&lt;br /&gt;&lt;br /&gt;First up: if you grab a copy of the Shim currently shipped in Fedora and extract the certificates from it, you'll learn it's not directly signed with the "Microsoft Corporation UEFI CA 2011" certificate. Instead, it's signed with a "Microsoft Windows UEFI Driver Publisher" certificate that chains to the "Microsoft Corporation UEFI CA 2011" certificate. That's not unusual, intermediates are commonly used and rotated. But if we look more closely at that certificate, we learn that it was issued in 2023 and expired in 2024. Older versions of Shim were signed with older intermediates. A very large number of Linux systems are already booting certificates that have expired, and yet things keep working. Why?&lt;br /&gt;&lt;br /&gt;Let's talk about time. In the ways we care about in this discussion, time is a social construct rather than a meaningful reality. There's no way for a computer to observe the state of the universe and know what time it is - it needs to be told. It has no idea whether that time is accurate or an elaborate fiction, and so it can't with any degree of certainty declare that a certificate is valid from an external frame of reference. The failure modes of getting this wrong are also extremely bad! If a system has a GPU that relies on an option ROM, and if you stop trusting the option ROM because either its certificate has genuinely expired or because your clock is wrong, you can't display any graphical output[3] and the user can't fix the clock and, well, crap.&lt;br /&gt;&lt;br /&gt;The upshot is that nobody actually enforces these expiry dates - &lt;a href="https://github.com/tianocore/edk2/blob/edk2-stable202505/CryptoPkg/Library/BaseCryptLib/Pk/CryptPkcs7VerifyCommon.c#L893-L900"&gt;here's the reference code that disables it&lt;/a&gt;. In a year's time we'll have gone past the expiration date for "Microsoft Windows UEFI Driver Publisher" and everything will still be working, and a few months later "Microsoft Windows Production PCA 2011" will also expire and systems will keep booting Windows despite being signed with a now-expired certificate. This isn't a Y2K scenario where everything keeps working because people have done a huge amount of work - it's a situation where everything keeps working even if nobody does any work.&lt;br /&gt;&lt;br /&gt;So, uh, what's the story here? Why is there any engineering effort going on at all? What's all this talk of new certificates? Why are there sensationalist pieces about how Linux is going to stop working on old computers or new computers or maybe all computers?&lt;br /&gt;&lt;br /&gt;Microsoft will shortly start signing things with a new certificate that chains to a new root, and most systems don't trust that new root. System vendors are supplying updates[4] to their systems to add the new root to the set of trusted keys, and Microsoft has supplied a fallback that can be applied to all systems even without vendor support[5]. If something is signed purely with the new certificate then it won't boot on something that only trusts the old certificate (which shouldn't be a realistic scenario due to the above), but if something is signed purely with the old certificate then it won't boot on something that only trusts the new certificate.&lt;br /&gt;&lt;br /&gt;How meaningful a risk is this? We don't have an explicit statement from Microsoft as yet as to what's going to happen here, but we expect that there'll be at least a period of time where Microsoft signs binaries with both the old and the new certificate, and in that case those objects should work just fine on both old and new computers. The problem arises if Microsoft stops signing things with the old certificate, at which point new releases will stop booting on systems that don't trust the new key (which, again, shouldn't happen). But even if that does turn out to be a problem, nothing is going to force Linux distributions to stop using existing Shims signed with the old certificate, and having a Shim signed with an old certificate does nothing to stop distributions signing new versions of grub and kernels. In an ideal world we have no reason to ever update Shim[6] and so we just keep on shipping one signed with two certs.&lt;br /&gt;&lt;br /&gt;If there's a point in the future where Microsoft &lt;em&gt;only&lt;/em&gt; signs with the new key, and if we were to somehow end up in a world where systems only trust the old key and not the new key[7], then those systems wouldn't boot with new graphics cards, wouldn't be able to run new versions of Windows, wouldn't be able to run any Linux distros that ship with a Shim signed only with the new certificate. That would be bad, but we have a mechanism to avoid it. On the other hand, systems that only trust the new certificate and not the old one would refuse to boot older Linux, wouldn't support old graphics cards, and also wouldn't boot old versions of Windows. Nobody wants that, and for the foreseeable future we're going to see new systems continue trusting the old certificate and old systems have updates that add the new certificate, and everything will just continue working exactly as it does now.&lt;br /&gt;&lt;br /&gt;Conclusion: Outside some corner cases, the worst case is you might need to boot an old Linux to update your trusted keys to be able to install a new Linux, and no computer currently running Linux will break in any way whatsoever.&lt;br /&gt;&lt;br /&gt;[1] (there's also a separate revocation mechanism called SBAT which I wrote about &lt;a href="https://mjg59.dreamwidth.org/70348.html"&gt;here&lt;/a&gt;, but it's not relevant in this scenario)&lt;br /&gt;&lt;br /&gt;[2] Microsoft won't sign GPLed code for reasons I think are unreasonable, so having them sign grub was a non-starter, but also the point of Shim was to allow distributions to have something that doesn't change often and be able to sign their own bootloaders and kernels and so on without having to have Microsoft involved, which means grub and the kernel can be updated without having to ask Microsoft to sign anything and updates can be pushed without any additional delays&lt;br /&gt;&lt;br /&gt;[3] It's been a &lt;em&gt;long&lt;/em&gt; time since graphics cards booted directly into a state that provided any well-defined programming interface. Even back in 90s, cards didn't present VGA-compatible registers until card-specific code had been executed (hence DEC Alphas having an x86 emulator in their firmware to run the driver on the card). No driver? No video output.&lt;br /&gt;&lt;br /&gt;[4] There's a UEFI-defined mechanism for updating the keys that doesn't require a full firmware update, and it'll work on all devices that use the same keys rather than being per-device&lt;br /&gt;&lt;br /&gt;[5] Using the generic update without a vendor-specific update means it wouldn't be possible to issue further updates for the next key rollover, or any additional revocation updates, but I'm hoping to be retired by then and I hope all these computers will also be retired by then&lt;br /&gt;&lt;br /&gt;[6] I said this in 2012 and it turned out to be wrong then so it's probably wrong now sorry, but at least SBAT means we can revoke vulnerable grubs without having to revoke Shim&lt;br /&gt;&lt;br /&gt;[7] Which shouldn't happen! There's an update to add the new key that should work on all PCs, but there's always the chance of firmware bugs&lt;br /&gt;&lt;br /&gt;&lt;img alt="comment count unavailable" height="12" src="https://www.dreamwidth.org/tools/commentcount?user=mjg59&amp;amp;ditemid=72892" style="vertical-align: middle;" width="30" /&gt; comments</description><author>Matthew Garrett</author><dc:creator>Matthew Garrett</dc:creator><pubDate>Thu, 31 Jul 2025 16:12:59 GMT</pubDate><guid isPermaLink="true">https://mjg59.dreamwidth.org/72892.html</guid></item><item><title>Alley Chaggar: Challenges</title><link>https://alleych.github.io/gnome/challenges/</link><description>&lt;h1 id="debugging-and-my-challenges"&gt;Debugging and My Challenges&lt;/h1&gt;
  3411.  
  3412. &lt;p&gt;For the past two weeks, I’ve been debugging the &lt;a href="https://gitlab.gnome.org/AlleyChaggar/vala/-/blob/098c51eb28c99d4d9fa4786d84109782fe8cf2c3/codegen/valajsonmodule.vala"&gt;json module&lt;/a&gt;. I hooked up the JSON module into the codebase hierarchy by modifying &lt;a href="https://gitlab.gnome.org/AlleyChaggar/vala/-/blob/main/codegen/valagsignalmodule.vala?ref_type=heads"&gt;valagsignalmodule.vala&lt;/a&gt; to extend the JSON module, which, before extended the &lt;a href="https://gitlab.gnome.org/GNOME/vala/-/blob/main/codegen/valagobjectmodule.vala?ref_type=heads"&gt;GObject module&lt;/a&gt;. Running the test case called &lt;a href="https://gitlab.gnome.org/AlleyChaggar/vala/-/blob/alley/json-glib-module/tests/annotations/json.vala?ref_type=heads"&gt;json.vala&lt;/a&gt;, crashes the program.
  3413. &lt;br /&gt;&lt;/p&gt;
  3414.  
  3415. &lt;p&gt;In the beginning, I was having quite the difficulty trying to use &lt;code class="language-vala highlighter-rouge"&gt;&lt;span class="n"&gt;gdb&lt;/span&gt;&lt;/code&gt; and &lt;code class="language-vala highlighter-rouge"&gt;&lt;span class="n"&gt;coredumpctl&lt;/span&gt;&lt;/code&gt; to investigate the crash. I kept doing:&lt;/p&gt;
  3416.  
  3417. &lt;div class="language-vala highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="p"&gt;./&lt;/span&gt;&lt;span class="n"&gt;autogen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sh&lt;/span&gt; &lt;span class="p"&gt;--&lt;/span&gt;&lt;span class="n"&gt;enable&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="n"&gt;debug&lt;/span&gt;
  3418. &lt;span class="n"&gt;make&lt;/span&gt;
  3419. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
  3420. &lt;p&gt;/&lt;/p&gt;
  3421. &lt;div class="language-vala highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="p"&gt;./&lt;/span&gt;&lt;span class="n"&gt;configure&lt;/span&gt; &lt;span class="p"&gt;--&lt;/span&gt;&lt;span class="n"&gt;enable&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="n"&gt;debug&lt;/span&gt;
  3422. &lt;span class="n"&gt;make&lt;/span&gt;
  3423. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
  3424. &lt;p&gt;Then I’d run commands like:&lt;/p&gt;
  3425.  
  3426. &lt;div class="language-vala highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="n"&gt;coredumpctl&lt;/span&gt; &lt;span class="n"&gt;gdb&lt;/span&gt;
  3427. &lt;span class="n"&gt;coredumpctl&lt;/span&gt; &lt;span class="n"&gt;info&lt;/span&gt;
  3428. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
  3429. &lt;p&gt;It simply wasn’t working when I built it this way with the following coredumpctl commands. It wasn’t showing the debug symbols that I needed to be able to see the functions that were causing the program to crash. When I built Vala using GNOME Builder’s build button, it also didn’t work.
  3430. &lt;br /&gt;&lt;/p&gt;
  3431.  
  3432. &lt;p&gt;Lorenz, my mentor, helped me a lot on this issue. How we were able to fix this was first, I needed to build Vala by doing&lt;/p&gt;
  3433.  
  3434. &lt;div class="language-vala highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="p"&gt;./&lt;/span&gt;&lt;span class="n"&gt;configure&lt;/span&gt; &lt;span class="p"&gt;--&lt;/span&gt;&lt;span class="n"&gt;enable&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="n"&gt;debug&lt;/span&gt;
  3435. &lt;span class="n"&gt;make&lt;/span&gt;
  3436. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
  3437.  
  3438. &lt;p&gt;Then I needed to run the test in the β€˜build terminal’ in GNOME Builder &lt;code class="language-vala highlighter-rouge"&gt;&lt;span class="n"&gt;compiler&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="n"&gt;valac&lt;/span&gt; &lt;span class="p"&gt;--&lt;/span&gt;&lt;span class="n"&gt;pkg&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="n"&gt;glib&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="m"&gt;1.0&lt;/span&gt; &lt;span class="n"&gt;tests&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="n"&gt;annotations&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;vala&lt;/span&gt;&lt;/code&gt;
  3439. &lt;br /&gt;&lt;/p&gt;
  3440.  
  3441. &lt;p&gt;Then, in a regular terminal, I ran:&lt;/p&gt;
  3442.  
  3443. &lt;div class="language-vala highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="n"&gt;gdb&lt;/span&gt; &lt;span class="n"&gt;compiler&lt;/span&gt;&lt;span class="p"&gt;/.&lt;/span&gt;&lt;span class="n"&gt;libs&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="n"&gt;lt&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="nf"&gt;valac&lt;/span&gt;
  3444. &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gdb&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="p"&gt;--&lt;/span&gt;&lt;span class="n"&gt;pkg&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="n"&gt;glib&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="m"&gt;1.0&lt;/span&gt; &lt;span class="n"&gt;tests&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="n"&gt;annotations&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;vala&lt;/span&gt;
  3445. &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gdb&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;bt&lt;/span&gt;
  3446. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
  3447. &lt;p&gt;Once I ran these commands, I was finally able to see the functions causing the crash to happen.&lt;/p&gt;
  3448.  
  3449. &lt;div class="language-vala highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="m"&gt;123456&lt;/span&gt;&lt;span class="cp"&gt;#6  0x00007ffff7a1ef37 in vala_ccode_constant_construct_string (object_type=Python Exception &amp;lt;class 'gdb.error'&amp;gt;: value has been optimized out&lt;/span&gt;
  3450. &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_name&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="m"&gt;0x5555563ef1c0&lt;/span&gt; &lt;span class="s"&gt;"anything"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="n"&gt;home&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="n"&gt;alley&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="n"&gt;Desktop&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="n"&gt;vala&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="n"&gt;ccode&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="n"&gt;valaccodeconstant&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;vala&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;41&lt;/span&gt;
  3451. &lt;span class="cp"&gt;#7  0x00007ffff7a1f9f7 in vala_ccode_constant_new_string (_name=0x5555563ef1c0 "anything") at /home/alley/Desktop/vala/ccode/valaccodeconstant.vala:40&lt;/span&gt;
  3452. &lt;span class="cp"&gt;#8  0x00007ffff7a10918 in vala_json_module_json_builder (self=0x55555558c810) at /home/alley/Desktop/vala/codegen/valajsonmodule.vala:292&lt;/span&gt;
  3453. &lt;span class="cp"&gt;#9  0x00007ffff7a0f07d in vala_json_module_generate_class_to_json (self=0x55555558c810, cl=0x555557199120) at /home/alley/Desktop/vala/codegen/valajsonmodule.vala:191&lt;/span&gt;
  3454. &lt;span class="cp"&gt;#10 0x00007ffff7a127f4 in vala_json_module_real_generate_class_init (base=0x55555558c810, cl=0x555557199120) at /home/alley/Desktop/vala/codegen/valajsonmodule.vala:410&lt;/span&gt;
  3455. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
  3456.  
  3457. &lt;p&gt;This snippet of the backtrace shows that the function vala_json_module_json_builder () on line 292 was the actual crash culprit.
  3458. &lt;br /&gt;&lt;/p&gt;
  3459.  
  3460. &lt;p&gt;After I fixed the debug symbols, my git push decided not to work properly for a few days. So I was manually editing my changes on GitLab. My theory for git push not working is that Kwalletmanager had a problem, and so the credentials stopped working, which hanged the git push. Either way, I fixed it by switching my repo to SSH. I’ll investigate why the HTTP side of git stopped working, and I’ll fix it.&lt;/p&gt;
  3461.  
  3462. &lt;h1 id="guadec"&gt;GUADEC&lt;/h1&gt;
  3463.  
  3464. &lt;p&gt;This was my first-ever GUADEC event that I’ve ever watched. I watched it online, and I particularly found the lightning talks to be my favourite parts. They were all short, sweet, and to the point. It was also kind of comedic how fast people talked to fit everything they wanted to say in a short time span.
  3465. &lt;br /&gt;&lt;/p&gt;
  3466.  
  3467. &lt;p&gt;Some talks I particularly found interesting are:
  3468. &lt;br /&gt;&lt;/p&gt;
  3469.  
  3470. &lt;ol&gt;
  3471.  &lt;li&gt;
  3472.    &lt;p&gt;The open source game called &lt;a href="https://github.com/endlessm/threadbare"&gt;Threadbare&lt;/a&gt; by Endless Access. As someone who is a game programming graduate, it instantly caught my eye and had my attention. I’ll definitely be checking it out and trying to contribute to it.&lt;/p&gt;
  3473.  &lt;/li&gt;
  3474.  &lt;li&gt;
  3475.    &lt;p&gt;Carlos Garnacho’s talk about GNOME on our TVs. The idea of GNOME expanding onto smart TVs opens up a whole new area of usability and user experience. It got me thinking about the specs of a regular TV set and how GNOME can adapt to and enhance that experience. The possibilities are exciting, and I’m curious to see how far this concept goes.
  3476. &lt;br /&gt;&lt;/p&gt;
  3477.  &lt;/li&gt;
  3478. &lt;/ol&gt;
  3479.  
  3480. &lt;p&gt;Overall, GUADEC made me feel more connected to the GNOME community even though I joined remotely. I’d love to have GAUDEC hosted in Toronto :)&lt;/p&gt;</description><author>Alley Chaggar</author><dc:creator>Alley Chaggar</dc:creator><pubDate>Thu, 31 Jul 2025 07:30:00 GMT</pubDate><guid isPermaLink="true">https://alleych.github.io/gnome/challenges/</guid></item><item><title>Christian Schaller: Artificial Intelligence and the Linux Community</title><link>https://blogs.gnome.org/uraeus/2025/07/29/artificial-intelligence-and-the-linux-community/</link><description>&lt;p&gt;I have wanted to write this blog post for quite some time, but been unsure about the exact angle of it. I think I found that angle now where I will root the post in a very tangible concrete example. &lt;/p&gt;
  3481. &lt;p&gt;So the reason I wanted to write this was because I do feel there is a palpable skepticism and negativity towards AI in the Linux community, and I understand that there are societal implications that worry us all, like how deep fakes have the potential to upend a lot of things from news disbursement to court proceedings. Or how malign forces can use AI to drive narratives in social media etc., is if social media wasn&amp;#8217;t toxic enough as it is. But for open source developers like us in the Linux community there is also I think deep concerns about tooling that deeply incurs into something that close to the heart of our community, writing code and being skilled at writing code. I hear and share all those concerns, but at the same time having spent time the last weeks using &lt;a class="external" href="https://claude.ai"&gt;Claude.ai&lt;/a&gt; I do feel it is not something we can afford not to engage with. So I know people have probably used a lot of different AI tools in the last year, some being more cute than useful others being somewhat useful and others being interesting improvements to your Google search for instance. I think I shared a lot of those impressions, but using Claude this last week has opened my eyes to what AI enginers are going to be capable of going forward.&lt;/p&gt;
  3482. &lt;p&gt;So my initial test was writing a python application for internal use at Red Hat, basically connecting to a variety of sources and pulling data and putting together reports, typical management fare. How simple it was impressed me though, I think most of us having to deal with pulling data from a new source know how painful it can be, with issues ranging from missing, outdated or hard to parse API documentation. I think a lot of us also then spend a lot of time experimenting to figure out the right API calls to make in order to pull the data we need. Well Claude was able to give me python scripts that pulled that data right away, I still had to spend some time with it to fine tune the data being pulled and ensuring we pulled the right data, but I did it in a fraction of the time I would have spent figuring that stuff out on my own. The one data source Claude struggled with Fedora&amp;#8217;s Bohdi, well once I pointed it to the URL with the latest documentation for that it figured out that it would be better to use the bohdi client library to pull data and once it had that figured out it was clear sailing.&lt;/p&gt;
  3483. &lt;p&gt;So coming of pretty impressed by that experience I wanted to understand if Claude would be able to put together something programmatically more complex, like a GTK+ application using Vulkan. [Note: should have checked the code better, but thanks to the people who pointed this out. I told the AI to use Vulkan, which it did, but not in the way I expected, I expected it to render the globe using Vulkan, but it instead decided to ensure GTK used its Vulkan backend, an important lesson in both prompt engineering and checking the code afterwards).]So I thought what would be a good example of such an application and I also figured it would be fun if I found something really old and asked Claude to help me bring it into the current age. So I suddenly remembered xtraceroute, which is an old application orginally written in GTK1 and OpenGL showing your traceroute on a 3d Globe.&lt;div class="wp-caption alignright" id="attachment_11216" style="width: 221px;"&gt;&lt;img alt="Screenshot of original xtraceroute" class="size-medium wp-image-11216" height="300" src="https://blogs.gnome.org/uraeus/files/2025/07/originalxtraceroute-211x300.jpg" width="211" /&gt;&lt;p class="wp-caption-text" id="caption-attachment-11216"&gt;Screenshot of the original Xtraceroute application&lt;/p&gt;&lt;/div&gt;&lt;/p&gt;
  3484. &lt;p&gt;I went looking for it and found that while it had been updated to GTK2 since last I looked at it, it had not been touched in 20 years. So I thought, this is a great testcase. So I grabbed the code and fed it into Claude, asking Claude to give me a modern GTK4 version of this application using Vulkan. Ok so how did it go? Well it ended up being an iterative effort, with a lot of back and forth between myself and Claude. One nice feature Claude has is that you can upload screenshots of your application and Claude will use it to help you debug. Thanks to that I got a long list of screenshots showing how this application evolved over the course of the day I spent on it.&lt;/p&gt;
  3485. &lt;div class="wp-caption alignnone" id="attachment_11226" style="width: 610px;"&gt;&lt;img alt="First output of Claude" class="size-medium wp-image-11226" height="228" src="https://blogs.gnome.org/uraeus/files/2025/07/xtraceroutefirstattempt-300x228.png" width="300" /&gt;&lt;p class="wp-caption-text" id="caption-attachment-11226"&gt;This screenshot shows Claudes first attempt of transforming the 20 year old xtraceroute application into a modern one using GTK4, Vulkan and also adding a Meson build system. My prompt to create this was feeding in the old code and asking Claude to come up with a GTK4 and Vulkan equivalent. As you can see the GTK4 UI is very simple, but ok as it is. The rendered globe leaves something to be desired though. I assume the old code had some 2d fall backcode, so Claude latched onto that and focused on trying to use the Cairo API to recreate this application, despite me telling it I wanted a Vulkan application. What what we ended up with was a 2d circle that I could spin around like a wheel of fortuen. The code did have some Vulkan stuff, but defaulted to the Cairo code.&lt;/p&gt;&lt;/div&gt;
  3486. &lt;div class="wp-caption alignnone" id="attachment_11227" style="width: 610px;"&gt;&lt;img alt="Second attempt image" class="size-medium wp-image-11227" height="218" src="https://blogs.gnome.org/uraeus/files/2025/07/xtraceroutesecondattempt-300x218.png" width="300" /&gt;&lt;p class="wp-caption-text" id="caption-attachment-11227"&gt;Second attempt at updating this application Anyway, I feed the screenshot of my first version back into Claude and said that the image was not a globe, it was missing the texture and the interaction model was more like a wheel of fortune. As you can see the second attempt did not fare any better, in fact we went from circle to square. This was also the point where I realized that I hadn&amp;#8217;t uploaded the textures into Claude, so I had to tell it to load the earth.png from the local file repository.&lt;/p&gt;&lt;/div&gt;
  3487. &lt;div class="wp-caption alignnone" id="attachment_11228" style="width: 610px;"&gt;&lt;img alt="Third attempt by Claude" class="size-medium wp-image-11228" height="228" src="https://blogs.gnome.org/uraeus/files/2025/07/xtraceroutethirdaattempt-300x228.png" width="300" /&gt;&lt;p class="wp-caption-text" id="caption-attachment-11228"&gt;Third attempt from Claude.Ok, so I feed my second screenshot back into Claude and pointed out that it was no globe, in fact it wasn&amp;#8217;t even a circle and the texture was still missing. With me pointing out it needed to load the earth.png file from disk it came back with the texture loading. Well, I really wanted it to be a globe, so I said thank you for loading the texture, now do it on a globe.&lt;/p&gt;&lt;/div&gt;
  3488. &lt;div class="wp-caption alignnone" id="attachment_11219" style="width: 610px;"&gt;&lt;img alt="" class="size-medium wp-image-11219" height="228" src="https://blogs.gnome.org/uraeus/files/2025/07/xtraceroute4th-300x228.png" width="300" /&gt;&lt;p class="wp-caption-text" id="caption-attachment-11219"&gt;This is the output of the 4th attempt. As you can see, it did bring back a circle, but the texture was gone again. At this point I also decided I didn&amp;#8217;t want Claude to waste anymore time on the Cairo code, this was meant to be a proper 3d application. So I told Claude to drop all the Cairo code and instead focus on making a Vulkan application.&lt;/p&gt;&lt;/div&gt;
  3489. &lt;div class="wp-caption alignnone" id="attachment_11220" style="width: 610px;"&gt;&lt;img alt="Fifth attempt" class="size-medium wp-image-11220" height="228" src="https://blogs.gnome.org/uraeus/files/2025/07/xtraceroute5th-300x228.png" width="300" /&gt;&lt;p class="wp-caption-text" id="caption-attachment-11220"&gt;So now we finally had something that started looking like something, although it was still a circle, not a globe and it got that weird division of 4 thing on the globe. Anyway, I could see it using Vulkan now and it was loading the texture. So I was feeling like we where making some decent forward movement. So I wrote a longer prompt describing the globe I wanted and how I wanted to interact with it and this time Claude did come back with Vulkan code that rendered this as a globe, thus I didn&amp;#8217;t end up screenshoting it unfortunately.&lt;/td&gt;
  3490. &lt;p&gt;&lt;/p&gt;&lt;/div&gt;
  3491. &lt;div class="wp-caption alignnone" id="attachment_11221" style="width: 610px;"&gt;&lt;img alt="" class="size-medium wp-image-11221" height="228" src="https://blogs.gnome.org/uraeus/files/2025/07/xtraceroute6th-300x228.png" width="300" /&gt;&lt;p class="wp-caption-text" id="caption-attachment-11221"&gt;So with the working globe now in place, I wanted to bring in the day/night cycle from the original application. So I asked Claude to load the night texture and use it as an overlay to get that day/night effect. I also asked it to calculate the position of the sun to earth at the current time, so that it could overlay the texture in the right location. As you can see Claude did a decent job of it, although the colors was broken.&lt;/p&gt;&lt;/div&gt;
  3492. &lt;div class="wp-caption alignnone" id="attachment_11222" style="width: 610px;"&gt;&lt;img alt="7th attempt" class="size-medium wp-image-11222" height="228" src="https://blogs.gnome.org/uraeus/files/2025/07/xtraceroute7th-300x228.png" width="300" /&gt;&lt;p class="wp-caption-text" id="caption-attachment-11222"&gt;So I kept fighting with the color for a bit, Claude could see it was rendering it brown, but could not initally figure out why. I could tell the code was doing things mostly right so I also asked it to look at some other things, like I realized that when I tried to spin the globe it just twisted the texture. We got that fixed and also I got Claude to create some tests scripts that helped us figure out that the color issue was a RGB vs BRG issue, so as soon as we understood that then Claude was able to fix the code to render colors correctly. I also had a few iterations trying to get the scaling and mouse interaction behaving correctly.&lt;br /&gt;&lt;/p&gt;&lt;/div&gt;
  3493. &lt;div class="wp-caption alignnone" id="attachment_11217" style="width: 610px;"&gt;&lt;img alt="10th attempt" class="size-medium wp-image-11217" height="228" src="https://blogs.gnome.org/uraeus/files/2025/07/xtraceroiute10th-300x228.png" width="300" /&gt;&lt;p class="wp-caption-text" id="caption-attachment-11217"&gt;So at this point I had probably worked on this for 4-5 hours, the globe was rendering nicely and I could interact with it using the mouse. Next step was adding the traceroute lines back. By default Claude had just put in code to render some small dots on the hop points, not draw the lines. Also the old method for getting the geocoordinates, but I asked Claude to help me find some current services which it did and once I picked one it on first try gave me code that was able to request the geolocation of the ip addresses it got back. To polish it up I also asked Claude to make sure we drew the lines following the globes curvature instead of just drawing straight lines.&lt;/p&gt;&lt;/div&gt;
  3494. &lt;div class="wp-caption alignnone" id="attachment_11225" style="width: 610px;"&gt;&lt;img alt="Final version" class="size-medium wp-image-11225" height="228" src="https://blogs.gnome.org/uraeus/files/2025/07/xtraceroute11th-300x228.png" width="300" /&gt;&lt;p class="wp-caption-text" id="caption-attachment-11225"&gt;Final version of the updated Xtraceroute application. It mostly works now, but I did realize why I always thought this was a fun idea, but less interesting in practice, you often don&amp;#8217;t get very good traceroutes back, probably due to websites being cached or hosted globally. But I felt that I had proven that with a days work Claude was able to help me bring this old GTK application into the modern world.&lt;br /&gt;&lt;/p&gt;&lt;/div&gt;
  3495. &lt;h2&gt;Conclusions&lt;/h2&gt;
  3496. &lt;p&gt;So I am not going to argue that Xtraceroute is an important application that deserved to be saved, in fact while I feel the current version works and proves my point I also lost motivation to try to polish it up due to the limitations of tracerouting, but t&lt;a class="external" href="https://gitlab.com/cschalle/xtraceroute-ai-revamped"&gt;he code is available for anyone who finds it worthwhile&lt;/a&gt;. &lt;/p&gt;
  3497. &lt;p&gt;But this wasn&amp;#8217;t really about Xtraceroute, what I wanted to show here is how someone lacking C and Vulkan development skills can actually use a tool like Claude to put together a working application even one using more advanced stuff like Vulkan, which I know many more than me would feel daunting. I also found Claude really good at producing documentation and architecture documents for your application. It was also able to give me a working Meson build system and create all the desktop integration files for me, like the .desktop file, the metainfo file and so on. For the icons I ended up using Gemini as Claude do not do image generation at this point, although it was able to take a png file and create a SVG version of it (although not a perfect likeness to the original png).&lt;/p&gt;
  3498. &lt;p&gt;Another thing I want to say is that the way I think about this, it is not that it makes coding skills less valuable, AIs can do amazing things, but you need to keep a close eye on them to ensure the code they create actually do what you want and that it does it in a sensible manner. For instance in my reporting application I wanted to embed a pdf file and Claude initial thought was to bring in webkit to do the rendering. That would have worked, but would have added a very big and complex dependency to my application, so I had to tell it that it could just use libpoppler to do it, something Claude agreed was a much better solution. The bigger the codebase the harder it also becomes for the AI to deal with it, but I think it hose circumstances what you can do is use the AI to give you sample code for the functionality you want in the programming language you want and then you can just work on incorporating that into your big application.&lt;/p&gt;
  3499. &lt;p&gt;The other part here if course in terms of open source is how should contributors and projects deal with this? I know there are projects where AI generated CVEs or patches are drowning them and that helps nobody. But I think if we see AI as a developers tool and that the developer using the tool is responsible for the code generated, then I think that mindset can help us navigate this. So if you used an AI tool to create a patch for your favourite project, it is your responsibility to verify that patch before sending it in, and with that I don&amp;#8217;t mean just verifying the functionality it provides, but that the code is clean and readable and following the coding standards of said upstream project. Maintainers on the other hand can use AI to help them review and evaluate patches quicker and thus this can be helpful on both sides of the equation. I also found Claude and other AI tools like Gemini pretty good at generating test cases for the code they make, so this is another area where open source patch contributions can improve, by improving test coverage for the code.&lt;/p&gt;
  3500. &lt;p&gt;I do also believe there are many areas where projects can greatly benefit from AI, for instance in the GNOME project a constant challenge for extension developers have been keeping their extensions up-to-date, well I do believe a tool like Claude or Gemini should be able to update GNOME Shell extensions quite easily. So maybe having a service which tries to provide a patch each time there is a GNOME Shell update might be a great help there. At the same time having a AI take a look at updated extensions and giving an first review of the update might help reduce the load on people doing code reviews on extensions and help flag problematic extensions.&lt;/p&gt;
  3501. &lt;p&gt;I know for a lot of cases and situations uploading your code to a webservice like Claude, Gemini or Copilot is not something you want or can do. I know privacy is a big concern for many people in the community. My team at Red Hat has been working on a code assistant tool using the &lt;a class="external" href="https://www.ibm.com/granite?utm_content=SRCWW&amp;amp;p1=Search&amp;amp;p4=43700080962491259&amp;amp;p5=e&amp;amp;p9=58700008798105472&amp;amp;gclsrc=aw.ds&amp;amp;gad_source=1&amp;amp;gad_campaignid=21836574700&amp;amp;gclid=Cj0KCQjw4qHEBhCDARIsALYKFNMp43_IuT_sFQdnUMMuMFPaMM79ZMr6qI5ooNqic_R9eh8zIaZmYMAaAtbSEALw_wcB"&gt;IBM Granite model&lt;/a&gt;, called &lt;a class="external" href="https://granitecode.ai/"&gt;Granite.code&lt;/a&gt;. What makes Granite different is that it relies on having the model run locally on your own system, so you don&amp;#8217;t send your code or data of somewhere else. This of course have great advantages in terms of improving privacy and security, but it has challenges too. The top end AI models out there at the moment, of which Claude is probably the best at the time of writing this blog post, are running on hardware with vast resources in terms of computing power and memory available. Most of us do not have those kind of capabilities available at home, so the model size and performance will be significantly lower. So at the moment if you are looking for a great open source tool to use with VS Code to do things like code completion I recommend giving Granite.code a look. If you on the other hand want to do something like I have described here you need to use something like Claude, Gemini or ChatGPT. I do recommend Claude, not just because I believe them to be the best at it at the moment, but they also are a company trying to hold themselves to high ethical standards. Over time we hope to work with IBM and others in the community to improve local models, and I am also sure local hardware will keep improving, so over time the experience you can get with a local model on your laptop at least has less of a gap than what it does today compared to the big cloud hosted models. There is also the middle of the road option that will become increasingly viable, where you have a powerful server in your home or at your workplace that can at least host a midsize model, and then you connect to that on your LAN. I know IBM is looking at that model for the next iteration of Granite models where you can choose from a wide variety of sizes, some small enough to be run on a laptop, others of a size where a strong workstation or small server can run them or of course the biggest models for people able to invest in top of the line hardware to run their AI.&lt;/p&gt;
  3502. &lt;p&gt;Also the AI space is moving blazingly fast, if you are reading this 6 Months from now I am sure the capabilities of online and local models will have changed drastically already.&lt;/p&gt;
  3503. &lt;p&gt;So to all my friends in the Linux community I ask you to take a look at AI and what it can do and then lets work together on improving it, not just in terms of capabilities, but trying to figure out things like societal challenges around it and sustainability concerns I also know a lot of us got.&lt;/p&gt;
  3504. &lt;p&gt;&lt;h2&gt;Whats next for this code&lt;/h2&gt;
  3505. &lt;p&gt;As I mentioned I while I felt I got it to a point where I proved to myself it worked, I am not planning on working anymore on it. But I did make a cute little application for internal use that shows a spinning globe with all global Red Hat offices showing up as little red lights and where it pulls Red Hat news at the bottom. Not super useful either, but I was able to use Claude to refactor the globe rendering code from xtraceroute into this in just a few hours.&lt;/p&gt;
  3506. &lt;div class="wp-caption alignright" id="attachment_11251" style="width: 610px;"&gt;&lt;img alt="Red Hat Globe" class="size-medium wp-image-11251" height="562" src="https://blogs.gnome.org/uraeus/files/2025/07/Screenshot-From-2025-07-29-11-38-51-300x281.png" width="600" /&gt;&lt;p class="wp-caption-text" id="caption-attachment-11251"&gt;Red Hat Offices Globe and news.&lt;/p&gt;&lt;/div&gt;</description><author>Christian Schaller</author><dc:creator>Christian Schaller</dc:creator><pubDate>Tue, 29 Jul 2025 16:24:22 GMT</pubDate><guid isPermaLink="true">https://blogs.gnome.org/uraeus/2025/07/29/artificial-intelligence-and-the-linux-community/</guid></item><item><title>Bastien Nocera: Digitising CDs (aka using your phone as an image scanner)</title><link>https://www.hadess.net/2025/07/digitising-cds-aka-using-your-phone-as.html</link><description>&lt;p style="text-align: justify;"&gt;I recently found, under the rain, next to a book swap box, a pile of 90's β€œsoftware magazines” which I spent my evening cleaning, drying, and sorting in the days afterwards.&lt;/p&gt;&lt;p style="text-align: justify;"&gt;&lt;b&gt;Magazine cover CDs with nary a magazine&lt;/b&gt;&amp;nbsp;&lt;/p&gt;&lt;p style="text-align: justify;"&gt;Those magazines are a peculiar thing in France, using the mechanism of β€œ&lt;a href="https://fr.wikipedia.org/wiki/Commission_paritaire_des_publications_et_des_agences_de_presse"&gt;Commission paritaire des publications et des agences de presse&lt;/a&gt;” or β€œCommission paritaire” for short. This structure exists to assess whether a magazine can benefit from state subsidies for the written press (whether on paper at the time, and also the internet nowadays), which include &lt;a href="https://www.cppap.fr/publications/"&gt;a reduced VAT charge (2.1% instead of 20%), reduced postal rates, and tax exemptions&lt;/a&gt;.&lt;/p&gt;&lt;p style="text-align: justify;"&gt;In the 90s, this was used by Diamond Editions[1] (a publisher related to &lt;a href="https://www.pearl.fr/"&gt;tech shop Pearl&lt;/a&gt;, which French and German computer enthusiasts probably know) to publish magazines with just enough original text to qualify for those subsidies, bundled with the really interesting part, a piece of software on CD.&lt;/p&gt;&lt;p style="text-align: justify;"&gt;If you were to visit a French newsagent nowadays, you would be able to find other examples of this: magazines bundled with music CDs, DVDs or Blu-rays, or even toys or collectibles. Some publishers (including the infamous and now shuttered&amp;nbsp;&lt;a href="https://fr.wikipedia.org/wiki/%C3%89ditions_Atlas"&gt;Γ‰ditions Atlas&lt;/a&gt;) will even get you a cheap kickstart to a new collection, with the first few issues (and collectibles) available at very interesting prices of a couple of euros, before making that β€œmagazine” subscription-only, with each issue being increasingly more expensive (&lt;a href="https://www.quechoisir.org/actualite-collections-atlas-manque-de-transparence-n10529/"&gt;article from a consumer protection association&lt;/a&gt;).&lt;/p&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgHOW1KuPxZb5ceg-ujUuFHzvYJucKnjC6IXlhHLEM0R9bf6BMBfQq5sVTtWOaPaCAtkM5btpkOTh2FsIw5lVOngi0eY9S3ZaSSqJScKk9OGgnJg8dKw_VHd14CxgeI3h79okL-pMP2rJLzBFcnDLV6GGCWMErYzsKeq0iyH5ZjF5bY4HAMjQu0-RPVXnptysPHgMNfmg/s1024/KpOVBdFpM3zeY9XMM2zr7AhbJq4.jpg" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="149" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgHOW1KuPxZb5ceg-ujUuFHzvYJucKnjC6IXlhHLEM0R9bf6BMBfQq5sVTtWOaPaCAtkM5btpkOTh2FsIw5lVOngi0eY9S3ZaSSqJScKk9OGgnJg8dKw_VHd14CxgeI3h79okL-pMP2rJLzBFcnDLV6GGCWMErYzsKeq0iyH5ZjF5bY4HAMjQu0-RPVXnptysPHgMNfmg/w200-h149/KpOVBdFpM3zeY9XMM2zr7AhbJq4.jpg" width="200" /&gt;&lt;/a&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjbjvuIIXrGwv8Ix-_gxunaNf5tBTRcYMdGKfBLvDX-3qDkLrKFXTTNVjT_el2moPE-2D4Rp_pFP5kIFjgNtYiIBaadBN_7I_kOVtPlhiukFG2NqyKZGaaAqY6BzJprOqfyL7mzKuDd1dmPAQuuevbgLl_Vng1w7zX_OpLIS7UsDd4kq80NAXYdJfeL1vFUfuQKD0Sh1Q/s1200/HZA-XhjstxCdEjvEfmdkJ8lTSj0.jpg" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;/a&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhyUyEF12RKPy_AEXmxabARtDmsPuI0ekOzdbmb1QjhfLeQVsZ5hMALIzDWhdWimXQ9rcRDnkSigWY9N_yRdQ30oeO-ObrDEr5OP3_6OvwejMC1m8_MQ1no0JOAaMqhjYALI-C1awBg671OLbRdqI8FTEStdjw4li0qqmjhxJxsBdPUmXQRbsEr8PJp5ITw6GQBmdnmyA/s1200/P8pBxNYOR4B2C4zS9R0t3bKgN6E.jpg" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="200" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhyUyEF12RKPy_AEXmxabARtDmsPuI0ekOzdbmb1QjhfLeQVsZ5hMALIzDWhdWimXQ9rcRDnkSigWY9N_yRdQ30oeO-ObrDEr5OP3_6OvwejMC1m8_MQ1no0JOAaMqhjYALI-C1awBg671OLbRdqI8FTEStdjw4li0qqmjhxJxsBdPUmXQRbsEr8PJp5ITw6GQBmdnmyA/w150-h200/P8pBxNYOR4B2C4zS9R0t3bKgN6E.jpg" width="150" /&gt;&lt;/a&gt;&lt;img border="0" height="200" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjbjvuIIXrGwv8Ix-_gxunaNf5tBTRcYMdGKfBLvDX-3qDkLrKFXTTNVjT_el2moPE-2D4Rp_pFP5kIFjgNtYiIBaadBN_7I_kOVtPlhiukFG2NqyKZGaaAqY6BzJprOqfyL7mzKuDd1dmPAQuuevbgLl_Vng1w7zX_OpLIS7UsDd4kq80NAXYdJfeL1vFUfuQKD0Sh1Q/w150-h200/HZA-XhjstxCdEjvEfmdkJ8lTSj0.jpg" width="150" /&gt;&lt;/div&gt;&lt;br /&gt;&lt;p&gt;&lt;/p&gt;&lt;p style="text-align: justify;"&gt;Other publishers have followed suite.&lt;/p&gt;&lt;p style="text-align: center;"&gt;&lt;/p&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;img border="0" height="203" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEguVTmzd3RHemAsd1SaidJgUeJU2Df7s6FB_xFmN2J-8lBgoC2DWyK65CmvL6bgshOiEZjy06Kt1jneGkP6q9sC5MLvioQpB8fZBvBdCl830cUIGOaucor1VLEMO4TuGxdLJtXAUvGNrJar3r8_0OLfY87-twBn4OmSpKgigxpXDIGTqVd0oJ65sbpR-yFqzlylDXImEg/s320/image_0648575_20250723_ob_884720_fast-and-furious-toyota-suora-lancem.jpg" width="320" /&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhdCz1wJ9PXuzawq3UTDa22H4ChTJe62jvwQFNUCB9kSQXWKxBALrKQ1pEja7SNa1n7j_2ThqNaBtmZtfjnFkFYI2iR_wfwVCgiz-lhvkhp0o9O_apaFd2zK7QGQD9AMtRjsz_5SERKA_G8m841igIrS52ADVXKQI4AX1Ycsx1fTLLRIs3jD4bYS02FIyRe4RnHhMYWrA/s1600/image_0648575_20250723_ob_a45d7b_fast-and-furious-toyota-suora-lancem.jpg" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="215" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhdCz1wJ9PXuzawq3UTDa22H4ChTJe62jvwQFNUCB9kSQXWKxBALrKQ1pEja7SNa1n7j_2ThqNaBtmZtfjnFkFYI2iR_wfwVCgiz-lhvkhp0o9O_apaFd2zK7QGQD9AMtRjsz_5SERKA_G8m841igIrS52ADVXKQI4AX1Ycsx1fTLLRIs3jD4bYS02FIyRe4RnHhMYWrA/s320/image_0648575_20250723_ob_a45d7b_fast-and-furious-toyota-suora-lancem.jpg" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;p&gt;&lt;/p&gt;&lt;p style="text-align: justify;"&gt;I guess you can only imagine how much your scale model would end up costing with that business model (50 eurocent for the first part, 4.99€ for the second), although I would expect them to have given up the idea of being categorised as β€œwritten press”.&lt;/p&gt;&lt;p style="text-align: justify;"&gt;To go back to Diamond Editions, this meant the eventual birth of 3 magazines:&amp;nbsp;&lt;a href="https://www.abandonware-magazines.org/affiche_mag.php?mag=178"&gt;Presqu'Offert&lt;/a&gt;, &lt;a href="https://www.abandonware-magazines.org/affiche_mag.php?mag=209"&gt;BestSellerGames&lt;/a&gt; and &lt;a href="https://www.abandonware-magazines.org/affiche_mag.php?mag=173"&gt;StratΓ©J&lt;/a&gt;. I remember me or my dad buying a few of those, an older but legit and complete version of ClarisWorks, CorelDraw or a talkie version of a LucasArt point'n'click was certainly a more interesting proposition than a cut-down warez version full of viruses when budget was tight.&lt;/p&gt;&lt;p style="text-align: justify;"&gt;&lt;/p&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhd1-cILqODqgG6a-xaVyw2IPgO8FcZg1-6VCLM52lTFg63BTlfVpUF88DJxxGJdd5m98bbkvWsm79oHh7R1hfMUogvq2wXbVXieyixOK2Ii5Dq4abvIq1FkOo3jBNtGtbt4rdEWf7ZRwYr2K8bvIeAMoQ0_4q2TJT5m4RXCAhEtVFjlKz8QjhYUvqVs2dq8YIsE-nH8g/s1547/IMG_4131.JPG" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="194" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhd1-cILqODqgG6a-xaVyw2IPgO8FcZg1-6VCLM52lTFg63BTlfVpUF88DJxxGJdd5m98bbkvWsm79oHh7R1hfMUogvq2wXbVXieyixOK2Ii5Dq4abvIq1FkOo3jBNtGtbt4rdEWf7ZRwYr2K8bvIeAMoQ0_4q2TJT5m4RXCAhEtVFjlKz8QjhYUvqVs2dq8YIsE-nH8g/w400-h194/IMG_4131.JPG" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;p&gt;&lt;/p&gt;&lt;p style="text-align: center;"&gt;&lt;i&gt;3 of the magazines I managed to rescue from the rain&lt;/i&gt;&lt;/p&gt;&lt;p style="text-align: justify;"&gt;You might also be interested in the &lt;a href="https://www.youtube.com/watch?v=MCpfjwcOVjs"&gt;UK β€œcovertape wars”&lt;/a&gt;.&lt;/p&gt;&lt;p style="text-align: justify;"&gt;&lt;b&gt;Don't stress the technique&lt;/b&gt;&amp;nbsp;&lt;/p&gt;&lt;p style="text-align: justify;"&gt;This brings us back to today and while the magazines are still waiting for scanning, I tried to get a wee bit organised and digitising the CDs.&lt;/p&gt;&lt;p style="text-align: justify;"&gt;Some of them will have printing that covers the whole of the CD, a fair few use the foil/aluminium backing of the CD as a blank surface, which will give you pretty bad results when scanning them with a flatbed scanner: the light source keeps moving with the sensor, and what you'll be scanning is the sensor's reflection on the CD.&lt;/p&gt;&lt;p style="text-align: justify;"&gt;My workaround for this is to use a digital camera (my phone's 24MP camera), with a white foam board behind it, so the blank parts appear more light grey. Of course, this means that you need to take the picture from an angle, and that the CD will appear as an oval instead of perfectly circular.&lt;/p&gt;&lt;p&gt;I tried for a while to use &lt;a href="https://www.gimp.org/"&gt;GIMP&lt;/a&gt; perspective tools, and β€œMultimedia” Mike Melanson's &lt;a href="https://github.com/multimediamike/MobyCAIRO"&gt;MobyCAIRO&lt;/a&gt; rotation and cropping tool. In the end, I settled on Darktable, which allowed me to do 4-point perspective deskewing, I just had to have those reference points.&lt;/p&gt;&lt;p&gt;So I came up with a &lt;a href="https://github.com/masible/cd-deskew-template"&gt;simple "deskew" template&lt;/a&gt;, which you can print yourself, although you could probably achieve similar results with grid paper.&lt;/p&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAiTM-nsPt4VwDPH6ZsfI5vTf-NRO6kJm6d6y2yqvjXc6AbRWZ4tS3Ysw6H__86Hutezd4F-GWXwT2UDGQagKV7S-R3j0GeRpnBl4lk4vzIEb-2_rXAGIjX_Je4ktaUirMIfYxrl1Olv8KhZ9B4kZAYV_ktqwhsrk6TO-AxW2NbjLE4GCzXrZ9vMgR-5NI5rasifc-Eg/s1333/backing-foam-board.jpg" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAiTM-nsPt4VwDPH6ZsfI5vTf-NRO6kJm6d6y2yqvjXc6AbRWZ4tS3Ysw6H__86Hutezd4F-GWXwT2UDGQagKV7S-R3j0GeRpnBl4lk4vzIEb-2_rXAGIjX_Je4ktaUirMIfYxrl1Olv8KhZ9B4kZAYV_ktqwhsrk6TO-AxW2NbjLE4GCzXrZ9vMgR-5NI5rasifc-Eg/s320/backing-foam-board.jpg" width="240" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div style="text-align: center;"&gt;&lt;i&gt;My janky setup&lt;/i&gt;&lt;/div&gt;&lt;p&gt;&lt;/p&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhVZvnrSebOSuVLXRlMuRVjRKmmEPo0_U0M7U6vqhtms2kR9VLKN184AyYT5oOG52d5fPYMbbgcTNgrp98a35VW0uRucu3WAZS-AxYsEy2G_Yu_FZL6P6T58afe7SxBECIZO7QmaDPUNth1gNfgIzhOdjgvSPP5gar-WNMDTRqCEEscUF-hpwtMeY2vZyi1C0AX1fmDsA/s1000/example-photo.jpg" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhVZvnrSebOSuVLXRlMuRVjRKmmEPo0_U0M7U6vqhtms2kR9VLKN184AyYT5oOG52d5fPYMbbgcTNgrp98a35VW0uRucu3WAZS-AxYsEy2G_Yu_FZL6P6T58afe7SxBECIZO7QmaDPUNth1gNfgIzhOdjgvSPP5gar-WNMDTRqCEEscUF-hpwtMeY2vZyi1C0AX1fmDsA/s320/example-photo.jpg" width="240" /&gt;&amp;nbsp;&lt;/a&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;i&gt;The resulting picture&lt;/i&gt;&amp;nbsp;&lt;/div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;After opening your photo with Darktable, and selecting the β€œ&lt;i&gt;darkroom&lt;/i&gt;” tab, go to the β€œ&lt;i&gt;rotate and perspective tool&lt;/i&gt;”, select the β€œ&lt;i&gt;manually defined rectangle&lt;/i&gt;” structure, and adjust the rectangle to match the centers of the 4 deskewing targets. Then click on β€œ&lt;i&gt;horizontal/vertical fit&lt;/i&gt;”. This will give you a squished CD, don't worry, and select the β€œ&lt;i&gt;specific&lt;/i&gt;” lens model and voilΓ .&lt;/p&gt;&lt;p style="text-align: left;"&gt;&lt;/p&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjDeH7kiK7f5G72NbJKFy6yoscyuU9SzFEregWWfSt3aU_dyMB76R7FsOd4t-9lgyIXAgxZZ2eQcfzSviTuYzFBujXUq8XO7Qz2zBQChYRjIZhL8VmYmpaambp-Ukw2niYgL8lqQdG9e9srdIXME0AhallSEieDxPS2Q-iktzA6QUSl5ArxUBQ474S3J1ZQ82SwywkiVg/s1533/rotate-and-perspective.png" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="231" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjDeH7kiK7f5G72NbJKFy6yoscyuU9SzFEregWWfSt3aU_dyMB76R7FsOd4t-9lgyIXAgxZZ2eQcfzSviTuYzFBujXUq8XO7Qz2zBQChYRjIZhL8VmYmpaambp-Ukw2niYgL8lqQdG9e9srdIXME0AhallSEieDxPS2Q-iktzA6QUSl5ArxUBQ474S3J1ZQ82SwywkiVg/s320/rotate-and-perspective.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;p&gt;&lt;/p&gt;&lt;p style="text-align: center;"&gt;&lt;i&gt;Tools at the ready&lt;/i&gt;&lt;br /&gt;&lt;/p&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEisJTCVSh_jfFgr3wflmIPsRw1iWt3LmtptV_aUCypokIWO687i7slq62_weyrBpZgHEeUC7PN5YuOipI3g5XeVnVYoZvTy-kweNxpEgfIxzaGy8Edst1g6NZIAW3yUq5sEEWba-9ru9NiX0AnaMUwDcF5siUtsl4V_342zWGkgh5LIuU-kmtxYGVLMczs2FO7Q9zk1MA/s825/squared.png" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="279" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEisJTCVSh_jfFgr3wflmIPsRw1iWt3LmtptV_aUCypokIWO687i7slq62_weyrBpZgHEeUC7PN5YuOipI3g5XeVnVYoZvTy-kweNxpEgfIxzaGy8Edst1g6NZIAW3yUq5sEEWba-9ru9NiX0AnaMUwDcF5siUtsl4V_342zWGkgh5LIuU-kmtxYGVLMczs2FO7Q9zk1MA/s320/squared.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;p&gt;&lt;/p&gt;&lt;p style="text-align: center;"&gt;&lt;i&gt;Targets acquired&lt;/i&gt;&lt;/p&gt;&lt;p style="text-align: center;"&gt;&lt;i&gt;&lt;/i&gt;&lt;/p&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;i&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjDAKLiMWugK7L0BdH8_BIw8rEUVjeMjwPPGYQUc4z4J42KV3TC5x7s74S7ES3NIw9POXefT7Fh7Z4Cy_KGMZNyGZkzAvzRjaxTu2m3CSXQNiGJbyLPI8i6L-6mImxTXt50mqcWGlNBY_FDrQ-N3tMVCwrc0h6gZExa0wtDT6UFxy2SqiF05qckRG5ZJ6Ml8Qzl6kJ7eQ/s888/squished.png" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="230" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjDAKLiMWugK7L0BdH8_BIw8rEUVjeMjwPPGYQUc4z4J42KV3TC5x7s74S7ES3NIw9POXefT7Fh7Z4Cy_KGMZNyGZkzAvzRjaxTu2m3CSXQNiGJbyLPI8i6L-6mImxTXt50mqcWGlNBY_FDrQ-N3tMVCwrc0h6gZExa0wtDT6UFxy2SqiF05qckRG5ZJ6Ml8Qzl6kJ7eQ/s320/squished.png" width="320" /&gt;&lt;/a&gt;&lt;/i&gt;&lt;/div&gt;&lt;i&gt;&lt;br /&gt;&lt;/i&gt;&lt;div style="text-align: center;"&gt;&lt;i&gt;&amp;nbsp;Straightened but squished&lt;/i&gt;&lt;/div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;You can now export the processed image (I usually use PNG to avoid data loss at each step), open things up in GIMP and use the ellipse selection tool to remove the background (don't forget the center hole), the rotate tool to make the writing straight, and the crop tool to crop it to size.&lt;/p&gt;&lt;p&gt;And we're done!&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgeb_MRj4vBP8v5fVb0TVJQU-rkT6ubBRZhU61jSOpMKJGkdzU7RQ0dds631c3Dgviq0q2wrQSK1EDI8uSjQEQiLWXDtT8TuwC4ARrCfiXBes4bgXTfEeRBuwhtdLDLegBCkT8yKfKuWkibPBL3eGET5UXRvEnrKa1hT-l67LQXOAaRBu8yUjftCx0GduLg2plBaXBY0g/s762/IMG_4111_01.png" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgeb_MRj4vBP8v5fVb0TVJQU-rkT6ubBRZhU61jSOpMKJGkdzU7RQ0dds631c3Dgviq0q2wrQSK1EDI8uSjQEQiLWXDtT8TuwC4ARrCfiXBes4bgXTfEeRBuwhtdLDLegBCkT8yKfKuWkibPBL3eGET5UXRvEnrKa1hT-l67LQXOAaRBu8yUjftCx0GduLg2plBaXBY0g/s320/IMG_4111_01.png" width="315" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&amp;nbsp;The result of &lt;a href="https://archive.org/details/apple-macintosh-power-book-1400-restore-cd-french"&gt;this example&lt;/a&gt; is available on Archive.org, with the &lt;a href="https://archive.org/details/@hadess"&gt;rest of my uploads&lt;/a&gt; being made available on Archive.org and Abandonware-Magazines for those 90s magazines and their accompanying CDs.&lt;p&gt;&lt;/p&gt;&lt;p&gt;[1]: Full disclosure, I wrote a couple of articles for Linux Pratique and Linux Magazine France in the early 2000s, that were edited by that same company.&lt;/p&gt;</description><author>Bastien Nocera</author><dc:creator>Bastien Nocera</dc:creator><pubDate>Sun, 27 Jul 2025 20:39:00 GMT</pubDate><guid isPermaLink="true">https://www.hadess.net/2025/07/digitising-cds-aka-using-your-phone-as.html</guid></item><item><title>Sam Thursfield: Thoughts during GUADEC 2025</title><link>https://samthursfield.wordpress.com/2025/07/26/thoughts-during-guadec-2025/</link><description>&lt;p&gt;Greetings readers of the future from my favourite open technology event of the year. I am hanging out with the people who develop the &lt;a href="https://www.gnome.org/"&gt;GNOME platform&lt;/a&gt; talking about interesting stuff.&lt;/p&gt;
  3507.  
  3508.  
  3509.  
  3510. &lt;p&gt;Being realistic, I won&amp;#8217;t have time to make a readable writeup of the event. So I&amp;#8217;m going to set myself a challenge: how much can I write up of the event so far, in 15 minutes?&lt;/p&gt;
  3511.  
  3512.  
  3513.  
  3514. &lt;p&gt;Let&amp;#8217;s go!&lt;/p&gt;
  3515.  
  3516.  
  3517.  
  3518. &lt;h2 class="wp-block-heading"&gt;Conversations and knowledge&lt;/h2&gt;
  3519.  
  3520.  
  3521.  
  3522. &lt;p&gt;Conferences involve a series of talks, usually monologues on different topics, with slides and demos. A good talk leads to multi-way conversations.&lt;/p&gt;
  3523.  
  3524.  
  3525.  
  3526. &lt;p&gt;One thing I love about open source is: it encourages you to understand how things work. Big tech companies want you to understand nothing about your devices beyond how to put in your credit card details and send them money. Sharing knowledge is cool, though. If you know how things work then you can fix it yourself.&lt;/p&gt;
  3527.  
  3528.  
  3529.  
  3530. &lt;h2 class="wp-block-heading"&gt;Structures&lt;/h2&gt;
  3531.  
  3532.  
  3533.  
  3534. &lt;p&gt;Last year, I also attended the conference and was left with a big question for the GNOME project: &amp;#8220;What is our story?&amp;#8221; (Inspired by an excellent keynote from Ryan Sipes about the Thunderbird email app, and how it&amp;#8217;s supported by donations).&lt;/p&gt;
  3535.  
  3536.  
  3537.  
  3538. &lt;p&gt;We didn&amp;#8217;t answer that directly, but I have some new thoughts.&lt;/p&gt;
  3539.  
  3540.  
  3541.  
  3542. &lt;p&gt;Open source desktops are more popular than ever. Apparently we have like 5% of the desktop market share now. Big tech firms are nowadays run as huge piles of cash, whose story is that they need to make more cash, in order to give it to shareholders, so that one day you can, allegedly, have a pension. Their main goal isn&amp;#8217;t to make computers do interesting things. The modern for-profit corporation is a super complex institution, with great power, which is often abused.&lt;/p&gt;
  3543.  
  3544.  
  3545.  
  3546. &lt;p&gt;Open communities like GNOME are an antidote to that. With way fewer people, they nevertheless manage to produce better software in many cases, but in a way that&amp;#8217;s demanding, fun, chaotic, mostly leaderless and which frequently burns out volunteers who contribute.&lt;/p&gt;
  3547.  
  3548.  
  3549.  
  3550. &lt;p&gt;Is the GNOME project&amp;#8217;s goal to make computers do interesting things? For me, the most interesting part of the conference so far was the focus on &lt;em&gt;project structure&lt;/em&gt;. I think we learned some things about how independent, non-profit communities can work, and how they can fail, and how we can make things better.&lt;/p&gt;
  3551.  
  3552.  
  3553.  
  3554. &lt;p&gt;In a world where political structures are being heavily tested and, in many cases, are crumbling, we would do well to talk more about structures, and to introspect a bit more on what works and what doesn&amp;#8217;t. And to highlight the amazing work that the GNOME Foundation&amp;#8217;s many volunteer directors have achieved over the last 30 years to create an institution that still functions today, and in many ways functions a lot better than organizations with significantly more resources.&lt;/p&gt;
  3555.  
  3556.  
  3557.  
  3558. &lt;p&gt;Relevant talks&lt;/p&gt;
  3559.  
  3560.  
  3561.  
  3562. &lt;ul class="wp-block-list"&gt;
  3563. &lt;li&gt;Stephen Deobold&amp;#8217;s keynote&lt;/li&gt;
  3564.  
  3565.  
  3566.  
  3567. &lt;li&gt;Emmanuele&amp;#8217;s talk on teams&lt;/li&gt;
  3568. &lt;/ul&gt;
  3569.  
  3570.  
  3571.  
  3572. &lt;p&gt;&lt;/p&gt;
  3573.  
  3574.  
  3575.  
  3576. &lt;h2 class="wp-block-heading"&gt;Teams&lt;/h2&gt;
  3577.  
  3578.  
  3579.  
  3580. &lt;p&gt;Emmanuele Bassi tried, in a friendly way, to set fire to long-standing structures around how the GNOME community agrees and disagrees changes to the platform. Based on ideas from other successful projects that are driven by independent, non-profit communities such as the Rust and Python programming languages.&lt;/p&gt;
  3581.  
  3582.  
  3583.  
  3584. &lt;p&gt;&lt;/p&gt;
  3585.  
  3586.  
  3587.  
  3588. &lt;p&gt;Part of this idea is to create well-defined teams of people who collaborate on different parts of the GNOME platform.&lt;/p&gt;
  3589.  
  3590.  
  3591.  
  3592. &lt;p&gt;I&amp;#8217;ve been contributing to GNOME in different ways for a &lt;em&gt;loooong&lt;/em&gt; time, partly due to my day job, where I sometimes work with the technology stack, and partly because its a great group of people, we get to meet around the world once a year, and make software that&amp;#8217;s a little more independent from the excesses and the exploitation of modern capitalism, or technofuedalism. &lt;/p&gt;
  3593.  
  3594.  
  3595.  
  3596. &lt;p&gt;And I think it&amp;#8217;s going to be really helpful to organize my contributions according to a team structure with a defined form.&lt;/p&gt;
  3597.  
  3598.  
  3599.  
  3600. &lt;h2 class="wp-block-heading"&gt;Search&lt;/h2&gt;
  3601.  
  3602.  
  3603.  
  3604. &lt;p&gt;I really hope we&amp;#8217;ll have a search team.&lt;/p&gt;
  3605.  
  3606.  
  3607.  
  3608. &lt;p&gt;I don&amp;#8217;t have much news about search. GNOME&amp;#8217;s indexer (localsearch) might start indexing the whole home directory soon. Carlos Garnacho continues to heroically make it work really well.&lt;/p&gt;
  3609.  
  3610.  
  3611.  
  3612. &lt;h2 class="wp-block-heading"&gt;QA / Testing / Developer Experience&lt;/h2&gt;
  3613.  
  3614.  
  3615.  
  3616. &lt;p&gt;I did a talk at the conference (and half of another one with MartΓ­n Abente Lahaye) , about end-to-end testing using openQA.&lt;/p&gt;
  3617.  
  3618.  
  3619.  
  3620. &lt;p&gt;The talks were pretty successful, they lead to some interesting conversations with new people. I hope we&amp;#8217;ll continue to grow the Linux QA call and try to keep these conversations going, and try to share knowledge and create better structures so that paid QA engineers who are testing products built with GNOME can collaborate on testing upstream.&lt;/p&gt;
  3621.  
  3622.  
  3623.  
  3624. &lt;h2 class="wp-block-heading"&gt;Freeform notes&lt;/h2&gt;
  3625.  
  3626.  
  3627.  
  3628. &lt;p&gt;I&amp;#8217;m 8 minutes over time already so the rest of this will be freeform notes from my notepad.&lt;/p&gt;
  3629.  
  3630.  
  3631.  
  3632. &lt;p&gt;Live-coding streams aren&amp;#8217;t something I watch or create. It&amp;#8217;s an interesting way to share knowledge with the new generation of people who have grown up with internet videos as a primary knowledge source. I don&amp;#8217;t have age stats for this blog, but I&amp;#8217;m curious how many readers under 30 have read this far down. (Leave a comment if you read this and prove me wrong! : -)&lt;/p&gt;
  3633.  
  3634.  
  3635.  
  3636. &lt;p&gt;systemd-sysexts for development are going to catch on.&lt;/p&gt;
  3637.  
  3638.  
  3639.  
  3640. &lt;p&gt;There should be karaoke every year.&lt;/p&gt;
  3641.  
  3642.  
  3643.  
  3644. &lt;p&gt;Fedora Silverblue isn&amp;#8217;t actively developed at the moment. bootc is something to keep an eye on.&lt;/p&gt;
  3645.  
  3646.  
  3647.  
  3648. &lt;p&gt;GNOME Shell Extensions are really popular and are a good &amp;#8220;gateway drug&amp;#8221; to get newcomers involved. Nobody figured out a good automated testing story for these yet. I wonder if there&amp;#8217;s a QA project there? I wonder if there&amp;#8217;s a low-cost way to allow extension developers to test extensions?&lt;/p&gt;
  3649.  
  3650.  
  3651.  
  3652. &lt;p&gt;Legacy code is &amp;#8220;code without tests&amp;#8221;. I&amp;#8217;m not sure I agree with that.&lt;/p&gt;
  3653.  
  3654.  
  3655.  
  3656. &lt;p&gt;&amp;#8220;Toolkits are transient, apps are forever&amp;#8221;. That&amp;#8217;s spot-on.&lt;/p&gt;
  3657.  
  3658.  
  3659.  
  3660. &lt;p&gt;There is a spectrum between being a user and a developer. It&amp;#8217;s not a black-and-white distinction.&lt;/p&gt;
  3661.  
  3662.  
  3663.  
  3664. &lt;p&gt;BuildStream is still difficult to learn and the documentation isn&amp;#8217;t a helpful getting started guide for newcomers.&lt;/p&gt;
  3665.  
  3666.  
  3667.  
  3668. &lt;p&gt;We need more live demos of accessibility tools. I still don&amp;#8217;t know how you use the screen reader. I&amp;#8217;d like to have the computer read to me.&lt;/p&gt;
  3669.  
  3670.  
  3671.  
  3672. &lt;p&gt;That&amp;#8217;s it for now. It took 34 minutes to empty my brain into my blog, more than planned, but a necessary step. Hope some of it was interesting. See you soon!&lt;/p&gt;
  3673.  
  3674.  
  3675.  
  3676. &lt;p&gt;&lt;/p&gt;</description><author>Sam Thursfield</author><dc:creator>Sam Thursfield</dc:creator><pubDate>Sat, 26 Jul 2025 17:19:59 GMT</pubDate><guid isPermaLink="true">https://samthursfield.wordpress.com/2025/07/26/thoughts-during-guadec-2025/</guid></item><item><title>Nick Richards: Octopus Agile Prices For Linux</title><link>https://www.nedrichards.com/2025/07/octopus-agile-prices-for-linux/</link><description>&lt;p&gt;I&amp;rsquo;m on the &lt;a href="https://octopus.energy/smart/agile/"&gt;Octopus Agile&lt;/a&gt; electricity tariff, where the price changes every half hour based on wholesale costs. This is great for saving money and using less carbon intensive energy, provided you can shift your heavy usage to cheaper times. With a family that insists on eating at a normal hour, that mostly means scheduling the dishwasher and washing machine.&lt;/p&gt;
  3677. &lt;p&gt;The snag was not having an easy way to see upcoming prices on my Linux laptop. To scratch that itch, I built a small GTK app: &lt;a href="https://github.com/nedrichards/octopus-agile-energy/"&gt;Octopus Agile Energy&lt;/a&gt;. You can use it yourself if you&amp;rsquo;re in the UK and have this electricity tarriff. Either install it directly &lt;a href="https://flathub.org/apps/com.nedrichards.octopusagile"&gt;from Flathub&lt;/a&gt; or download the source code and &amp;lsquo;press play&amp;rsquo; in GNOME Builder. The app is heavily inspired by the excellent &lt;a href="https://play.google.com/store/apps/details?id=com.m4consulting.octopus_comparison"&gt;Octopus Compare&lt;/a&gt; for mobile but I stripped the concept back to a single job: what&amp;rsquo;s the price now and for the next 24 hours? This felt right for a simple desktop utility and was achievable with a bit of JSON parsing and some hand waving.&lt;/p&gt;
  3678. &lt;p&gt;&lt;img alt="Screenshot of the Octopus Agile Energy app showing the current electricity price and a graph of future prices" src="https://github.com/nedrichards/octopus-agile-energy/raw/main/data/octopus-agile-screenshot.png" /&gt;&lt;/p&gt;
  3679. &lt;p&gt;I wrote a good chunk of the Python for it with the &lt;a href="https://github.com/google-gemini/gemini-cli"&gt;gemini-cli&lt;/a&gt;, which was a pleasant surprise. My workflow was running Gemini in a &lt;a href="https://containertoolbx.org/"&gt;Toolbx&lt;/a&gt; container, building on my &lt;a href="https://fedoraproject.org/atomic-desktops/silverblue/"&gt;Silverblue&lt;/a&gt; desktop with GNOME Builder, and manually feeding back any errors. I kept myself in the loop, taking my own screenshots of visual issues rather than letting the model run completely free and using integrations like &lt;a href="https://github.com/bilelmoussaoui/gnome-mcp-server?tab=readme-ov-file"&gt;gnome-mcp-server&lt;/a&gt; to inspect itself.&lt;/p&gt;
  3680. &lt;p&gt;It&amp;rsquo;s genuinely fun to make apps with GTK 4, libadwaita, and Python. The modern stack has a much lower barrier to entry than the GTK-based frameworks I&amp;rsquo;ve worked on in the past. And while I have my reservations about cloud-hosted AI, using this kind of technology feels like a step towards giving users more control over their computing, not less. Of course, the 25 years of experience I have in software development helped bridge the gap between a semi-working prototype that only served one specific pricing configuration, didn&amp;rsquo;t cache anything and was constantly re-rendering; and an actual app. The AI isn&amp;rsquo;t quite there yet at all, but the potential is there and a locally hosted system by and for the free software ecosystem would be super handy.&lt;/p&gt;
  3681. &lt;p&gt;I hope the app is useful. Whilst I may well make some tweaks or changes this does exactly what I want and I&amp;rsquo;d encourage anyone interested to fork the code and build something that makes them happy.&lt;/p&gt;</description><author>Nick Richards</author><dc:creator>Nick Richards</dc:creator><pubDate>Sat, 26 Jul 2025 17:01:29 GMT</pubDate><guid isPermaLink="true">https://www.nedrichards.com/2025/07/octopus-agile-prices-for-linux/</guid></item><item><title>Nancy Nyambura: Outreachy Update:Understanding and Improving def-extractor.py</title><link>https://nanciedev.wordpress.com/2025/07/25/outreachy-updateunderstanding-and-improving-def-extractor-py/</link><description>&lt;h3 class="wp-block-heading"&gt;&lt;strong&gt;Introduction&lt;/strong&gt;&lt;/h3&gt;
  3682.  
  3683.  
  3684.  
  3685. &lt;p&gt;Over the past couple of weeks, I have been working on understanding and improving&amp;nbsp;&lt;strong&gt;def-extractor.py&lt;/strong&gt;, a Python script that processes dictionary data from Wiktionary to generate word lists and definitions in structured formats. My main task has been to refactor the script to use configuration files instead of hardcoded values, making it more flexible and maintainable.&lt;/p&gt;
  3686.  
  3687.  
  3688.  
  3689. &lt;p&gt;In this blog post, I’ll explain:&lt;/p&gt;
  3690.  
  3691.  
  3692.  
  3693. &lt;ol class="wp-block-list" start="1"&gt;
  3694. &lt;li&gt;&lt;strong&gt;What the script does&lt;/strong&gt;&lt;/li&gt;
  3695.  
  3696.  
  3697.  
  3698. &lt;li&gt;&lt;strong&gt;How it works under the hood&lt;/strong&gt;&lt;/li&gt;
  3699.  
  3700.  
  3701.  
  3702. &lt;li&gt;&lt;strong&gt;The changes I made to improve it&lt;/strong&gt;&lt;/li&gt;
  3703.  
  3704.  
  3705.  
  3706. &lt;li&gt;&lt;strong&gt;Why these changes matter&lt;/strong&gt;&lt;/li&gt;
  3707. &lt;/ol&gt;
  3708.  
  3709.  
  3710.  
  3711. &lt;h2 class="wp-block-heading"&gt;&lt;strong&gt;What Does the Script Do?&lt;/strong&gt;&lt;/h2&gt;
  3712.  
  3713.  
  3714.  
  3715. &lt;p&gt;At a high level, this script processes huge JSONL (JSON Lines) dictionary dumps, like the ones from &lt;a class="" href="https://kaikki.org"&gt;Kaikki.org&lt;/a&gt; ,  and filters them down into clean, usable formats.&lt;/p&gt;
  3716.  
  3717.  
  3718.  
  3719. &lt;p&gt;The&amp;nbsp;&lt;strong&gt;def-extractor.py&lt;/strong&gt;&amp;nbsp;script takes raw dictionary data (from Wiktionary) and processes it into structured formats like:&lt;/p&gt;
  3720.  
  3721.  
  3722.  
  3723. &lt;ul class="wp-block-list"&gt;
  3724. &lt;li&gt;&lt;strong&gt;Filtered word lists&lt;/strong&gt;&amp;nbsp;(JSONL)&lt;/li&gt;
  3725.  
  3726.  
  3727.  
  3728. &lt;li&gt;&lt;strong&gt;GVariant binary files&lt;/strong&gt;&amp;nbsp;(for efficient storage)&lt;/li&gt;
  3729.  
  3730.  
  3731.  
  3732. &lt;li&gt;&lt;strong&gt;Enum tables&lt;/strong&gt;&amp;nbsp;(for parts of speech &amp;amp; word tags)&lt;/li&gt;
  3733. &lt;/ul&gt;
  3734.  
  3735.  
  3736.  
  3737. &lt;p&gt;It was originally designed to work with specific word lists (Wordnik, Broda, and a test list), but my goal is to make it&amp;nbsp;&lt;strong&gt;configurable&lt;/strong&gt;&amp;nbsp;so it could support any word list with a simple config file.&lt;/p&gt;
  3738.  
  3739.  
  3740.  
  3741. &lt;h2 class="wp-block-heading"&gt;&lt;strong&gt;How It Works (Step by Step)&lt;/strong&gt;&lt;/h2&gt;
  3742.  
  3743.  
  3744.  
  3745. &lt;h3 class="wp-block-heading"&gt;&lt;strong&gt;1. Loading the Word List&lt;/strong&gt;&lt;/h3&gt;
  3746.  
  3747.  
  3748.  
  3749. &lt;p&gt;The script starts by loading a word list (e.g., Wordnik’s list of common English words). It filters out invalid words (too short, contain numbers, etc.) and stores them in a hash table for quick lookup.&lt;/p&gt;
  3750.  
  3751.  
  3752.  
  3753. &lt;h3 class="wp-block-heading"&gt;&lt;strong&gt;2. Filtering Raw Wiktionary Data&lt;/strong&gt;&lt;/h3&gt;
  3754.  
  3755.  
  3756.  
  3757. &lt;p&gt;Next, it processes a massive&amp;nbsp;&lt;strong&gt;raw-wiktextract-data.jsonl&lt;/strong&gt;&amp;nbsp;file (theWiktionary dump) and keeps only entries that:&lt;/p&gt;
  3758.  
  3759.  
  3760.  
  3761. &lt;ul class="wp-block-list"&gt;
  3762. &lt;li&gt;Match words from the loaded word list&lt;/li&gt;
  3763.  
  3764.  
  3765.  
  3766. &lt;li&gt;Are in the correct language (e.g., English)&lt;/li&gt;
  3767. &lt;/ul&gt;
  3768.  
  3769.  
  3770.  
  3771. &lt;figure class="wp-block-image size-large"&gt;&lt;img alt="" class="wp-image-29" height="620" src="https://nanciedev.wordpress.com/wp-content/uploads/2025/07/filter.png?w=608" width="608" /&gt;&lt;/figure&gt;
  3772.  
  3773.  
  3774.  
  3775. &lt;h3 class="wp-block-heading"&gt;&lt;strong&gt;3. Generating Structured Outputs&lt;/strong&gt;&lt;/h3&gt;
  3776.  
  3777.  
  3778.  
  3779. &lt;p&gt;After filtering, the script creates:&lt;/p&gt;
  3780.  
  3781.  
  3782.  
  3783. &lt;ul class="wp-block-list"&gt;
  3784. &lt;li&gt;&lt;strong&gt;Enum tables&lt;/strong&gt;&amp;nbsp;(JSON files listing parts of speech &amp;amp; word tags)&lt;/li&gt;
  3785.  
  3786.  
  3787.  
  3788. &lt;li&gt;&lt;strong&gt;GVariant files&lt;/strong&gt;&amp;nbsp;(binary files for efficient storage and fast lookup)&lt;/li&gt;
  3789. &lt;/ul&gt;
  3790.  
  3791.  
  3792.  
  3793. &lt;figure class="wp-block-image size-large"&gt;&lt;img alt="" class="wp-image-31" height="418" src="https://nanciedev.wordpress.com/wp-content/uploads/2025/07/gvariant.png?w=751" width="751" /&gt;&lt;/figure&gt;
  3794.  
  3795.  
  3796.  
  3797. &lt;hr class="wp-block-separator has-alpha-channel-opacity" /&gt;
  3798.  
  3799.  
  3800.  
  3801. &lt;h2 class="wp-block-heading"&gt;&lt;strong&gt;What Changes have  I Made?&lt;/strong&gt;&lt;/h2&gt;
  3802.  
  3803.  
  3804.  
  3805. &lt;h3 class="wp-block-heading"&gt;&lt;strong&gt;1. Added Configuration Support&lt;/strong&gt;&lt;/h3&gt;
  3806.  
  3807.  
  3808.  
  3809. &lt;p&gt;Originally, the script uses hardcoded paths and settings. I modified it to read from&amp;nbsp;&lt;strong&gt;.config files&lt;/strong&gt;, allowing users to define:&lt;/p&gt;
  3810.  
  3811.  
  3812.  
  3813. &lt;ul class="wp-block-list"&gt;
  3814. &lt;li&gt;&lt;strong&gt;Source word list file&lt;/strong&gt;&lt;/li&gt;
  3815.  
  3816.  
  3817.  
  3818. &lt;li&gt;&lt;strong&gt;Output directory&lt;/strong&gt;&lt;/li&gt;
  3819.  
  3820.  
  3821.  
  3822. &lt;li&gt;&lt;strong&gt;Word validation rules&lt;/strong&gt;&amp;nbsp;(min/max length, allowed characters)&lt;/li&gt;
  3823. &lt;/ul&gt;
  3824.  
  3825.  
  3826.  
  3827. &lt;p&gt;&lt;strong&gt;Before (Hardcoded):&lt;/strong&gt;&lt;/p&gt;
  3828.  
  3829.  
  3830.  
  3831. &lt;pre class="wp-block-preformatted"&gt;&lt;code&gt;WORDNIK_LIST = "wordlist-20210729.txt"&lt;br /&gt;ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"&lt;/code&gt;&lt;/pre&gt;
  3832.  
  3833.  
  3834.  
  3835. &lt;p&gt;&lt;strong&gt;After (Configurable):&lt;/strong&gt;&lt;/p&gt;
  3836.  
  3837.  
  3838.  
  3839. &lt;p&gt;&lt;code&gt;ini&lt;/code&gt;&lt;/p&gt;
  3840.  
  3841.  
  3842.  
  3843. &lt;pre class="wp-block-preformatted"&gt;&lt;code&gt;[Word List]&lt;br /&gt;Source = my-wordlist.txt&lt;br /&gt;MinLength = 2&lt;br /&gt;MaxLength = 20&lt;/code&gt;&lt;/pre&gt;
  3844.  
  3845.  
  3846.  
  3847. &lt;h3 class="wp-block-heading"&gt;&lt;strong&gt;2. Improved File Path Handling&lt;/strong&gt;&lt;/h3&gt;
  3848.  
  3849.  
  3850.  
  3851. &lt;p&gt;Instead of hardcoding paths, the script now constructs them dynamically:&lt;/p&gt;
  3852.  
  3853.  
  3854.  
  3855. &lt;pre class="wp-block-preformatted"&gt;output_path = os.path.join(config.word_lists_dir, f"{config.id}-filtered.jsonl")&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;
  3856.  
  3857.  
  3858.  
  3859. &lt;h2 class="wp-block-heading"&gt;&lt;strong&gt;Why Do These Changes Matter?&lt;/strong&gt;&lt;/h2&gt;
  3860.  
  3861.  
  3862.  
  3863. &lt;p&gt;&lt;strong&gt;Flexibility&lt;/strong&gt;&amp;nbsp;-Now supports any word list via config files.&lt;br /&gt;&lt;strong&gt;Maintainability&lt;/strong&gt;&amp;#8211; No more editing code to change paths or rules.&lt;br /&gt;&lt;strong&gt;Scalability&lt;/strong&gt;&amp;nbsp;-Easier to add new word lists or languages.&lt;br /&gt;&lt;strong&gt;Consistency&lt;/strong&gt;&amp;nbsp;-All settings are  in configs.&lt;/p&gt;
  3864.  
  3865.  
  3866.  
  3867. &lt;p&gt;Next Steps?&lt;/p&gt;
  3868.  
  3869.  
  3870.  
  3871. &lt;h3 class="wp-block-heading"&gt;&lt;strong&gt;1. Better Error Handling&lt;/strong&gt;&lt;/h3&gt;
  3872.  
  3873.  
  3874.  
  3875. &lt;p&gt;I am working on adding  checks for:&lt;/p&gt;
  3876.  
  3877.  
  3878.  
  3879. &lt;ul class="wp-block-list"&gt;
  3880. &lt;li&gt;Missing config fields&lt;/li&gt;
  3881.  
  3882.  
  3883.  
  3884. &lt;li&gt;Invalid word list files&lt;/li&gt;
  3885.  
  3886.  
  3887.  
  3888. &lt;li&gt;Incorrectly formatted data&lt;/li&gt;
  3889.  
  3890.  
  3891.  
  3892. &lt;li&gt;&lt;/li&gt;
  3893. &lt;/ul&gt;
  3894.  
  3895.  
  3896.  
  3897. &lt;h3 class="wp-block-heading"&gt;&lt;strong&gt;2. Unified Word Loading Logic&lt;/strong&gt;&lt;/h3&gt;
  3898.  
  3899.  
  3900.  
  3901. &lt;p&gt;There are separate functions (&lt;code&gt;load_wordnik()&lt;/code&gt;,&amp;nbsp;&lt;code&gt;load_broda()&lt;/code&gt;). &lt;/p&gt;
  3902.  
  3903.  
  3904.  
  3905. &lt;p&gt;I  want to merged them into one&amp;nbsp;&lt;strong&gt;&lt;code&gt;load_words(config)&lt;/code&gt;&lt;/strong&gt;&amp;nbsp;that would works for any word list.&lt;/p&gt;
  3906.  
  3907.  
  3908.  
  3909. &lt;p&gt;&amp;nbsp;3.  Refactor legacy code&amp;nbsp;for better structure&lt;/p&gt;
  3910.  
  3911.  
  3912.  
  3913. &lt;h3 class="wp-block-heading"&gt;&lt;strong&gt;Try It Yourself&lt;/strong&gt;&lt;/h3&gt;
  3914.  
  3915.  
  3916.  
  3917. &lt;ol class="wp-block-list" start="1"&gt;
  3918. &lt;li&gt;Download the script: [&lt;a href="https://gitlab.gnome.org/jrb/word-list-tools"&gt;wordlist-Gitlab&lt;/a&gt;]&lt;/li&gt;
  3919.  
  3920.  
  3921.  
  3922. &lt;li&gt;Create a&amp;nbsp;&lt;code&gt;.&lt;/code&gt;conf&amp;nbsp;config file&lt;/li&gt;
  3923.  
  3924.  
  3925.  
  3926. &lt;li&gt; Run:&lt;code&gt; python3 def-extractor.py --config my-wordlist.conf filtered-list&lt;/code&gt;&lt;/li&gt;
  3927. &lt;/ol&gt;
  3928.  
  3929.  
  3930.  
  3931. &lt;p&gt;Happy coding! &lt;/p&gt;</description><author>Nancy Nyambura</author><dc:creator>Nancy Nyambura</dc:creator><pubDate>Fri, 25 Jul 2025 14:05:26 GMT</pubDate><guid isPermaLink="true">https://nanciedev.wordpress.com/2025/07/25/outreachy-updateunderstanding-and-improving-def-extractor-py/</guid></item><item><title>Nancy Wairimu Nyambura: Outreachy Update:Understanding and Improving def-extractor.py</title><link>https://nanciedev.wordpress.com/2025/07/25/outreachy-updateunderstanding-and-improving-def-extractor-py/</link><description>&lt;h3 class="wp-block-heading"&gt;&lt;strong&gt;Introduction&lt;/strong&gt;&lt;/h3&gt;
  3932.  
  3933.  
  3934.  
  3935. &lt;p&gt;Over the past couple of weeks, I have been working on understanding and improving&amp;nbsp;&lt;strong&gt;def-extractor.py&lt;/strong&gt;, a Python script that processes dictionary data from Wiktionary to generate word lists and definitions in structured formats. My main task has been to refactor the script to use configuration files instead of hardcoded values, making it more flexible and maintainable.&lt;/p&gt;
  3936.  
  3937.  
  3938.  
  3939. &lt;p&gt;In this blog post, I’ll explain:&lt;/p&gt;
  3940.  
  3941.  
  3942.  
  3943. &lt;ol class="wp-block-list" start="1"&gt;
  3944. &lt;li&gt;&lt;strong&gt;What the script does&lt;/strong&gt;&lt;/li&gt;
  3945.  
  3946.  
  3947.  
  3948. &lt;li&gt;&lt;strong&gt;How it works under the hood&lt;/strong&gt;&lt;/li&gt;
  3949.  
  3950.  
  3951.  
  3952. &lt;li&gt;&lt;strong&gt;The changes I made to improve it&lt;/strong&gt;&lt;/li&gt;
  3953.  
  3954.  
  3955.  
  3956. &lt;li&gt;&lt;strong&gt;Why these changes matter&lt;/strong&gt;&lt;/li&gt;
  3957. &lt;/ol&gt;
  3958.  
  3959.  
  3960.  
  3961. &lt;h2 class="wp-block-heading"&gt;&lt;strong&gt;What Does the Script Do?&lt;/strong&gt;&lt;/h2&gt;
  3962.  
  3963.  
  3964.  
  3965. &lt;p&gt;At a high level, this script processes huge JSONL (JSON Lines) dictionary dumps, like the ones from &lt;a class="" href="https://kaikki.org"&gt;Kaikki.org&lt;/a&gt; ,  and filters them down into clean, usable formats.&lt;/p&gt;
  3966.  
  3967.  
  3968.  
  3969. &lt;p&gt;The&amp;nbsp;&lt;strong&gt;def-extractor.py&lt;/strong&gt;&amp;nbsp;script takes raw dictionary data (from Wiktionary) and processes it into structured formats like:&lt;/p&gt;
  3970.  
  3971.  
  3972.  
  3973. &lt;ul class="wp-block-list"&gt;
  3974. &lt;li&gt;&lt;strong&gt;Filtered word lists&lt;/strong&gt;&amp;nbsp;(JSONL)&lt;/li&gt;
  3975.  
  3976.  
  3977.  
  3978. &lt;li&gt;&lt;strong&gt;GVariant binary files&lt;/strong&gt;&amp;nbsp;(for efficient storage)&lt;/li&gt;
  3979.  
  3980.  
  3981.  
  3982. &lt;li&gt;&lt;strong&gt;Enum tables&lt;/strong&gt;&amp;nbsp;(for parts of speech &amp;amp; word tags)&lt;/li&gt;
  3983. &lt;/ul&gt;
  3984.  
  3985.  
  3986.  
  3987. &lt;p&gt;It was originally designed to work with specific word lists (Wordnik, Broda, and a test list), but my goal is to make it&amp;nbsp;&lt;strong&gt;configurable&lt;/strong&gt;&amp;nbsp;so it could support any word list with a simple config file.&lt;/p&gt;
  3988.  
  3989.  
  3990.  
  3991. &lt;h2 class="wp-block-heading"&gt;&lt;strong&gt;How It Works (Step by Step)&lt;/strong&gt;&lt;/h2&gt;
  3992.  
  3993.  
  3994.  
  3995. &lt;h3 class="wp-block-heading"&gt;&lt;strong&gt;1. Loading the Word List&lt;/strong&gt;&lt;/h3&gt;
  3996.  
  3997.  
  3998.  
  3999. &lt;p&gt;The script starts by loading a word list (e.g., Wordnik’s list of common English words). It filters out invalid words (too short, contain numbers, etc.) and stores them in a hash table for quick lookup.&lt;/p&gt;
  4000.  
  4001.  
  4002.  
  4003. &lt;h3 class="wp-block-heading"&gt;&lt;strong&gt;2. Filtering Raw Wiktionary Data&lt;/strong&gt;&lt;/h3&gt;
  4004.  
  4005.  
  4006.  
  4007. &lt;p&gt;Next, it processes a massive&amp;nbsp;&lt;strong&gt;raw-wiktextract-data.jsonl&lt;/strong&gt;&amp;nbsp;file (theWiktionary dump) and keeps only entries that:&lt;/p&gt;
  4008.  
  4009.  
  4010.  
  4011. &lt;ul class="wp-block-list"&gt;
  4012. &lt;li&gt;Match words from the loaded word list&lt;/li&gt;
  4013.  
  4014.  
  4015.  
  4016. &lt;li&gt;Are in the correct language (e.g., English)&lt;/li&gt;
  4017. &lt;/ul&gt;
  4018.  
  4019.  
  4020.  
  4021. &lt;figure class="wp-block-image size-large"&gt;&lt;img alt="" class="wp-image-29" height="620" src="https://nanciedev.wordpress.com/wp-content/uploads/2025/07/filter.png?w=608" width="608" /&gt;&lt;/figure&gt;
  4022.  
  4023.  
  4024.  
  4025. &lt;h3 class="wp-block-heading"&gt;&lt;strong&gt;3. Generating Structured Outputs&lt;/strong&gt;&lt;/h3&gt;
  4026.  
  4027.  
  4028.  
  4029. &lt;p&gt;After filtering, the script creates:&lt;/p&gt;
  4030.  
  4031.  
  4032.  
  4033. &lt;ul class="wp-block-list"&gt;
  4034. &lt;li&gt;&lt;strong&gt;Enum tables&lt;/strong&gt;&amp;nbsp;(JSON files listing parts of speech &amp;amp; word tags)&lt;/li&gt;
  4035.  
  4036.  
  4037.  
  4038. &lt;li&gt;&lt;strong&gt;GVariant files&lt;/strong&gt;&amp;nbsp;(binary files for efficient storage and fast lookup)&lt;/li&gt;
  4039. &lt;/ul&gt;
  4040.  
  4041.  
  4042.  
  4043. &lt;figure class="wp-block-image size-large"&gt;&lt;img alt="" class="wp-image-31" height="418" src="https://nanciedev.wordpress.com/wp-content/uploads/2025/07/gvariant.png?w=751" width="751" /&gt;&lt;/figure&gt;
  4044.  
  4045.  
  4046.  
  4047. &lt;hr class="wp-block-separator has-alpha-channel-opacity" /&gt;
  4048.  
  4049.  
  4050.  
  4051. &lt;h2 class="wp-block-heading"&gt;&lt;strong&gt;What Changes have  I Made?&lt;/strong&gt;&lt;/h2&gt;
  4052.  
  4053.  
  4054.  
  4055. &lt;h3 class="wp-block-heading"&gt;&lt;strong&gt;1. Added Configuration Support&lt;/strong&gt;&lt;/h3&gt;
  4056.  
  4057.  
  4058.  
  4059. &lt;p&gt;Originally, the script uses hardcoded paths and settings. I modified it to read from&amp;nbsp;&lt;strong&gt;.config files&lt;/strong&gt;, allowing users to define:&lt;/p&gt;
  4060.  
  4061.  
  4062.  
  4063. &lt;ul class="wp-block-list"&gt;
  4064. &lt;li&gt;&lt;strong&gt;Source word list file&lt;/strong&gt;&lt;/li&gt;
  4065.  
  4066.  
  4067.  
  4068. &lt;li&gt;&lt;strong&gt;Output directory&lt;/strong&gt;&lt;/li&gt;
  4069.  
  4070.  
  4071.  
  4072. &lt;li&gt;&lt;strong&gt;Word validation rules&lt;/strong&gt;&amp;nbsp;(min/max length, allowed characters)&lt;/li&gt;
  4073. &lt;/ul&gt;
  4074.  
  4075.  
  4076.  
  4077. &lt;p&gt;&lt;strong&gt;Before (Hardcoded):&lt;/strong&gt;&lt;/p&gt;
  4078.  
  4079.  
  4080.  
  4081. &lt;pre class="wp-block-preformatted"&gt;&lt;code&gt;WORDNIK_LIST = "wordlist-20210729.txt"&lt;br /&gt;ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"&lt;/code&gt;&lt;/pre&gt;
  4082.  
  4083.  
  4084.  
  4085. &lt;p&gt;&lt;strong&gt;After (Configurable):&lt;/strong&gt;&lt;/p&gt;
  4086.  
  4087.  
  4088.  
  4089. &lt;p&gt;&lt;code&gt;ini&lt;/code&gt;&lt;/p&gt;
  4090.  
  4091.  
  4092.  
  4093. &lt;pre class="wp-block-preformatted"&gt;&lt;code&gt;[Word List]&lt;br /&gt;Source = my-wordlist.txt&lt;br /&gt;MinLength = 2&lt;br /&gt;MaxLength = 20&lt;/code&gt;&lt;/pre&gt;
  4094.  
  4095.  
  4096.  
  4097. &lt;h3 class="wp-block-heading"&gt;&lt;strong&gt;2. Improved File Path Handling&lt;/strong&gt;&lt;/h3&gt;
  4098.  
  4099.  
  4100.  
  4101. &lt;p&gt;Instead of hardcoding paths, the script now constructs them dynamically:&lt;/p&gt;
  4102.  
  4103.  
  4104.  
  4105. &lt;pre class="wp-block-preformatted"&gt;output_path = os.path.join(config.word_lists_dir, f"{config.id}-filtered.jsonl")&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;
  4106.  
  4107.  
  4108.  
  4109. &lt;h2 class="wp-block-heading"&gt;&lt;strong&gt;Why Do These Changes Matter?&lt;/strong&gt;&lt;/h2&gt;
  4110.  
  4111.  
  4112.  
  4113. &lt;p&gt;&lt;strong&gt;Flexibility&lt;/strong&gt;&amp;nbsp;-Now supports any word list via config files.&lt;br /&gt;&lt;strong&gt;Maintainability&lt;/strong&gt;&amp;#8211; No more editing code to change paths or rules.&lt;br /&gt;&lt;strong&gt;Scalability&lt;/strong&gt;&amp;nbsp;-Easier to add new word lists or languages.&lt;br /&gt;&lt;strong&gt;Consistency&lt;/strong&gt;&amp;nbsp;-All settings are  in configs.&lt;/p&gt;
  4114.  
  4115.  
  4116.  
  4117. &lt;p&gt;Next Steps?&lt;/p&gt;
  4118.  
  4119.  
  4120.  
  4121. &lt;h3 class="wp-block-heading"&gt;&lt;strong&gt;1. Better Error Handling&lt;/strong&gt;&lt;/h3&gt;
  4122.  
  4123.  
  4124.  
  4125. &lt;p&gt;I am working on adding  checks for:&lt;/p&gt;
  4126.  
  4127.  
  4128.  
  4129. &lt;ul class="wp-block-list"&gt;
  4130. &lt;li&gt;Missing config fields&lt;/li&gt;
  4131.  
  4132.  
  4133.  
  4134. &lt;li&gt;Invalid word list files&lt;/li&gt;
  4135.  
  4136.  
  4137.  
  4138. &lt;li&gt;Incorrectly formatted data&lt;/li&gt;
  4139.  
  4140.  
  4141.  
  4142. &lt;li&gt;&lt;/li&gt;
  4143. &lt;/ul&gt;
  4144.  
  4145.  
  4146.  
  4147. &lt;h3 class="wp-block-heading"&gt;&lt;strong&gt;2. Unified Word Loading Logic&lt;/strong&gt;&lt;/h3&gt;
  4148.  
  4149.  
  4150.  
  4151. &lt;p&gt;There are separate functions (&lt;code&gt;load_wordnik()&lt;/code&gt;,&amp;nbsp;&lt;code&gt;load_broda()&lt;/code&gt;). &lt;/p&gt;
  4152.  
  4153.  
  4154.  
  4155. &lt;p&gt;I  want to merged them into one&amp;nbsp;&lt;strong&gt;&lt;code&gt;load_words(config)&lt;/code&gt;&lt;/strong&gt;&amp;nbsp;that would works for any word list.&lt;/p&gt;
  4156.  
  4157.  
  4158.  
  4159. &lt;p&gt;&amp;nbsp;3.  Refactor legacy code&amp;nbsp;for better structure&lt;/p&gt;
  4160.  
  4161.  
  4162.  
  4163. &lt;h3 class="wp-block-heading"&gt;&lt;strong&gt;Try It Yourself&lt;/strong&gt;&lt;/h3&gt;
  4164.  
  4165.  
  4166.  
  4167. &lt;ol class="wp-block-list" start="1"&gt;
  4168. &lt;li&gt;Download the script: [&lt;a href="https://gitlab.gnome.org/jrb/word-list-tools"&gt;wordlist-Gitlab&lt;/a&gt;]&lt;/li&gt;
  4169.  
  4170.  
  4171.  
  4172. &lt;li&gt;Create a&amp;nbsp;&lt;code&gt;.&lt;/code&gt;conf&amp;nbsp;config file&lt;/li&gt;
  4173.  
  4174.  
  4175.  
  4176. &lt;li&gt; Run:&lt;code&gt; python3 def-extractor.py --config my-wordlist.conf filtered-list&lt;/code&gt;&lt;/li&gt;
  4177. &lt;/ol&gt;
  4178.  
  4179.  
  4180.  
  4181. &lt;p&gt;Happy coding! &lt;/p&gt;</description><author>Nancy Wairimu Nyambura</author><dc:creator>Nancy Wairimu Nyambura</dc:creator><pubDate>Fri, 25 Jul 2025 14:05:26 GMT</pubDate><guid isPermaLink="true">https://nanciedev.wordpress.com/2025/07/25/outreachy-updateunderstanding-and-improving-def-extractor-py/</guid></item><item><title>Hari Rana: GNOME Calendar: A New Era of Accessibility Achieved in 90 Days</title><link>https://tesk.page/2025/07/25/gnome-calendar-a-new-era-of-accessibility-achieved-in-90-days/</link><description>&lt;div class="statements"&gt; &lt;section class="card note"&gt; &lt;strong class="big"&gt;Note&lt;/strong&gt; &lt;p&gt; &lt;p&gt;Please consider supporting my effort in making GNOME apps accessible for everybody. Thanks!&lt;/p&gt; &lt;ul&gt; &lt;li&gt;&lt;a href="https://liberapay.com/TheEvilSkeleton"&gt;Liberapay&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href="https://ko-fi.com/theevilskeleton"&gt;Ko-fi&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href="https://github.com/sponsors/TheEvilSkeleton"&gt;GitHub Sponsors&lt;/a&gt;&lt;/li&gt; &lt;/ul&gt; &lt;/p&gt; &lt;/section&gt; &lt;/div&gt; &lt;h2 id="introduction"&gt;Introduction&lt;/h2&gt; &lt;p&gt;There is no calendaring app that I love more than GNOME Calendar. The design is slick, it works extremely well, it is touchpad friendly, and best of all, the community around it is just full of wonderful developers, designers, and contributors worth collaborating with, especially with the recent community growth and engagement over the past few years. &lt;a href="https://feaneron.com/"&gt;Georges Stavracas&lt;/a&gt; and &lt;a href="https://fortintam.com/en/"&gt;Jeff Fortin Tam&lt;/a&gt; are some of the best maintainers I have ever worked with. I cannot express how thankful I am of Jeff’s underappreciated superhuman capabilities to voluntarily coordinate huge initiatives and issue trackers.&lt;/p&gt; &lt;p&gt;One of Jeff’s many initiatives is &lt;a href="https://gitlab.gnome.org/GNOME/gnome-calendar/-/issues/1036"&gt;gnome-calendar#1036&lt;/a&gt;: &lt;em&gt;the accessibility initiative&lt;/em&gt;, which is a big and detailed list of issues related to accessibility. In my opinion, GNOME Calendar’s biggest problem was the lack of accessibility support, which made the app completely unusable for people exclusively using a keyboard, or people relying on assistive technologies.&lt;/p&gt; &lt;p&gt;This article will explain in details about the fundamental issues that held back accessibility in GNOME Calendar since the very beginning of its existence (12 years at a minimum), the progress we have made with accessibility as well as our thought process in achieving it, and the now and future of accessibility in GNOME Calendar.&lt;/p&gt; &lt;h2 id="calendaring-complications"&gt;Calendaring Complications&lt;/h2&gt; &lt;div class="statements"&gt; &lt;section class="card note"&gt; &lt;strong class="big"&gt;Note&lt;/strong&gt; &lt;p&gt; &lt;p&gt;On a desktop or tablet form factor, GNOME Calendar has a month view and a week view, both of which are a grid comprising of cells representing a time frame.&lt;/p&gt; &lt;p&gt;In the month view, each row is a week, and each cell is a day.&lt;/p&gt; &lt;figure&gt;  &lt;source media="(prefers-color-scheme: light)" /&gt; &lt;img alt="" height="936" src="https://tesk.page/assets/Articles/making-gnome-calendar-accessible-in-six-months/month-view.webp" width="1682" /&gt;  &lt;figcaption&gt;&lt;/figcaption&gt; &lt;/figure&gt; &lt;p&gt;In the week view, the time frame within cells varies on the zooming level.&lt;/p&gt; &lt;figure&gt;  &lt;source media="(prefers-color-scheme: light)" /&gt; &lt;img alt="" height="936" src="https://tesk.page/assets/Articles/making-gnome-calendar-accessible-in-six-months/week-view.webp" width="1682" /&gt;  &lt;figcaption&gt;&lt;/figcaption&gt; &lt;/figure&gt; &lt;/p&gt; &lt;/section&gt; &lt;/div&gt; &lt;p&gt;There are mainly two reasons that made GNOME Calendar inaccessible: firstly, GTK’s accessibility tree does not cover the logically and structurally complicated workflow and design that is a typical calendaring app; and secondly, the significant negative implications of accessibility due to reducing as much overhead as possible.&lt;/p&gt; &lt;h3 id="accessibility-trees-are-insufficient-for-calendaring-apps"&gt;Accessibility Trees Are Insufficient for Calendaring Apps&lt;/h3&gt; &lt;p&gt;GTK’s accessibility tree, or rather &lt;em&gt;any&lt;/em&gt; &lt;a href="https://developer.mozilla.org/en-US/docs/Glossary/Accessibility_tree"&gt;accessibility tree&lt;/a&gt;, is rendered insufficient for calendaring apps, mainly because events are extremely versatile. Tailoring the entire interface and experience around that versatility pushes us to explore alternate and custom structures.&lt;/p&gt; &lt;p&gt;Events are highly flexible, because they are time-based. An event can last a couple of minutes, but it can as well last for hours, days, weeks, or even months. It can start in the middle of a day and end on the upcoming day; it can start by the end of a week and end at the beginning of the upcoming week. Essentially, events are limitless, just like time itself.&lt;/p&gt; &lt;figure&gt;  &lt;source media="(prefers-color-scheme: light)" /&gt; &lt;img alt="" height="936" src="https://tesk.page/assets/Articles/making-gnome-calendar-accessible-in-six-months/events-in-month-view.webp" width="1682" /&gt;  &lt;figcaption&gt;&lt;/figcaption&gt; &lt;/figure&gt; &lt;p&gt;Since events can last more than a day, cell widgets cannot hold a meaningful link with event widgets, because otherwise event widgets would not be capable of spanning across cells. As such, event widgets are overlaid on top of cell widgets and positioned based on the coordinates, width, and height of each widget.&lt;/p&gt; &lt;p&gt;As a consequence, the visual representation of GNOME Calendar is fundamentally incompatible with accessibility trees. GNOME Calendar’s month and week views are &lt;em&gt;visually&lt;/em&gt; &lt;span class="no-wrap"&gt;&lt;a href="https://en.wikipedia.org/wiki/2.5D_(visual_perception)"&gt;2.5 dimensional&lt;/a&gt;: A grid&lt;/span&gt; layout by itself is structurally &lt;span class="no-wrap"&gt;two-dimensional&lt;/span&gt;, but overlaying event widgets that is capable of spanning across cells adds an additional layer. Conversely, accessibility trees are fundamentally &lt;span class="no-wrap"&gt;two-dimensional&lt;/span&gt;, so GNOME Calendar’s visual representation cannot be sufficiently adapted into a &lt;span class="no-wrap"&gt;two-dimensional&lt;/span&gt; logical tree.&lt;/p&gt; &lt;p&gt;In summary, accessibility trees are insufficient for calendaring apps, because the versatility and high requirements of events prevents us from linking cell widgets with event widgets, so event widgets are instead overlaid on top, consequently making the visual representation &lt;span class="no-wrap"&gt;2.5 dimensional; however,&lt;/span&gt; the additional layer makes it fundamentally impossible to adapt to a &lt;span class="no-wrap"&gt;two-dimensional&lt;/span&gt; accessibility tree.&lt;/p&gt; &lt;h3 id="negative-implications-of-accessibility-due-to-maximizing-performance"&gt;Negative Implications of Accessibility due to Maximizing Performance&lt;/h3&gt; &lt;p&gt;Unlike the majority of apps, GNOME Calendar’s layout and widgetry consist of custom widgets and complex calculations according to several factors, such as: &lt;/p&gt; &lt;ul&gt; &lt;li&gt;the size of the window;&lt;/li&gt; &lt;li&gt;the height and width of each cell widget to figure out if one or more event widgets can perceptibly fit inside a cell;&lt;/li&gt; &lt;li&gt;the position of each event widget to figure out where to position the event widget, and where to reposition all the event widgets around it if necessary;&lt;/li&gt; &lt;li&gt;&lt;del&gt;what went wrong in my life to work on a calendaring app written in C.&lt;/del&gt;&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;Due to these complex calculations, along with the fact that it is also possible to have tens, hundreds, or even thousands of events, nearly every calendar app relies on maximizing performance as much as possible, while being at the mercy of the framework or toolkit. Furthermore, GNOME Calendar supports smooth scrolling and &lt;a href="https://alvarotrigo.com/blog/kinetic-scrolling/"&gt;kinetic scrolling&lt;/a&gt;, so each event and cell widget’s position needs to be recalculated for every pixel when the user scrolls or swipes with a mouse or touchpad.&lt;/p&gt; &lt;p&gt;One way to minimize that problem is by creating custom widgets that are minimal and only fulfill the purpose we absolutely need. However, this comes at the cost of needing to reimplement most functionality, including most, if not all accessibility features and semantics, such as keyboard focus, which severely impacted accessibility in GNOME Calendar.&lt;/p&gt; &lt;p&gt;While GTK’s widgets are great for general purpose use-cases and do not have any performance impact with limited instances of them, performance starts to deteriorate on weaker systems when there are hundreds, if not thousands of instances in the view, because they contain a lot of functionality that event widgets may not need.&lt;/p&gt; &lt;p&gt;In the case of the &lt;a href="https://docs.gtk.org/gtk4/class.Button.html"&gt;GtkButton&lt;/a&gt; widget, it has a custom &lt;a href="https://en.wikipedia.org/wiki/Multiplexer"&gt;multiplexer&lt;/a&gt;, it applies different styles for different &lt;a href="https://docs.gtk.org/gtk4/property.Button.child.html"&gt;child types&lt;/a&gt;, it implements the &lt;a href="https://docs.gtk.org/gtk4/iface.Actionable.html"&gt;GtkActionable&lt;/a&gt; interface for custom actions, and more technical characteristics. Other functionality-based widgets will have more capabilities that might impact performance with hundreds of instances.&lt;/p&gt; &lt;p&gt;To summarize, GNOME Calendar reduces overhead by creating minimal custom widgets that fulfill a specific purpose. This unfortunately severely impacted accessibility throughout the app and made it unusable with a keyboard, as some core functionalities, accessibility features and semantics were never (re)implemented.&lt;/p&gt; &lt;h2 id="improving-the-existing-experience"&gt;Improving the Existing Experience&lt;/h2&gt; &lt;p&gt;Despite being inaccessible as an app altogether, not every aspect was inaccessible in GNOME Calendar. Most areas throughout the app worked with a keyboard and/or assistive technologies, but they needed some changes to improve the experience. For this reason, this section is reserved specifically for mentioning the aspects that underwent a lot of improvements.&lt;/p&gt; &lt;h3 id="improving-focus-rings"&gt;Improving Focus Rings&lt;/h3&gt; &lt;p&gt;The first major step was to improve the focus ring situation throughout GNOME Calendar. Since the majority of widgets are custom widgets, many of them require to manually apply focus rings. &lt;a class="no-wrap" href="https://gitlab.gnome.org/GNOME/gnome-calendar/-/merge_requests/563"&gt;!563&lt;/a&gt; addresses that by &lt;a href="https://gitlab.gnome.org/GNOME/gnome-calendar/-/merge_requests/563/diffs?commit_id=336ecd9ec30603cc8a37c7ba13de66cc3c61a5c0"&gt;declaring custom CSS properties&lt;/a&gt;, to use as a base for focus rings. &lt;a class="no-wrap" href="https://gitlab.gnome.org/GNOME/gnome-calendar/-/merge_requests/399"&gt;!399&lt;/a&gt; tweaks the style of the reminders popover in the event editor dialog, with the &lt;a href="https://gitlab.gnome.org/GNOME/gnome-calendar/-/merge_requests/399/diffs?commit_id=4ec25d877577b0b0bc9a33b1335133b57edc3092"&gt;addition of a focus ring&lt;/a&gt;.&lt;/p&gt; &lt;p&gt;We changed the behavior of the event notes box under the β€œNotes” section in the event editor dialog. Every time the user focuses on the event notes box, the focus ring appears and outlines the entire box until the user leaves focus. This was accomplished by &lt;a href="https://gitlab.gnome.org/GNOME/gnome-calendar/-/merge_requests/563/diffs?commit_id=140dd3b145f98e7198fca140939b1f5e09bbfb4f"&gt;subclassing AdwPreferencesRow&lt;/a&gt; to inherit its style, then &lt;a href="https://gitlab.gnome.org/GNOME/gnome-calendar/-/merge_requests/563/diffs?commit_id=140dd3b145f98e7198fca140939b1f5e09bbfb4f"&gt;applying the &lt;code class="language-plaintext no-wrap highlighter-rouge"&gt;.focused&lt;/code&gt; class&lt;/a&gt; whenever the user focuses on the notes.&lt;/p&gt; &lt;h3 id="improving-the-calendar-grid"&gt;Improving the Calendar Grid&lt;/h3&gt; &lt;div class="statements"&gt; &lt;section class="card note"&gt; &lt;strong class="big"&gt;Note&lt;/strong&gt; &lt;p&gt; &lt;p&gt;The calendar grid is a 7Γ—6 grid of buttons representing each day. The horizontal axis represents the day of the week, and the vertical axis represents the week number.&lt;/p&gt; &lt;figure&gt;  &lt;source media="(prefers-color-scheme: light)" /&gt; &lt;img alt="" class="drop-shadow" height="276" src="https://tesk.page/assets/Articles/making-gnome-calendar-accessible-in-six-months/date-grid.webp" width="314" /&gt;  &lt;figcaption&gt;&lt;/figcaption&gt; &lt;/figure&gt; &lt;/p&gt; &lt;/section&gt; &lt;/div&gt; &lt;p&gt;The calendar grid on the sidebar suffered from several issues when it came to keyboard navigation, namely:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;pressing &lt;kbd&gt;β†Ή&lt;/kbd&gt; would focus the next cell in the grid up until the last cell;&lt;/li&gt; &lt;li&gt;when out of bounds, there would be no auditory feedback;&lt;/li&gt; &lt;li&gt;on the last row, pressing &lt;kbd&gt;↓&lt;/kbd&gt; would focus a blank element; and&lt;/li&gt; &lt;li&gt;pressing &lt;kbd&gt;β†’&lt;/kbd&gt; in left-to-right languages, or &lt;kbd&gt;←&lt;/kbd&gt; in right-to-left languages, on the last column would move focus to a completely different widget.&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;While the calendar grid can be interacted with a keyboard, the keyboard experience was far from desired. &lt;a class="no-wrap" href="https://gitlab.gnome.org/GNOME/gnome-calendar/-/merge_requests/608"&gt;!608&lt;/a&gt; addresses these issues by overriding the &lt;a href="https://docs.gtk.org/gtk4/vfunc.Widget.focus.html"&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;Gtk.Widget.focus ()&lt;/code&gt;&lt;/a&gt; virtual method. Pressing &lt;kbd&gt;β†Ή&lt;/kbd&gt; or &lt;kbd&gt;&lt;kbd&gt;Shift&lt;/kbd&gt;+&lt;kbd&gt;β†Ή&lt;/kbd&gt;&lt;/kbd&gt; &lt;a href="https://gitlab.gnome.org/GNOME/gnome-calendar/-/merge_requests/608/diffs?commit_id=032a71dde6b476111848bb0bdd5000a111d5ac6d"&gt;skips the entire grid&lt;/a&gt;, and the grid is &lt;a href="https://gitlab.gnome.org/GNOME/gnome-calendar/-/merge_requests/608/diffs?commit_id=ba716102b916257362feeab40dce48528d39ac19"&gt;wrapped&lt;/a&gt; to allow focusing between the first and last columns with &lt;kbd&gt;←&lt;/kbd&gt; and &lt;kbd&gt;β†’&lt;/kbd&gt;, while notifying the user when out of bounds.&lt;/p&gt; &lt;h3 id="improving-the-calendar-list-box"&gt;Improving the Calendar List Box&lt;/h3&gt; &lt;div class="statements"&gt; &lt;section class="card note"&gt; &lt;strong class="big"&gt;Note&lt;/strong&gt; &lt;p&gt; &lt;p&gt;The calendar list box holds a list of available calendars, all of which can be displayed or hidden from the week view and month view. Each row is a &lt;a href="https://docs.gtk.org/gtk4/class.ListBoxRow.html"&gt;GtkListBoxRow&lt;/a&gt; that holds a &lt;a href="https://docs.gtk.org/gtk4/class.CheckButton.html"&gt;GtkCheckButton&lt;/a&gt;.&lt;/p&gt; &lt;/p&gt; &lt;/section&gt; &lt;/div&gt; &lt;p&gt;The calendar list box had several problems in regards to keyboard navigation and the information each row provided to assistive technologies.&lt;/p&gt; &lt;p&gt;The user was required to press &lt;kbd&gt;β†Ή&lt;/kbd&gt; a second time to get to the next row in the list. To elaborate: pressing &lt;kbd&gt;β†Ή&lt;/kbd&gt; once focused the row; pressing it another time moved focus to the check button within the row (bad); and finally pressing the third time focused the next row.&lt;/p&gt; &lt;p&gt;Row widgets had no actual purpose besides toggling the check button upon activation. Similarly, the only use for a check button widget inside each row was to display the β€œcheck mark” icon if the calendar was displayed. This meant that the check button widget held all the desired semantics, such as the β€œ&lt;em&gt;&lt;a href="https://docs.gtk.org/gtk4/enum.AccessibleRole.html#checkbox"&gt;checkbox&lt;/a&gt;&lt;/em&gt;” role and the β€œ&lt;em&gt;&lt;a href="https://docs.gtk.org/gtk4/enum.AccessibleState.html#checked"&gt;checked&lt;/a&gt;&lt;/em&gt;” state; but worst of all, it was getting focus. Essentially, the check button widget was handling responsibilities that should have been handled by the row.&lt;/p&gt; &lt;p&gt;Both inconveniences were addressed by &lt;a class="no-wrap" href="https://gitlab.gnome.org/GNOME/gnome-calendar/-/merge_requests/588"&gt;!588&lt;/a&gt;. The check button widget was replaced with a check mark icon using &lt;a href="https://docs.gtk.org/gtk4/class.Image.html"&gt;GtkImage&lt;/a&gt;, a widget that does not grab focus. The accessible role of the row widget was changed to β€œ&lt;em&gt;&lt;a href="https://docs.gtk.org/gtk4/enum.AccessibleRole.html#checkbox"&gt;checkbox&lt;/a&gt;&lt;/em&gt;”, and the code was adapted to handle the β€œ&lt;em&gt;&lt;a href="https://docs.gtk.org/gtk4/enum.AccessibleState.html#checked"&gt;checked&lt;/a&gt;&lt;/em&gt;” state.&lt;/p&gt; &lt;h2 id="implementing-accessibility-functionality"&gt;Implementing Accessibility Functionality&lt;/h2&gt; &lt;p&gt;Accessibility is often absolute: there is no β€˜in-between’ state; either the user &lt;em&gt;can&lt;/em&gt; access functionality, or they cannot, which can potentially make the app completely unusable. This section goes in depth with the widgets that were not only entirely inaccessible but also rendered GNOME Calendar completely unusable with a keyboard and assistive technology.&lt;/p&gt; &lt;h3 id="making-the-event-widget-accessible"&gt;Making the Event Widget Accessible&lt;/h3&gt; &lt;div class="statements"&gt; &lt;section class="card note"&gt; &lt;strong class="big"&gt;Note&lt;/strong&gt; &lt;p&gt; &lt;p&gt;&lt;a href="https://gitlab.gnome.org/GNOME/gnome-calendar/-/blob/main/src/gui/gcal-event-widget.c"&gt;&lt;em&gt;GcalEventWidget&lt;/em&gt;&lt;/a&gt;, the name of the event widget within GNOME Calendar, is a colored rectangular toggle button containing the summary of an event.&lt;/p&gt; &lt;figure&gt;  &lt;img alt="" class="drop-shadow" height="62" src="https://tesk.page/assets/Articles/making-gnome-calendar-accessible-in-six-months/event-widget-preview.webp" width="650" /&gt;  &lt;figcaption&gt;&lt;/figcaption&gt; &lt;/figure&gt; &lt;p&gt;Activating it displays a popover that displays additional detail for that event.&lt;/p&gt; &lt;p&gt;GcalEventWidget subclasses &lt;a href="https://docs.gtk.org/gtk4/class.Widget.html"&gt;GtkWidget&lt;/a&gt;.&lt;/p&gt; &lt;/p&gt; &lt;/section&gt; &lt;/div&gt; &lt;p&gt;The biggest problem in GNOME Calendar, which also made it completely impossible to use the app with a keyboard, was the lack of a way to focus and activate event widgets with a keyboard. Essentially, one would be able to create events, but there would be no way to access them in GNOME Calendar.&lt;/p&gt; &lt;p&gt;Quite literally, this entire saga began all thanks to a dream I had, which was to make GcalEventWidget subclass &lt;a href="https://docs.gtk.org/gtk4/class.Button.html"&gt;GtkButton&lt;/a&gt; instead of &lt;a href="https://docs.gtk.org/gtk4/class.Widget.html"&gt;GtkWidget&lt;/a&gt; directly. The thought process was: &lt;em&gt;GtkButton already implements focus and activation with a keyboard, so inheriting it should therefore inherit focus and activation behavior&lt;/em&gt;.&lt;/p&gt; &lt;p&gt;In merge request &lt;a class="no-wrap" href="https://gitlab.gnome.org/GNOME/gnome-calendar/-/merge_requests/559"&gt;!559&lt;/a&gt;, the initial &lt;a href="https://gitlab.gnome.org/TheEvilSkeleton/gnome-calendar/-/commit/fcd7347ecb469e026e603f7f9c4d3cc9f4422d43"&gt;implementation&lt;/a&gt; indeed subclassed GtkButton. However, that implementation did not go through, due to the reason outlined in &lt;a href="https://tesk.page/2025/07/25/gnome-calendar-a-new-era-of-accessibility-achieved-in-90-days/#negative-implications-of-accessibility-due-to-maximizing-performance"&gt;&lt;em&gt;&lt;span class="no-wrap"&gt;Β§ Negative&lt;/span&gt; Implications of Accessibility due to Maximizing Performance&lt;/em&gt;&lt;/a&gt;.&lt;/p&gt; &lt;p&gt;Despite that, the initial implementation instead significantly helped us figure out &lt;em&gt;exactly&lt;/em&gt; what were missing with GcalEventWidget: specifically, setting &lt;a class="no-wrap" href="https://docs.gtk.org/gtk4/property.Widget.receives-default.html"&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;Gtk.Widget:receives-default&lt;/code&gt;&lt;/a&gt; and &lt;a class="no-wrap" href="https://docs.gtk.org/gtk4/property.Widget.focusable.html"&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;Gtk.Widget:focusable&lt;/code&gt;&lt;/a&gt; properties to β€œ&lt;em&gt;True&lt;/em&gt;”. &lt;code class="language-plaintext no-wrap highlighter-rouge"&gt;Gtk.Widget:receives-default&lt;/code&gt; makes it so the widget can be activated how ever desired, and &lt;code class="language-plaintext no-wrap highlighter-rouge"&gt;Gtk.Widget:focusable&lt;/code&gt; allows it to become focusable with a keyboard. So, instead of subclassing GtkButton, we instead &lt;a href="https://gitlab.gnome.org/GNOME/gnome-calendar/-/merge_requests/559/diffs?commit_id=a3fb57939a307f5b661cc2f205e562f43a0a31fa"&gt;reimplemented GtkButton’s functionality&lt;/a&gt; in order to maintain performance.&lt;/p&gt; &lt;p&gt;While preliminary support for keyboard navigation was added into GcalEventWidget, accessible semantics for assistive technologies like screen readers were severely lacking. This was addressed by &lt;a class="no-wrap" href="https://gitlab.gnome.org/GNOME/gnome-calendar/-/merge_requests/587"&gt;!587&lt;/a&gt;, which sets the &lt;a href="https://gitlab.gnome.org/GNOME/gnome-calendar/-/merge_requests/587/diffs?commit_id=73e1baad04b4737cfd2133404a04819c118f2d3e"&gt;role to β€œ&lt;em&gt;toggle-button&lt;/em&gt;”&lt;/a&gt;, to convey that GcalEventWidget is a toggle button. The merge request also &lt;a href="https://gitlab.gnome.org/GNOME/gnome-calendar/-/merge_requests/587/diffs?commit_id=96fef80ca2786d80ecd1a3b69a1bfb3f9bf15c59"&gt;indicates that the widget has a popup&lt;/a&gt; for the event popover, and has the means to &lt;a href="https://gitlab.gnome.org/GNOME/gnome-calendar/-/merge_requests/587/diffs?commit_id=e8249e9e431928096fd84416e6856440dac314b8"&gt;update the β€œ&lt;em&gt;pressed&lt;/em&gt;” state&lt;/a&gt; of the widget.&lt;/p&gt; &lt;p&gt;In summary, we first made GcalEventWidget accessible with a keyboard by reimplementing some of GtkButton’s functionality. Then, we later added the means to appropriately convey information to assistive technologies. This was the worst offender, and was the primary reason why GNOME Calendar was unusable with a keyboard, but we finally managed to solve it!&lt;/p&gt; &lt;h3 id="making-the-month-and-year-spin-buttons-accessible"&gt;Making the Month and Year Spin Buttons Accessible&lt;/h3&gt; &lt;div class="statements"&gt; &lt;section class="card note"&gt; &lt;strong class="big"&gt;Note&lt;/strong&gt; &lt;p&gt; &lt;p&gt;&lt;a href="https://gitlab.gnome.org/GNOME/gnome-calendar/-/blob/main/src/gui/event-editor/gcal-multi-choice.c"&gt;&lt;em&gt;GcalMultiChoice&lt;/em&gt;&lt;/a&gt; is the name of the custom &lt;a href="https://en.wiktionary.org/wiki/spin_button"&gt;spin button&lt;/a&gt; widget used for displaying and cycling through months and/or years.&lt;/p&gt; &lt;p&gt;It comprises of a β€œdecrement” button to the start, a flat toggle button in the middle that contains a label that displays the value, and an β€œincrement” button to the end. Only the button in the middle can gain keyboard focus throughout GcalMultiChoice.&lt;/p&gt; &lt;figure&gt;  &lt;source media="(prefers-color-scheme: light)" /&gt; &lt;img alt="" class="drop-shadow" height="46" src="https://tesk.page/assets/Articles/making-gnome-calendar-accessible-in-six-months/multi-choice.webp" width="310" /&gt;  &lt;figcaption&gt;&lt;/figcaption&gt; &lt;/figure&gt; &lt;p&gt;In some circumstances, GcalMultiChoice can display a popover for increased granularity.&lt;/p&gt; &lt;/p&gt; &lt;/section&gt; &lt;/div&gt; &lt;p&gt;GcalMultiChoice was not interactable with a keyboard, because:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;it did not react to &lt;kbd&gt;↑&lt;/kbd&gt; and &lt;kbd&gt;↓&lt;/kbd&gt; keys; and&lt;/li&gt; &lt;li&gt;the β€œdecrement” and β€œincrement” buttons were not focusable.&lt;/li&gt; &lt;/ol&gt; &lt;p&gt;For a spin button widget, the β€œdecrement” and β€œincrement” buttons should generally remain unfocusable, because &lt;kbd&gt;↑&lt;/kbd&gt; and &lt;kbd&gt;↓&lt;/kbd&gt; keys already accomplish that behavior. Furthermore, &lt;a href="https://docs.gtk.org/gtk4/class.SpinButton.html"&gt;GtkSpinButton&lt;/a&gt;’s β€œincrement” (+) and β€œdecrement” (-) buttons are not focusable either, and the &lt;a href="https://www.w3.org/WAI/ARIA/apg/patterns/spinbutton/examples/datepicker-spinbuttons/"&gt;Date Picker Spin Button Example&lt;/a&gt; by the ARIA Authoring Practices Guide (APG) avoids that functionality as well.&lt;/p&gt; &lt;p&gt;However, since GcalMultiChoice did not react to &lt;kbd&gt;↑&lt;/kbd&gt; and &lt;kbd&gt;↓&lt;/kbd&gt; keys, having the β€œdecrement” and β€œincrement” buttons be focusable would have been a somewhat acceptable workaround. Unfortunately, since those buttons were not focusable, and &lt;kbd&gt;↑&lt;/kbd&gt; and &lt;kbd&gt;↓&lt;/kbd&gt; keys were not supported, it was impossible to increment or decrement values in GcalMultiChoice with a keyboard without resorting to workarounds.&lt;/p&gt; &lt;p&gt;Additionally, GcalMultiChoice lacked the semantics to communicate with assistive technologies. So, for example, a screen reader would never say anything meaningful.&lt;/p&gt; &lt;p&gt;All of the above problems remained problems until merge request &lt;a class="no-wrap" href="https://gitlab.gnome.org/GNOME/gnome-calendar/-/merge_requests/603"&gt;!603&lt;/a&gt;. For starters, it implements &lt;a href="https://docs.gtk.org/gtk4/iface.Accessible.html"&gt;GtkAccessible&lt;/a&gt; and &lt;a href="https://docs.gtk.org/gtk4/iface.AccessibleRange.html"&gt;GtkAccessibleRange&lt;/a&gt;, and then &lt;a href="https://gitlab.gnome.org/GNOME/gnome-calendar/-/merge_requests/603/diffs?commit_id=b4853da2496efb62d1b9f891dc0ce9d0679caf1c"&gt;implements keyboard navigation&lt;/a&gt;.&lt;/p&gt; &lt;h4 id="implementing-gtkaccessible-and-gtkaccessiblerange"&gt;Implementing GtkAccessible and GtkAccessibleRange&lt;/h4&gt; &lt;p&gt;The merge request implements the GtkAccessible interface to &lt;a href="https://gitlab.gnome.org/GNOME/gnome-calendar/-/merge_requests/603/diffs?commit_id=70b9fde16239f855f985071bb3c60c16f05e59c0"&gt;retrieve information from the flat toggle button&lt;/a&gt;.&lt;/p&gt; &lt;p&gt;Fundamentally, since the toggle button was the only widget capable of gaining keyboard focus throughout GcalMultiChoice, this caused two distinct problems.&lt;/p&gt; &lt;p&gt;The first issue was that assistive technologies only retrieved semantic information from the flat toggle button, such as the type of widget (accessible role), its label, and its description. However, the toggle button was semantically &lt;em&gt;just&lt;/em&gt; a toggle button; since it contained semantics and provided information to assistive technologies, the information it provided was actually misleading, because it only provided information as a toggle button, not a spin button!&lt;/p&gt; &lt;p&gt;So, the solution to this is to strip the semantics from the flat toggle button. Setting its accessible role to β€œ&lt;em&gt;&lt;a href="https://docs.gtk.org/gtk4/enum.AccessibleRole.html#none"&gt;none&lt;/a&gt;&lt;/em&gt;” makes assistive technologies ignore its information. Then, setting the accessible role of the &lt;span class="no-wrap"&gt;top-level (GcalMultiChoice)&lt;/span&gt; to β€œ&lt;em&gt;&lt;a href="https://docs.gtk.org/gtk4/enum.AccessibleRole.html#spin_button"&gt;spin-button&lt;/a&gt;&lt;/em&gt;” gives semantic meaning to assistive technologies, which allows the widget to appropriately convey these information, when focused.&lt;/p&gt; &lt;p&gt;This led to the second issue: Assistive technologies only retrieved information from the flat toggle button, not from the top-level. Generally, assistive technologies retrieve information from the focused widget. Since the toggle button was the only widget capable of gaining focus, it was also the only widget providing information to them; however, since its semantics were stripped, it had no information to share, and thus assistive technologies would retrieve absolutely nothing.&lt;/p&gt; &lt;p&gt;The solution to this is to override the &lt;a class="no-wrap" href="https://docs.gtk.org/gtk4/vfunc.Accessible.get_platform_state.html"&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;Gtk.Accessible.get_platform_state ()&lt;/code&gt;&lt;/a&gt; virtual method, which allows us to bridge communication between the &lt;a href="https://docs.gtk.org/gtk4/enum.AccessiblePlatformState.html"&gt;states&lt;/a&gt; of child widgets and the top-level widget. In this case, both GcalMultiChoice and the flat toggle button share the stateβ€”if the flat toggle button is focused, then GcalMultiChoice is considered focused; and since GcalMultiChoice is focused, assistive technologies can then retrieve its information and state.&lt;/p&gt; &lt;p&gt;The last issue that needed to be addressed was that GcalMultiChoice was still not providing any of the values to assistive technologies. The solution to this is straightforward: &lt;a href="https://gitlab.gnome.org/GNOME/gnome-calendar/-/merge_requests/603/diffs?commit_id=2a7a4e3b30815e27501d7c8ae6a3c68c84defb20"&gt;implementing the GtkAccessibleRange interface&lt;/a&gt;, which makes it necessary to set values for the following accessible properties: β€œ&lt;em&gt;&lt;a href="https://docs.gtk.org/gtk4/enum.AccessibleProperty.html#value_max"&gt;value-max&lt;/a&gt;&lt;/em&gt;”, β€œ&lt;em&gt;&lt;a href="https://docs.gtk.org/gtk4/enum.AccessibleProperty.html#value_min"&gt;value-min&lt;/a&gt;&lt;/em&gt;”, β€œ&lt;em&gt;&lt;a href="https://docs.gtk.org/gtk4/enum.AccessibleProperty.html#value_now"&gt;value-now&lt;/a&gt;&lt;/em&gt;”, and β€œ&lt;em&gt;&lt;a href="https://docs.gtk.org/gtk4/enum.AccessibleProperty.html#value_text"&gt;value-text&lt;/a&gt;&lt;/em&gt;”.&lt;/p&gt; &lt;p&gt;After all this effort, GcalMultiChoice now provides correct semantics to assistive technologies. It appropriately reports its role, the current textual value, and whether it contains a popover.&lt;/p&gt; &lt;p&gt;To summarize:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;The flat toggle button was the only widget conveying information to assistive technologies, as it was the only widget capable of gaining focus and providing semantic information. To solve this, its semantics were stripped away.&lt;/li&gt; &lt;li&gt;The top-level, being GcalMultiChoice, was assigned the β€œspin-button” role to provide semantics; however, it was still incapable of providing information to assistive technologies, because it was never getting focused. To solve this, the state of the toggle button, including the focused state, carried over to the top-level to allow assistive technologies to retrieve information from the top-level.&lt;/li&gt; &lt;li&gt;GcalMultiChoice still did not provide its values to assistive technologies. This is solved by implementing the GtkAccessibleRange interface.&lt;/li&gt; &lt;/ul&gt; &lt;h4 id="providing-top-level-semantics-to-a-child-widget-as-opposed-to-the-top-level-widget-is-discouraged"&gt;Providing &lt;span class="no-wrap"&gt;Top-Level&lt;/span&gt; Semantics to a Child Widget As Opposed to the &lt;span class="no-wrap"&gt;Top-Level&lt;/span&gt; Widget Is Discouraged&lt;/h4&gt; &lt;p&gt;As you read through the previous section, you may have asked yourself: β€œ&lt;em&gt;Why go through all of those obstacles and complications when you could have just &lt;span class="no-wrap"&gt;re-assigned&lt;/span&gt; the flat toggle button as β€œ&lt;a href="https://docs.gtk.org/gtk4/enum.AccessibleRole.html#spin_button"&gt;spin-button&lt;/a&gt;” and not worry about the top-level’s role and focus state?&lt;/em&gt;”&lt;/p&gt; &lt;p&gt;Semantics should be provided by the top-level, because they are represented by the top-level. What makes GcalMultiChoice a spin button is not &lt;em&gt;just&lt;/em&gt; the flat toggle button, but it is the combination of all the child widgets/objects, event handlers (touch, key presses, and other inputs), accessibility attributes (role, states, relationships), widget properties, signals, and other characteristics. As such, we want to maintain that consistency for practically everything, including the state. The only exception to this is widgets whose sole purpose is to contain one or more elements, such as &lt;a href="https://docs.gtk.org/gtk4/class.Box.html"&gt;GtkBox&lt;/a&gt;.&lt;/p&gt; &lt;p&gt;This is especially important for when we want it to communicate with other widgets and APIs, such as the &lt;a href="https://docs.gtk.org/gtk4/signal.Widget.state-flags-changed.html"&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;Gtk.Widget::state-flags-changed&lt;/code&gt;&lt;/a&gt; signal, the &lt;a href="https://docs.gtk.org/gtk4/method.Widget.is_focus.html"&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;Gtk.Widget.is_focus ()&lt;/code&gt;&lt;/a&gt; method, and other APIs where it is necessary to have the top-level represent data accurately and behave predictably. In the case of GcalMultiChoice, we &lt;a href="https://gitlab.gnome.org/GNOME/gnome-calendar/-/merge_requests/603/diffs?commit_id=7a40a3be6a3c08ad417eb865bd0a2e55df6103fa"&gt;set accessible labels&lt;/a&gt; at the top-level. If we were to &lt;span class="no-wrap"&gt;re-assign&lt;/span&gt; the flat toggle button’s role as β€œspin-button”, and set the accessible label to the top-level, assistive technologies would only retrieve information from the toggle button while ignoring the labels defined at the top-level.&lt;/p&gt; &lt;p&gt;For the record, GtkSpinButton also &lt;a href="https://gitlab.gnome.org/GNOME/gtk/-/blob/c4c27e6193e6bacda8936ff7d26d19c6923032be/gtk/gtkspinbutton.c#L670-684"&gt;overrides&lt;/a&gt; &lt;code class="language-plaintext no-wrap highlighter-rouge"&gt;Gtk.Accessible.get_platform_state ()&lt;/code&gt;:&lt;/p&gt; &lt;div class="language-c highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;table class="rouge-table"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class="rouge-gutter gl"&gt;&lt;pre class="lineno"&gt;1
  4182. 2
  4183. 3
  4184. 4
  4185. 5
  4186. 6
  4187. 7
  4188. 8
  4189. 9
  4190. 10
  4191. 11
  4192. 12
  4193. 13
  4194. 14
  4195. &lt;/pre&gt;&lt;/td&gt;&lt;td class="rouge-code"&gt;&lt;pre&gt;&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;gboolean&lt;/span&gt;
  4196. &lt;span class="nf"&gt;gtk_spin_button_accessible_get_platform_state&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GtkAccessible&lt;/span&gt;              &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  4197.                                               &lt;span class="n"&gt;GtkAccessiblePlatformState&lt;/span&gt;  &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  4198. &lt;span class="p"&gt;{&lt;/span&gt;
  4199.  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;gtk_editable_delegate_get_accessible_platform_state&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GTK_EDITABLE&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  4200. &lt;span class="p"&gt;}&lt;/span&gt;
  4201.  
  4202. &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
  4203. &lt;span class="nf"&gt;gtk_spin_button_accessible_init&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GtkAccessibleInterface&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;iface&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  4204. &lt;span class="p"&gt;{&lt;/span&gt;
  4205.  &lt;span class="err"&gt;…&lt;/span&gt;
  4206.  
  4207.  &lt;span class="n"&gt;iface&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;get_platform_state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;gtk_spin_button_accessible_get_platform_state&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  4208. &lt;span class="p"&gt;}&lt;/span&gt;
  4209. &lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt; &lt;p&gt;To be fair, assigning the β€œspin-button” role to the flat toggle button is unlikely to cause major issues, especially for an app. &lt;span class="no-wrap"&gt;Re-assigning&lt;/span&gt; the flat toggle button was my first instinct. The &lt;a href="https://gitlab.gnome.org/GNOME/gnome-calendar/-/merge_requests/603/diffs?commit_id=3c383a8d92ea7a8c6d04f300bdbb59c2fd078827"&gt;initial implementation&lt;/a&gt; did just that as well. I was completely unaware of the &lt;code class="language-plaintext no-wrap highlighter-rouge"&gt;Gtk.Accessible.get_platform_state ()&lt;/code&gt; virtual method before finalizing the merge request, so I initially thought that was the correct way to do. Even if the toggle button had the β€œ&lt;em&gt;&lt;a href="https://docs.gtk.org/gtk4/enum.AccessibleRole.html#spin_button"&gt;spin-button&lt;/a&gt;&lt;/em&gt;” role instead of the top-level, it would not have stopped us from implementing workarounds, such as a getter method that retrieves the flat toggle button that we can then use to manipulate it.&lt;/p&gt; &lt;p&gt;In summary, we want to provide semantics at the top-level, because they are structurally part of it. This comes with the benefit of making the widget easier to work with, because APIs can directly communicate with it, instead of resorting to workarounds.&lt;/p&gt; &lt;h2 id="the-now-and-future-of-accessibility-in-gnome-calendar"&gt;The Now and Future of Accessibility in GNOME Calendar&lt;/h2&gt; &lt;p&gt;All these accessibility improvements will be available on GNOME 49, but you can download and install the pre-release on the β€œNightly GNOME Apps” &lt;del&gt;DLC&lt;/del&gt; Flatpak remote on &lt;a href="https://nightly.gnome.org/"&gt;nightly.gnome.org&lt;/a&gt;.&lt;/p&gt; &lt;p&gt;In the foreseeable future, I want to continue working on &lt;a class="no-wrap" href="https://gitlab.gnome.org/GNOME/gnome-calendar/-/merge_requests/564"&gt;!564&lt;/a&gt;, to make the month view itself accessible with a keyboard, as seen in the following:&lt;/p&gt; &lt;figure&gt; &lt;video alt="" class="drop-shadow" controls="" src="https://tesk.page/assets/Articles/making-gnome-calendar-accessible-in-six-months/month-view-keyboard-navigation.webm"&gt;&lt;/video&gt; &lt;figcaption&gt;&lt;p&gt;A screen recording demoing keyboard navigation within the month view. Focus rings appear and disappear as the user moves focus between cells. Going out of bounds in the vertical axis scrolls the view to the direction, and going out of bounds in the horizontal axis moves focus to the logical sibling.&lt;/p&gt;&lt;/figcaption&gt; &lt;/figure&gt; &lt;p&gt;However, it is already adding 640 lines of code, and I can only see it increasing overtime. We also want to make cells in the week view accessible, but this will also be a monstrous merge request, just like the above merge request.&lt;/p&gt; &lt;p&gt;Most importantly, we want (and need) to collaborate and connect with people who rely on assistive technologies to use their computer, especially when everybody working on GNOME Calendar does not rely on assistive technologies themselves.&lt;/p&gt; &lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt; &lt;p&gt;I am overwhelmingly satisfied of the progress we have made with accessibility on GNOME Calendar in six months. Just a year ago, if I was asked about what needs to be done to incorporate accessibility features in GNOME Calendar, I would have shamefully said β€œdude, I don’t know where to even &lt;em&gt;begin&lt;/em&gt;”; but as of today, we somehow managed to turn GNOME Calendar into an actual, usable calendaring app for people who rely on assistive technologies and/or a keyboard.&lt;/p&gt; &lt;p&gt;Since this is still Disability Pride Month, and GNOME 49 is not out yet, I encourage you to get the alpha release of GNOME Calendar on the β€œNightly GNOME Apps” Flatpak remote at &lt;a href="https://nightly.gnome.org/"&gt;nightly.gnome.org&lt;/a&gt;. The alpha release is in a state where &lt;a href="https://en.wikipedia.org/wiki/Queer_anarchism#%22Be_Gay,_Do_Crime%22"&gt;the gays with disabilities can organize and do crimes&lt;/a&gt; using GNOME Calendar 😎 /j&lt;/p&gt;</description><author>Hari Rana</author><dc:creator>Hari Rana</dc:creator><pubDate>Fri, 25 Jul 2025 00:00:00 GMT</pubDate><guid isPermaLink="true">https://tesk.page/2025/07/25/gnome-calendar-a-new-era-of-accessibility-achieved-in-90-days/</guid></item><item><title>Philip Withnall: A brief parental controls update</title><link>https://tecnocode.co.uk/2025/07/24/a-brief-parental-controls-update/</link><description>&lt;p&gt;Over the past few weeks, &lt;a href="https://blogs.gnome.org/ignapk/2025/07/11/digital-wellbeing-contract/"&gt;Ignacy&lt;/a&gt; and I have made good progress on the next phase of features for parental controls in GNOME: a refresh of the parental controls UI, support for screen time limits for child accounts, and basic web filtering support are all in progress. I’ve been working on the backend stuff, while Ignacy has been speedily implementing everything needed in the frontend.&lt;/p&gt;
  4210.  
  4211.  
  4212.  
  4213. &lt;p&gt;Ignacy is at GUADEC, so please say hi to him! The next phase of parental controls work will involve changes to gnome-control-center and gnome-shell, so he’ll be popping up all over the stack.&lt;/p&gt;
  4214.  
  4215.  
  4216.  
  4217. &lt;p&gt;I’ll try and blog more soon about the upcoming features and how they’re implemented, because there are necessarily quite a few moving parts to them.&lt;/p&gt;</description><author>Philip Withnall</author><dc:creator>Philip Withnall</dc:creator><pubDate>Thu, 24 Jul 2025 22:13:28 GMT</pubDate><guid isPermaLink="true">https://tecnocode.co.uk/2025/07/24/a-brief-parental-controls-update/</guid></item><item><title>Sjoerd Stendahl: A Brief History of Graphs; My Journey Into Application Development</title><link>https://blogs.gnome.org/sstendahl/2025/07/24/a-brief-history-of-graphs-my-journey-into-application-development/</link><description>&lt;p&gt;It&amp;#8217;s been a while since I originally created this page. I&amp;#8217;ve been planning for a while (over a year) to write an article like this, but have been putting this off for one reason or another. With GUADEC going on while writing this, listening to some interesting talks on YouTube, I thought this is a good time as ever to actually to submit my first post on this page. In this article, I&amp;#8217;ll simply lie down the history of Graphs, how it came to be, how it evolved to an actually useful program for some, and what is on the horizon. Be aware that any opinions expressed are my own, and do not necessarily reflect those of my employer, any contributors or the GNOME Foundation itself.&lt;/p&gt;
  4218. &lt;p&gt;I would also like to acknowledge that while I founded Graphs, the application I&amp;#8217;m mainly talking about, I&amp;#8217;m not the only one working on the project. We&amp;#8217;ve got a lot of input from the community. And I maintain the project with Christoph, the co-maintainer of the project. Any credit towards this particular program, is shared credit.&lt;/p&gt;
  4219. &lt;h2&gt;Motivations&lt;/h2&gt;
  4220. &lt;p&gt;As many open source projects, I originally developed Graphs because I had a personal itch to scratch. At the time I was working on my PhD, and I regularly had to plot data to prepare for presentations. As well as to do some simple manipulations. Things like cutting away the first few degrees from a X-ray reflectivity measurement, normalizing data, or shifting data to show multiple measurements on the same graph.&lt;/p&gt;
  4221. &lt;p&gt;At the time, I had a license for OriginLabs. Which was an issue for multiple reasons. Pragmatically, it only works on Windows and even if it had a Linux client, we had a single license coupled to my work PC in my office which I didn&amp;#8217;t tend to use a lot. Furthermore, the software itself is an interface nightmare and doing simple operations like cutting data or normalization is not exactly intuitive.&lt;/p&gt;
  4222. &lt;p&gt;My final issue was more philosophical, which is that I have fundamental problems with using proprietary software in scientific work. It is bluntly absurd how we have rigorous and harsh rules about showing your work and making your research replicable in scientific articles (which is fair), but as soon as software is involved it&amp;#8217;s suddenly good enough when a private entity tells us &amp;#8220;&lt;em&gt;just trust me bro&lt;/em&gt;&amp;#8220;. Let it be clear that I have no doubt that a proprietary application actually implements the algorithms that it says it does according to their manual. But you are still replacing a good chunk of your article with a black box, which in my view is fundamentally unscientific. There could be bugs, and subtlety could be missed. Let alone the fact that replicability is just completely thrown out of the window if you delegate all your data processing to a magic black box. This is an issue where a lot of people I talk to tend to agree with me on principle, yet very few people actually care enough to move away from proprietary solutions. Whenever people use open-source software for their research, I found it&amp;#8217;s typically a coincidence based on the merits that it was free, rather than a philosophical or ideological choice.&lt;/p&gt;
  4223. &lt;p&gt;Either way, philosophically, I wanted to do my data-reduction using complete transparency. And pragmatically I simply needed something that just plots my data, and allows me to do basic transformations. For years I had asked myself questions like &amp;#8220;why can&amp;#8217;t I just visually select part of the data, and then press a &amp;#8220;cut&amp;#8221; button?&amp;#8221; and &amp;#8220;Why do all these applications insist on over-complicating this?&amp;#8221; Whilst I still haven&amp;#8217;t found an answer to the second question, I had picked up programming as a hobby at that stage, so I decided to answer my first question with a &amp;#8220;fine, I&amp;#8217;ll do it myself&amp;#8221;. But first, let&amp;#8217;s start at what drove me to start working on applications such as these.&lt;/p&gt;
  4224. &lt;h2&gt;Getting into application development&lt;/h2&gt;
  4225. &lt;p&gt;Whilst I had developed a lot in MatLab during my master&amp;#8217;s (as well as TI-Basic at high-school), my endeavor in application-development started mostly during my PhD. Starting with some very simple applications, like a calculator tool for growth rate in magnetron sputtering based on calibration measurements. Another application that I wrote during that time was a tool that simply plotted logs that we got from our magnetron sputtering machine. Fun fact here is that my logging software also kept track of how the software running our magnetron sputtering chambers slowed down over time. Basically, our machine was steered using LabView, and after about 1000 instructions or so it started to slow down a bit. So if we tell it to do something for 24 seconds, it started to take 24.1 seconds for instance. At one point we had a reviewer comment that didn&amp;#8217;t believe that we could get such a delay with modern computers, so it was nice to have the receipts to back this up. Still the conclusion here should be that LabView is not exactly great to steer hardware directly, but it&amp;#8217;s not me that&amp;#8217;s calling the shots.&lt;/p&gt;
  4226. &lt;p&gt;My first &amp;#8220;bigger&amp;#8221; project was something in between a database (all stored in a csv file), as well as a plotting program. Basically for every sample I created in the lab, I added an item where I stored all relevant information, including links to the measurements I did on the sample (like sane people would do in an excel sheet). Then using a simple list of all samples, I could quickly just plot my data for the measurements I wanted. I also had some functionality like cutting away the start of the data, normalizing the data, or the ability to calculate sample thickness based on the measurement. In a sense, this was Graphs 0.1. &lt;a class="external" href="https://github.com/sstendahl/KARIN"&gt;The code is still online&lt;/a&gt;, if someone wants to laugh at a physicists code without any real developer experience.&lt;/p&gt;
  4227. &lt;p&gt;The second &amp;#8220;big&amp;#8221; tool that I created during that time was GIScan. This lied the foundation of &lt;a class="external" href="https://pubs.acs.org/doi/pdf/10.1021/acsami.4c01457?ref=article_openPDF"&gt;my very favourite article&lt;/a&gt; that I wrote during my PhD. Essentially, we got 24 hours to measure as many samples as we can at a Synchrotron facility. So that&amp;#8217;s exactly what we did, almost blindly. Then we came home with thousands of measurements on a few hundred samples, and it was time to analyze. At the very first stage, I did some basic analysis using Python. Basically all filenames were tagged somewhat strategically, so I could use regex to isolate the measurement series, and then I could quite quickly find the needle in the haystack. Basically, I found which 20 measurements or so where interesting for us and where to look further. The only problem, the work we were doing was extremely niche and the data reduction software available, which would do things like background subtraction and coordinate conversion for us (from pixels to actually physical coordinates), was barely functional and not made for our type of measurements. So here I wrote my own data reduction software, GIScan. Explaining what makes it actually incredibly useful would require an article series about the physics behind this, but my entire analysis hinged on this data analysis software. &lt;a class="external" href="https://github.com/sstendahl/GIScan"&gt;GIScan is also available&lt;/a&gt; as GPLv3 licensed software, but also here I will use my right to remain silent on any questions about the crimes committed in the code quality itself. Fun fact, all graphs in the mentioned article were made using Graphs and Inkscape. Most of the figures themselves are available &lt;a class="external" href="https://github.com/sstendahl/PhdThesis"&gt;under a CC-BY license as part of my PhD&lt;/a&gt;. I asked about using a CC-BY-SA license, but the university strongly recommended against as they felt it could make it more difficult to use for others in publishing if I care about sharing my work, basically journals are the biggest parasites in academia.&lt;/p&gt;
  4228. &lt;p&gt;Then we get to Graphs. This was the last, and biggest program that I wrote during that time in my career. At the very beginning, I actually started in Qt. Not because I preferred it as a toolkit (I didn&amp;#8217;t, and still don&amp;#8217;t), but because it&amp;#8217;s easier to port to Windows and spread to my peers. Quite quickly I got to the state where I could easily import two-column data, and do simple manipulations on this data like normalizing the data. It was barebones, but really useful for my workflow. However, as this quickly turned into a passion project, I decided to do the selfish thing and actually rewrite the entire thing in the toolkit that I personally preferred, GTK with libadwaita. It looked beautiful (well, in the same way a newborn baby is beautiful to their parents), and it integrated very nicely into my own desktop. In fact, I was so pleased with it, that I felt like I wanted to share this online, the original Reddit post can still be found &lt;a class="external" href="https://old.reddit.com/r/linux/comments/zqh8oj/introducing_graphs/"&gt;here&lt;/a&gt;. This marked the very first release of Graphs 1.0, which can be seen in its full glory below, and this is essentially where the fun began&lt;/p&gt;
  4229. &lt;figure class="wp-caption alignnone" id="attachment_76" style="width: 641px;"&gt;&lt;img alt="The very first version of Graphs" class="wp-image-76" height="361" src="https://blogs.gnome.org/sstendahl/files/2025/07/graphs1.0-300x169.png" width="641" /&gt;&lt;figcaption class="wp-caption-text" id="caption-attachment-76"&gt;Graphs 1.0&lt;/figcaption&gt;&lt;/figure&gt;
  4230. &lt;h2&gt;The power of community&lt;/h2&gt;
  4231. &lt;p&gt;When I originally developed this for my personal use, I simply called it &amp;#8220;Data Manipulator&amp;#8221;, which I had shortened to DatMan. Quite early in the process, even before I shared the project to Reddit, Hari Rana (aka TheEvilSkeleton) filed an issue asking me to consider naming the project in accordance with the GNOME HIG. After some small discussions there, we settled on Graphs. This was my first experience with feedback or contributions from the community, and something I am still grateful for. It&amp;#8217;s a much nicer name that fits in with GNOME applications. They also helped me with a few other design patterns like modal windows and capitalization. Shoutout to Skelly here, for the early help to a brand new project. It did push me to look more into the HIG, and thus helped a lot in getting that ball rolling. I don&amp;#8217;t take any donations, but feel free to help them out with the work on several projects that are significantly more high-stress than Graphs. There&amp;#8217;s a donation page on &lt;a class="external" href="https://tesk.page/"&gt;their webpage.&lt;/a&gt;&lt;/p&gt;
  4232. &lt;p&gt;After sharing the initial release to Reddit, I continued development and slowly started tweaking things and polishing existing features. I added support for multiple axes, added some more transformations and added basic options like import settings. It was also around this time that Tobias Bernard from the GNOME Design team dropped by with some help. At first with the generous offer to design a logo for the project, which is still the logo of Graphs today. The old logo, followed by the newly designed logo can be found here:&lt;/p&gt;
  4233. &lt;figure class="wp-caption alignnone" id="attachment_77" style="width: 128px;"&gt;&lt;img alt="The original graphs logo, a simpy red cruve on a graph roughly looking like x*sin(x)" class="wp-image-77 size-full" height="128" src="https://blogs.gnome.org/sstendahl/files/2025/07/og_logo.png" width="128" /&gt;&lt;figcaption class="wp-caption-text" id="caption-attachment-77"&gt;The original Graphs logo&lt;/figcaption&gt;&lt;/figure&gt;
  4234. &lt;figure class="wp-caption alignnone" id="attachment_78" style="width: 128px;"&gt;&lt;img alt="The redesigned Graphs logo. Showing a curve on a plot, on a notebook" class="wp-image-78 size-full" height="128" src="https://blogs.gnome.org/sstendahl/files/2025/07/se.sjoerd.Graphs.png" width="128" /&gt;&lt;figcaption class="wp-caption-text" id="caption-attachment-78"&gt;The redesigned Graphs logo&lt;/figcaption&gt;&lt;/figure&gt;
  4235. &lt;p&gt;Yet again, I was very pleasantly surprised by complete strangers just dropping by and offering help. Of course, they&amp;#8217;re not just helping me personally, but rather helping out the community and ecosystem as a whole. But this collaborative feeling that we&amp;#8217;re all working on a system that we collectively own together is something that really attracted me to GNOME and FOSS in general.&lt;/p&gt;
  4236. &lt;p&gt;It was also around these early days that Christoph, who now maintains Graphs with me came by with some pull requests. This went on to the point that he pretty naturally ended up in the role of maintainer. I can confidently say that him joining this endeavor is the best thing that ever happened to Graphs. Both in terms of a general elevation of the code quality, but also in terms of motivation and decision making. Not only did the introduction of a second maintainer mean that new code actually got reviewed, but someone else contributing is a really strong motivator and super contagious for me. In these somewhat early days things were moving fast, and we really saw strong improvement both in terms of the quality of the code, but also in the general user experience of the app.&lt;/p&gt;
  4237. &lt;p&gt;In terms of UX, I&amp;#8217;d really like to thank Tobias again. Even before we even had GNOME Circle on our radar as goal, he helped us a lot with general feedback about the UX. Highlighting papercuts, and coming up with design patterns that made more sense. Here we really saw a lot of improvements, and I really learned a lot at the time about having design at the core of the development process. The way the application works is not just a means to get something done, the design &lt;em&gt;is&lt;/em&gt; the program. Not to say that I&amp;#8217;d classify myself as an expert UI-designer these days, but a lot of lessons have been learned thanks to the involvement of people that have more expertise than me. The GNOME Design team in general has been very helpful with suggestions and feedback during the development. Whenever I got in touch with the GNOME Developer community, it&amp;#8217;s been nothing but helpfulness and honest advice. The internet stereotype about GNOME Developers being difficult to work with simply does not hold up. Not in my experience. It&amp;#8217;s been a fantastic journey. Note that nobody is obliged to fix your problems for you, but you will find that if you ask nicely and listen to feedback from others, people are more than willing to help you out!&lt;/p&gt;
  4238. &lt;p&gt;I couldn&amp;#8217;t talk about the history of Graphs, without at least mentioning the process of getting to GNOME Circle. It&amp;#8217;s there for me, that Graphs really went from a neat hobbyist tool to a proper useful application. When we initially applied there, I was pretty happy about the state we were at, but we&amp;#8217;ve actually undergone quite a bit of a transformation since Graphs got accepted. If someone feels inclined following the process, the entire process is still available on &lt;a class="external" href="https://gitlab.gnome.org/Teams/Circle/-/issues/185"&gt;the GitLab page.&lt;/a&gt; I won&amp;#8217;t go &lt;em&gt;too&lt;/em&gt; much about joining GNOME Circle here, there&amp;#8217;s a nice talk scheduled at GUADEC2025 from the developer of Drum Machine about that. But here&amp;#8217;s what Graphs looked like before, and after the GNOME Circle Application:&lt;/p&gt;
  4239. &lt;figure class="wp-caption alignnone" id="attachment_80" style="width: 556px;"&gt;&lt;img alt="A screenshot showing Graphs before the GNOME Circle application. In general with a more cluttered interface" class="wp-image-80" height="341" src="https://blogs.gnome.org/sstendahl/files/2025/07/XoruMwkfLbNBIGLkUjCfUVZL.CrNPbJlE_i3Kwq-300x184.png" width="556" /&gt;&lt;figcaption class="wp-caption-text" id="caption-attachment-80"&gt;The state of Graphs when we just applied to GNOME Circle&lt;/figcaption&gt;&lt;/figure&gt;
  4240. &lt;figure class="wp-caption alignnone" id="attachment_79" style="width: 557px;"&gt;&lt;img alt="A screenshot showing Graphs just after the GNOME Circle application. " class="wp-image-79" height="347" src="https://blogs.gnome.org/sstendahl/files/2025/07/RpXTZsGAaiHyaTmpBakgWKHi.DMBq_Ap1_EU2V4-300x187.png" width="557" /&gt;&lt;figcaption class="wp-caption-text" id="caption-attachment-79"&gt;Graphs just after the GNOME Circle application. This also coincided with the new sidebar in libadwaita.&lt;/figcaption&gt;&lt;/figure&gt;
  4241. &lt;p&gt;Two particular changes that stuck to me where the introduction of touchpad gesture support, and the change in the way we handle settings. Starting with touchpad gestures, I had always considered this to be out of our control. We use Matplotlib to render the plots themselves, which by itself doesn&amp;#8217;t support touch gestures. It&amp;#8217;s mostly thanks to Tobias naming it as part of the GNOME Cirle Review that I actually went ahead and try to implement it myself. After a week or so digging into documentations and testing some different calculations for the different axes, I actually got this working. It&amp;#8217;s a moment that stuck with me, partly because of the dopamine hit when things finally worked, but also because it again showed the value of starting with intended the user experience first and then working backwards to fit the technology with that. Rather than starting with the technology, and then creating a user experience from that.&lt;/p&gt;
  4242. &lt;p&gt;The change in settings is something I wanted to highlight, because this is such a common theme in discussions about GNOME in general. Over time, I&amp;#8217;ve been leaning more and more towards the idea that preferences in many cases are simply just an excuse to avoid making difficult choices. Before submitting, we had settings for basically everything. We had a setting for the default plotting style in dark mode and light mode, we had a setting for the clipboard size. We had a setting for the default equation when creating a new equation, and I could on for a bit. Most of these settings could simply be replaced by making the last alternative persistent between sessions. The default equation now is simply the last used equation, same for import settings where we just added a button to reset these settings to default. For the styling we don&amp;#8217;t have a separate dark and light style that can be set, instead you just set one style total and one of the options is just &amp;#8220;System&amp;#8221;, which essentially resembles Adwaita and Adwaita-dark in light and dark mode respectively. This really, really streamlined the entire user experience. Things got much easier to use, and options got much easier to find. I would strongly recommend anyone that develops applications (within the GNOME ecosystem or elsewhere), to read the &lt;a class="external" href="https://ometer.com/preferences.html"&gt;&amp;#8220;Choosing our preferences&amp;#8221; &lt;/a&gt;article, it&amp;#8217;s a real eye-opener.&lt;/p&gt;
  4243. &lt;h2&gt;Where we are now, and where we&amp;#8217;re going&lt;/h2&gt;
  4244. &lt;p&gt;These days Graphs is relatively mature and works pretty well. Since being accepted to the GNOME Circle, we haven&amp;#8217;t had such a major overhaul as presented here. We&amp;#8217;ve had some performance upgrades under the hood, fixed quite a bit of bugs and made some improvements with the layout system. We&amp;#8217;ve since also added full support for touchscreen devices (thanks to me getting a Steam Deck, allowing me to test on touch), improved the rubberband on the canvas, and improved equation parsing a bit.&lt;/p&gt;
  4245. &lt;p&gt;Despite the somewhat slower pace, there is a major release in the brewing with some exiting features already in the main branch. Some of the features that you can expect in the next stable release:&lt;/p&gt;
  4246. &lt;p&gt;&lt;strong&gt;Full equation support on an infinite canvas&lt;/strong&gt;&lt;/p&gt;
  4247. &lt;p&gt;At the moment, you cannot really add an &amp;#8220;equation&amp;#8221; to Graphs. Instead, what you do is that you generate data based on an equation. In the next release, we actually support &amp;#8220;equations&amp;#8221;. These span the entire canvas, and can be changed afterwards as well. Operations you do on the equation (such as derivatives), actually affect the equation accordingly, and you can actually change the equation also now after adding it.&lt;/p&gt;
  4248. &lt;figure class="wp-caption alignnone" id="attachment_82" style="width: 539px;"&gt;&lt;img alt="Screenshot of Graphs showing support for equations" class="wp-image-82" height="336" src="https://blogs.gnome.org/sstendahl/files/2025/07/Screenshot-From-2025-07-24-14-18-16-300x187.png" width="539" /&gt;&lt;figcaption class="wp-caption-text" id="caption-attachment-82"&gt;We now fully support equations on an infinite canvas&lt;/figcaption&gt;&lt;/figure&gt;
  4249. &lt;p&gt;&lt;strong&gt;Generated data can now be changed afterwards&lt;/strong&gt;&lt;/p&gt;
  4250. &lt;p&gt;You can still generate data from an equation like you could previously (so it doesn&amp;#8217;t have to be an infinite equation). But generated data can now also be changed afterwards by changing the input equation.&lt;/p&gt;
  4251. &lt;figure class="wp-caption alignnone" id="attachment_83" style="width: 534px;"&gt;&lt;img alt="A screenshot showing how generated data can be regenerated afterwards" class="wp-image-83" height="333" src="https://blogs.gnome.org/sstendahl/files/2025/07/Screenshot-From-2025-07-24-14-18-57-300x187.png" width="534" /&gt;&lt;figcaption class="wp-caption-text" id="caption-attachment-83"&gt;Generated data can now be regenerated afterwards&lt;/figcaption&gt;&lt;/figure&gt;
  4252. &lt;p&gt;&lt;strong&gt;A fully revamped style editor&lt;/strong&gt;&lt;/p&gt;
  4253. &lt;p&gt;In the upcoming release, you can actually open .mplstyle files using Graphs, which opens the style editor itself instead of the main application. Furthermore, you can now import styles from the GUI, and open Graphs styles in another application (like your text editor) to do some advanced changes in the style that are not supported by our GUI. Likewise, you can now export your Graphs style-file so you can share it with others. (Maybe even with us, as a merge request, if it&amp;#8217;s really nice &lt;img alt="πŸ˜‰" class="wp-smiley" src="https://s.w.org/images/core/emoji/16.0.1/72x72/1f609.png" style="height: 1em;" /&gt; )&lt;/p&gt;
  4254. &lt;p&gt;Another &lt;em&gt;reallyΒ &lt;/em&gt;nice touch is that you now get a live preview of the actual style you&amp;#8217;re working on, so you don&amp;#8217;t need to go back and forth every time when you make incremental changes.&lt;/p&gt;
  4255. &lt;figure class="wp-caption alignnone" id="attachment_81" style="width: 539px;"&gt;&lt;img alt="A screenshot showing Graph's new style editor" class="wp-image-81" height="336" src="https://blogs.gnome.org/sstendahl/files/2025/07/Screenshot-From-2025-07-24-14-01-08-300x187.png" width="539" /&gt;&lt;figcaption class="wp-caption-text" id="caption-attachment-81"&gt;Graph&amp;#8217;s new style editor&lt;/figcaption&gt;&lt;/figure&gt;
  4256. &lt;p&gt;&lt;strong&gt;Drag and drop support&lt;/strong&gt;&lt;/p&gt;
  4257. &lt;p&gt;You can now import data by simply drag and dropping data into the As usual, there’s more features that I probably forgot. But the next release is bound to be a banger. I won’t dare to pin a release date here. But all the mentioned changes are already working (sqlite support is still in MR) and can be tested from the main branch. There’s still work to do though with regard to a planned rework on the way we import data, and the way we access the style editor which is currenlty a bit buried in the stable release. main application&lt;/p&gt;
  4258. &lt;figure class="wp-caption alignnone" id="attachment_85" style="width: 580px;"&gt;&lt;img alt="A screenshot showing drag and drop support" class="wp-image-85" height="375" src="https://blogs.gnome.org/sstendahl/files/2025/07/Screenshot-From-2025-07-24-14-22-37-300x194.png" width="580" /&gt;&lt;figcaption class="wp-caption-text" id="caption-attachment-85"&gt;You can now drag and drop data in Graphs&lt;/figcaption&gt;&lt;/figure&gt;
  4259. &lt;p&gt;&lt;strong&gt;Multiple sessions&lt;/strong&gt;&lt;/p&gt;
  4260. &lt;p&gt;You can now finally have multiple sessions of Graphs open at the same time. Allowing you to view and work on data side-by-side.&lt;/p&gt;
  4261. &lt;figure class="wp-caption alignnone" id="attachment_84" style="width: 608px;"&gt;&lt;img alt="You can now have multiple sessions open in Graphs" class="wp-image-84" height="381" src="https://blogs.gnome.org/sstendahl/files/2025/07/Screenshot-From-2025-07-24-14-20-40-300x188.png" width="608" /&gt;&lt;figcaption class="wp-caption-text" id="caption-attachment-84"&gt;You can now have multiple sessions open in Graphs&lt;/figcaption&gt;&lt;/figure&gt;
  4262. &lt;p&gt;&lt;strong&gt;Support for sqlite databases&lt;/strong&gt;&lt;/p&gt;
  4263. &lt;p&gt;We now added support for sqlite databases. So you can import data from your .db file&lt;/p&gt;
  4264. &lt;figure class="wp-caption alignnone" id="attachment_86" style="width: 664px;"&gt;&lt;img alt="Graphs now supports databases as input, on import you can choose your database table, and your columns based on that." class="wp-image-86" height="414" src="https://blogs.gnome.org/sstendahl/files/2025/07/Screenshot-From-2025-07-24-14-23-35-300x187.png" width="664" /&gt;&lt;figcaption class="wp-caption-text" id="caption-attachment-86"&gt;Graphs now supports databases as input, on import you can choose your database table, and your columns based on that.&lt;/figcaption&gt;&lt;/figure&gt;
  4265. &lt;p&gt;&lt;strong&gt;And more&lt;/strong&gt;&lt;/p&gt;
  4266. &lt;p&gt;As usual, there&amp;#8217;s more features that I probably forgot. But the next release is bound to be a banger. I won&amp;#8217;t dare to pin a release date here. But all the mentioned changes are already working (sqlite support is still in MR) and can be tested from the main branch. There&amp;#8217;s still work to do though with regard to a planned rework on the way we import data, and the way we access the style editor which is currently a bit buried in the stable release.&lt;/p&gt;
  4267. &lt;h2&gt;Conclusion&lt;/h2&gt;
  4268. &lt;p&gt;This post got a bit longer than I anticipated. But I hope in general this could give people some insight on how it is for a newcomer to get into application development. I really encourage people to test the waters. It really shows that you really can get involved, even if it involves learning along the way. These days I no longer work in academia, and I am willing to bet that I&amp;#8217;d probably wouldn&amp;#8217;t have my current position working with software if it wasn&amp;#8217;t for these adventures.&lt;/p&gt;
  4269. &lt;p&gt;Again, I would really like to thank the GNOME Community as a whole. The adventure so far has been great, and I promise that it&amp;#8217;s far from over &lt;img alt="πŸ™‚" class="wp-smiley" src="https://s.w.org/images/core/emoji/16.0.1/72x72/1f642.png" style="height: 1em;" /&gt;&lt;/p&gt;</description><author>Sjoerd Stendahl</author><dc:creator>Sjoerd Stendahl</dc:creator><pubDate>Thu, 24 Jul 2025 12:27:40 GMT</pubDate><guid isPermaLink="true">https://blogs.gnome.org/sstendahl/2025/07/24/a-brief-history-of-graphs-my-journey-into-application-development/</guid></item><item><title>Jussi Pakkanen: Comparing a red-black tree to a B-tree</title><link>https://nibblestew.blogspot.com/2025/07/comparing-red-black-tree-to-b-tree.html</link><description>&lt;p&gt;&amp;nbsp;In an earlier blog post we found that &lt;a href="https://nibblestew.blogspot.com/2025/07/deoptimizing-red-black-tree.html"&gt;optimizing the memory layout of a red-black tree does not seem to work&lt;/a&gt;. A different way of implementing an ordered container is to use a B-tree. It was originally designed to be used for on-disk data. The design principle was that memory access is "instant" while disk access is slow. Nowadays this applies to memory access as well, as cache hits are "instant" and uncached memory is slow.&lt;/p&gt;&lt;p&gt;I implemented a &lt;a href="https://github.com/jpakkane/pystd/blob/master/include/pystd2025_btree.hpp"&gt;B-tree in Pystd&lt;/a&gt;. Here is how all the various containers compare. For test data we used numbers from zero to one million in a random order.&lt;/p&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjjbw0_EPbh851ZmLYZdMNa7-TOw0Z3JqtBq3Vwbeu3DY-oF0UOr5kZwjugRfCnehNv_Jm2g_RWRbzuE8eGarFL_FCd8_RnMk-_vlqcq-_j3znphEup4vy85AmHoYhSZ9LoPUGKaSi7iJspYGawK-C84h23O75qoJDzuZthinDf172SnZJfkAdfxUVFtkY/s605/btree-perf1.png" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="180" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjjbw0_EPbh851ZmLYZdMNa7-TOw0Z3JqtBq3Vwbeu3DY-oF0UOr5kZwjugRfCnehNv_Jm2g_RWRbzuE8eGarFL_FCd8_RnMk-_vlqcq-_j3znphEup4vy85AmHoYhSZ9LoPUGKaSi7iJspYGawK-C84h23O75qoJDzuZthinDf172SnZJfkAdfxUVFtkY/s320/btree-perf1.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjFuTZgL-5By4_-Cq2mlauwebegQbKuqASmHYAgSrRRLcRmpXibgQnj9NV5Xe89r257BuX1hirIG33uYXYB8k8oxAh7rHIJ8Q_r6AqvvfmtK1rqLXtNrZp-dGbJv5VmavziiqDRBo_lnnY6KZceBXSMvl7cjiOMZcCZZYysFR4kmtjldw4ivrxepTfKAfc/s605/btree-perf2.png" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="180" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjFuTZgL-5By4_-Cq2mlauwebegQbKuqASmHYAgSrRRLcRmpXibgQnj9NV5Xe89r257BuX1hirIG33uYXYB8k8oxAh7rHIJ8Q_r6AqvvfmtK1rqLXtNrZp-dGbJv5VmavziiqDRBo_lnnY6KZceBXSMvl7cjiOMZcCZZYysFR4kmtjldw4ivrxepTfKAfc/s320/btree-perf2.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;p&gt;As we can see, an unordered map is massively faster than any ordered container. If your data does not need to be ordered, that is the one you should use. For ordered data, the B-tree is clearly faster than either red-black tree implementation.&lt;/p&gt;&lt;h1 style="text-align: left;"&gt;Tuning the B-tree&lt;/h1&gt;&lt;p&gt;B-trees have one main tunable parameter, namely the spread factor of the nodes. In the test above it was five, but for on disk purposes the recommended value is "in the thousands". Here's how altering the value affects performance.&lt;/p&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiv4NE-NuMsITsblokCvFr8tcnnfmgNfpIJBi9sZaxf1PYEAk9YeFSUClS5BS7PnYHXcXQDc2IqYQo_mf412X0b-51w63mFVRobuSKQLqA0eE8gluP3uo4Yr1VuDZIrf8UAyQlKNknw7s6qxEGMkYhwRGvH2uIHSS7-mxXLZeX-d_g7ofKbHDZLxA_1HVQ/s605/btree-perf3.png" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="180" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiv4NE-NuMsITsblokCvFr8tcnnfmgNfpIJBi9sZaxf1PYEAk9YeFSUClS5BS7PnYHXcXQDc2IqYQo_mf412X0b-51w63mFVRobuSKQLqA0eE8gluP3uo4Yr1VuDZIrf8UAyQlKNknw7s6qxEGMkYhwRGvH2uIHSS7-mxXLZeX-d_g7ofKbHDZLxA_1HVQ/s320/btree-perf3.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhyRvyeu50L1jrcNvKWTHhyphenhyphen_bi3exXR3y8AFx5MLq2T-8i1LuWiRSYmvRdCh8InzkU1hBnquiT2ZKHStESJsP_JoKuyPxPM8Wx7YbHYcdhNlLBEHXWIxmngWK6mk7iwpN-d87gs1LNYarph28yxlZhkYUE421Q09qoJK2AYDcn7n9Iej85HftILR0gZ9s0/s605/btree-perf4.png" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="180" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhyRvyeu50L1jrcNvKWTHhyphenhyphen_bi3exXR3y8AFx5MLq2T-8i1LuWiRSYmvRdCh8InzkU1hBnquiT2ZKHStESJsP_JoKuyPxPM8Wx7YbHYcdhNlLBEHXWIxmngWK6mk7iwpN-d87gs1LNYarph28yxlZhkYUE421Q09qoJK2AYDcn7n9Iej85HftILR0gZ9s0/s320/btree-perf4.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;p&gt;The sweet spot seems to be in the 256-512 range, where the operations are 60% faster than standard &lt;span style="font-family: courier;"&gt;set&lt;/span&gt;. As the spread factor grows towards infinity, the B-tree reduces to just storing all data in a single sorted array. Insertion into that is an &lt;i&gt;O(N^2)&lt;/i&gt; algorithm as can be seen here.&lt;/p&gt;&lt;h1 style="text-align: left;"&gt;Getting weird&lt;/h1&gt;&lt;p&gt;The B-tree implementation has many &lt;span style="font-family: courier;"&gt;assert&lt;/span&gt; calls to verify the internal structures. We can compile the code with &lt;span style="font-family: courier;"&gt;-DNDEBUG&lt;/span&gt; to make all those asserts disappear. Removing redundant code should make things faster, so let's try it.&lt;/p&gt;&lt;p&gt;There are 13 measurements in total and disabling asserts (i.e. enabling &lt;span style="font-family: courier;"&gt;NDEBUG&lt;/span&gt;) makes the code run &lt;i&gt;slower&lt;/i&gt; in 8 of those cases. Let's state that again, since it is very unexpected: in this particular measurement code with assertions enabled runs &lt;i&gt;faster&lt;/i&gt; than the same code without them. This should not be happening. What could be causing it?&lt;/p&gt;&lt;p&gt;I don't know for sure, so here is some speculation instead.&lt;/p&gt;&lt;p&gt;First of all a result of 8/13 is probably not statistically significant to say that enabling assertions makes things faster. OTOH it does mean that enabling them does not make the code run noticeably slower. So I guess we can say that both ways of building the code are approximately as fast.&lt;/p&gt;&lt;p&gt;As to why that is, things get trickier. Maybe GCC's optimizer is just really good at removing unnecessary checks. It might even be that the assertions give the compiler more information so it can skip generating code for things that can never happen. I'm not a compiler engineer, so I'll refrain from speculating further, it would probably be wrong in any case.&lt;/p&gt;</description><author>Jussi Pakkanen</author><dc:creator>Jussi Pakkanen</dc:creator><pubDate>Wed, 23 Jul 2025 14:05:00 GMT</pubDate><guid isPermaLink="true">https://nibblestew.blogspot.com/2025/07/comparing-red-black-tree-to-b-tree.html</guid></item><item><title>Michael Catanzaro: Fedora Must (Carefully) Embrace Flathub</title><link>https://blogs.gnome.org/mcatanzaro/2025/07/21/fedora-must-carefully-embrace-flathub/</link><description>&lt;h2 class="wp-block-heading"&gt;Motivation&lt;/h2&gt;
  4270.  
  4271.  
  4272.  
  4273. &lt;p&gt;Opportunity is upon us! For the past few years, the desktop Linux user base has been growing at a historically high rate. &lt;a class="external" href="https://gs.statcounter.com/os-market-share/desktop/worldwide/#quarterly-202502-202502-bar"&gt;StatCounter currently has us at 4.14% desktop OS market share for Q2 2025.&lt;/a&gt; For comparison, when Fedora Workstation was first released in Q4 2014, desktop Linux was at 1.38%. Now, StatCounter measures HTTP requests, not computers, but it&amp;#8217;s safe to say the trend is highly encouraging. Don&amp;#8217;t trust StatCounter? &lt;a class="external" href="https://radar.cloudflare.com/explorer?dataSet=http&amp;amp;groupBy=os&amp;amp;filters=deviceType%253DDesktop%252CbotClass%253DLikely_Human&amp;amp;dt=2025-04-01_2025-06-30"&gt;Cloudflare reports 2.9% for Q2 2025&lt;/a&gt;. &lt;a class="external" href="https://www.reddit.com/r/linux/comments/1m4jlbp/comment/n44wfnc/"&gt;One of the world&amp;#8217;s most popular websites reports 5.1%.&lt;/a&gt; And although I was unable to figure out how to make a permanent link to the results, &lt;a class="external" href="https://analytics.usa.gov/"&gt;analytics.usa.gov&lt;/a&gt; is currently reporting a robust 6.2% for the past 90 days, and increasing. The Linux user base is already much larger than I previously suspected would ever be possible, and it seems to be increasing quickly. I wonder if we are perhaps nearing an inflection point where our user base may soon increase even more considerably. The &lt;a class="external" href="https://endof10.org/"&gt;End of 10&lt;/a&gt; and &lt;a class="external" href="https://www.xda-developers.com/pewdiepie-install-linux-tortured-by-windows/"&gt;enthusiastic YouTubers&lt;/a&gt; are certainly not hurting.&lt;/p&gt;
  4274.  
  4275.  
  4276.  
  4277. &lt;p&gt;Compared to its peers, Fedora is doing particularly well. It&amp;#8217;s pretty safe to say that Fedora is now one of the 2 or 3 most popular and successful desktop Linux operating systems, a far cry from its status 10 years ago, when Fedora suffered from an unfortunate longstanding reputation that it was an unstable &amp;#8220;test bed&amp;#8221; OS only suitable for experienced technical users. Those days are long gone; nowadays, Fedora has an army of social media users eager to promote it as a reliable, newcomer-friendly choice.&lt;/p&gt;
  4278.  
  4279.  
  4280.  
  4281. &lt;p&gt;But we cannot stop here. If we become complacent and content ourselves with the status quo, then we will fail to take maximum advantage of the current opportunity.&lt;/p&gt;
  4282.  
  4283.  
  4284.  
  4285. &lt;p&gt;Although Fedora Workstation works well for most users, and although quality and reliability has improved considerably over the past decade, it is still far too easy for inexperienced users to break the operating system. Today&amp;#8217;s Fedora Workstation is fundamentally just a nicer version of the same thing we already had 10 years ago. The &lt;a class="external" href="https://fedoraproject.org/wiki/Workstation/Workstation_PRD"&gt;original plan&lt;/a&gt; called for major changes that we have thus far failed to deliver, like &amp;#8220;Robust Upgrades,&amp;#8221; &amp;#8220;Better upgrade/rollback control,&amp;#8221; and &amp;#8220;Container based application install.&amp;#8221; These critical goals are notably all already achieved by Fedora Silverblue, the experimental image-based alternative to Fedora Workstation, but few Fedora users benefit because only the most experienced and adventurous users are willing to install Silverblue. I had long assumed that Silverblue would eventually become the next Fedora Workstation, and that the Silverblue code name would eventually be retired. This is now an explicit project goal of &lt;a class="external" href="https://communityblog.fedoraproject.org/strategy-2028-update/"&gt;Fedora&amp;#8217;s Strategy 2028&lt;/a&gt;, and it is critical for Fedora&amp;#8217;s success. The Fedora Workstation of the future must be:&lt;/p&gt;
  4286.  
  4287.  
  4288.  
  4289. &lt;ul class="wp-block-list"&gt;
  4290. &lt;li&gt;Safe and image-based by default: an atomic operating system composed of RPMs built on bootc. Most users should stick with image-based mode because it&amp;#8217;s much harder to break the OS, and easier to troubleshoot when something does go wrong.&lt;/li&gt;
  4291.  
  4292.  
  4293.  
  4294. &lt;li&gt;Flexible if you so choose: converting the image-based OS into the traditional package-based OS managed by RPM and dnf must be allowed, for users who prefer or require it. Or alternatively, if converting is not possible, then installing a traditional non-atomic Fedora must remain possible. Either way, we must not &lt;em&gt;force&lt;/em&gt; users to use image-based desktops if they do not want to, so no need to panic. But image-based must eventually become the new default.&lt;/li&gt;
  4295. &lt;/ul&gt;
  4296.  
  4297.  
  4298.  
  4299. &lt;p&gt;Silverblue is not ready yet, but Fedora has a large community of developers and should be able to eventually resolve the remaining problems.&lt;/p&gt;
  4300.  
  4301.  
  4302.  
  4303. &lt;p&gt;But wait, wasn&amp;#8217;t this supposed to be a blog post about Flathub? Well, consider that with an image-based OS, you cannot easily install traditional RPM packages. Instead, in Fedora Silverblue, desktop applications are installed only via Flatpak. (This is also true of Fedora Kinoite and Fedora&amp;#8217;s other atomic desktop variants.) So Fedora must have a source of Flatpaks, and that source must be enabled by default, or there won&amp;#8217;t be any apps available.&lt;/p&gt;
  4304.  
  4305.  
  4306.  
  4307. &lt;p&gt;(Don&amp;#8217;t like Flatpak? This blog post is long enough already, so I&amp;#8217;ll ask you to just make a leap of faith and accept that Flatpak is cool. Notably, Flatpak applications that keep their bundled dependencies updated and do not subvert the sandbox are &lt;em&gt;much&lt;/em&gt; safer to use than traditional distro-packaged applications.)&lt;/p&gt;
  4308.  
  4309.  
  4310.  
  4311. &lt;p&gt;In practice, there are currently only two interesting sources of Flatpaks to choose from: Fedora Flatpaks and Flathub. Flathub is the much better choice, and enabling it by default should be our end goal. &lt;a class="external" href="https://discussion.fedoraproject.org/t/proposal-enable-flathub-by-default/157011"&gt;Fedora is already discussing whether to do this.&lt;/a&gt; But Flathub also has several disadvantages, some of which ought to be blockers.&lt;/p&gt;
  4312.  
  4313.  
  4314.  
  4315. &lt;h2 class="wp-block-heading"&gt;Why Flathub?&lt;/h2&gt;
  4316.  
  4317.  
  4318.  
  4319. &lt;p&gt;There are important technical differences between Fedora&amp;#8217;s Flatpaks, built from Fedora RPMs, vs. Flathub&amp;#8217;s Flatpaks, which are usually built on top of &lt;a class="external" href="https://freedesktop-sdk.gitlab.io/"&gt;freedesktop-sdk&lt;/a&gt;. But I will not discuss those, because the social differences are more important than the technical differences.&lt;/p&gt;
  4320.  
  4321.  
  4322.  
  4323. &lt;h3 class="wp-block-heading"&gt;Users Like Flathub&lt;/h3&gt;
  4324.  
  4325.  
  4326.  
  4327. &lt;p&gt;Feedback from Fedora&amp;#8217;s user base has been clear: among users who like Flatpaks, Flathub is extremely popular. When installing a Flatpak application, users generally expect it to come from Flathub. In contrast, many users of Fedora Flatpaks do not install them intentionally, but rather by accident, only because they are the preferred software source in GNOME Software. Users are often frustrated to discover that Fedora Flatpaks are not supported by upstream software developers and have a different set of bugs than upstream Flatpaks do. It is also common for users and even Fedora developers to entirely remove the Fedora Flatpak application source.&lt;/p&gt;
  4328.  
  4329.  
  4330.  
  4331. &lt;p&gt;Not so many users prefer to use Fedora Flatpaks. Generally, these users cite some of Flathub&amp;#8217;s questionable packaging practices as justification for avoiding use of Flathub. These concerns are valid; Flathub has some serious problems, which I will discuss in more detail below. But improving Flathub and fixing these problems would surely be much easier than creating thousands of Fedora Flatpak packages and attempting to compete with Flathub, a competition that Fedora would be quite unlikely to win.&lt;/p&gt;
  4332.  
  4333.  
  4334.  
  4335. &lt;p&gt;Flathub is drastically more popular than Fedora Flatpaks even among the most hardcore Fedora community members who participate in change proposal debate on Fedora Discussion. (At time of writing, &lt;a class="external" href="https://discussion.fedoraproject.org/t/f43-change-proposal-filter-fedora-flatpaks-for-atomic-desktops-self-contained/157262/2"&gt;nearly 80% of discussion participants favor filtering out Fedora Flatpaks&lt;/a&gt;.)&lt;/p&gt;
  4336.  
  4337.  
  4338.  
  4339. &lt;p&gt;This is the most important point. Flathub has &lt;em&gt;already&lt;/em&gt; won.&lt;/p&gt;
  4340.  
  4341.  
  4342.  
  4343. &lt;h3 class="wp-block-heading"&gt;Cut Out the Middleman&lt;/h3&gt;
  4344.  
  4345.  
  4346.  
  4347. &lt;p&gt;In general, upstream software developers understand their software much better than downstream packagers. Bugs reported to downstream issue trackers are much less likely to be satisfactorily resolved. There are a variety of ways that downstream packagers could accidentally mess up a package, whether by failing to enable a feature flag, or upgrading a dependency before the application is compatible with the new version. Downstream support is almost never as good as upstream support.&lt;/p&gt;
  4348.  
  4349.  
  4350.  
  4351. &lt;p&gt;Adding a middleman between upstream and users really only makes sense if the middleman is adding significant value. Traditional distro-packaged applications used to provide considerable value by making it easy to install the software. Nowadays, since upstreams can distribute software directly to users via Flathub, that value is far more limited.&lt;/p&gt;
  4352.  
  4353.  
  4354.  
  4355. &lt;h3 class="wp-block-heading"&gt;Bus Factor is Critical&lt;/h3&gt;
  4356.  
  4357.  
  4358.  
  4359. &lt;p&gt;Most Flatpak application developers prefer to contribute to Flathub. Accordingly, there are very few developers working on Fedora Flatpaks. Almost all of the Fedora Flatpaks are actually owned by one single developer who has packaged many hundreds of applications. This is surely not a healthy situation.&lt;/p&gt;
  4360.  
  4361.  
  4362.  
  4363. &lt;p&gt;Bugs in Fedora Flatpaks are reported on the &lt;a class="external" href="https://gitlab.com/fedora/sigs/flatpak/fedora-flatpaks/-/issues"&gt;Fedora Flatpak SIG issue tracker&lt;/a&gt;. This SIG notably does not have a list of active members, but rather a &lt;a class="external" href="https://fedoraproject.org/w/index.php?title=SIGs/Flatpak&amp;amp;oldid=699796#Join_the_Flatpak_SIG"&gt;years-old list of people who are interested in joining the SIG&lt;/a&gt;, who are encouraged to attend the first meeting. Needless to say the SIG does not seem to be in a very good state.&lt;/p&gt;
  4364.  
  4365.  
  4366.  
  4367. &lt;p&gt;I suspect this situation is permanent, reflecting a general lack of interest in Fedora Flatpak development, not just a temporary shortfall of contributors. Quality is naturally going to be higher where there are more contributors. The quality of Fedora Flatpak applications is often lower than Flathub applications, sometimes significantly so. Fedora Flatpaks also receive significantly less testing than Flathub Flatpaks. Upstream developers do not test the Fedora Flatpaks, and downstream developers are spread too thin to have plausible hope of testing them adequately.&lt;/p&gt;
  4368.  
  4369.  
  4370.  
  4371. &lt;h3 class="wp-block-heading"&gt;Focus on What Really Matters&lt;/h3&gt;
  4372.  
  4373.  
  4374.  
  4375. &lt;p&gt;Fedora&amp;#8217;s main competency and primary value is the core operating system, not miscellaneous applications that ship on top of it for historical reasons.&lt;/p&gt;
  4376.  
  4377.  
  4378.  
  4379. &lt;p&gt;When people complain that &amp;#8220;distros are obsolete,&amp;#8221; they don&amp;#8217;t mean that Linux operating systems are not needed anymore. Of course you need an OS on which to run applications. The anti-distro people notably all use distros.&lt;/p&gt;
  4380.  
  4381.  
  4382.  
  4383. &lt;p&gt;But it&amp;#8217;s no longer necessary for a Linux distribution to attempt to package every open source desktop application. That used to be a requirement for a Linux operating system to be successful, but nowadays it is an optional activity that we perform primarily for historical reasons, because it is what we have always done rather than because it is still truly strategic or essential. It is a time-consuming, resource-intensive side quest that no longer makes sense and does not add meaningful value.&lt;/p&gt;
  4384.  
  4385.  
  4386.  
  4387. &lt;h2 class="wp-block-heading"&gt;The Status Quo&lt;/h2&gt;
  4388.  
  4389.  
  4390.  
  4391. &lt;p&gt;Let&amp;#8217;s review how things work currently:&lt;/p&gt;
  4392.  
  4393.  
  4394.  
  4395. &lt;ul class="wp-block-list"&gt;
  4396. &lt;li&gt;By default, Fedora Workstation allows users to install open source software from the following sources: Fedora Flatpaks, Fedora RPMs, and Cisco&amp;#8217;s OpenH264 RPM.&lt;/li&gt;
  4397.  
  4398.  
  4399.  
  4400. &lt;li&gt;The post-install initial setup workflow, gnome-initial-setup, suggests enabling third-party repositories. If the user does not click the Enable button, then GNOME Software will make the same suggestion the first time it is run. Clicking this button enables all of Flathub, plus &lt;a class="external" href="https://docs.fedoraproject.org/en-US/workstation-working-group/third-party-repos/#_included_software"&gt;a few other RPM repositories&lt;/a&gt;.&lt;/li&gt;
  4401. &lt;/ul&gt;
  4402.  
  4403.  
  4404.  
  4405. &lt;figure class="wp-block-image size-large"&gt;&lt;a href="https://blogs.gnome.org/mcatanzaro/files/2025/07/Screenshot-From-2025-07-11-20-06-43.png"&gt;&lt;img alt="Image displaying the Third-Party Repositories page in Fedora's gnome-initial-setup." class="wp-image-11043" height="795" src="https://blogs.gnome.org/mcatanzaro/files/2025/07/Screenshot-From-2025-07-11-20-06-43-1024x795.png" width="1024" /&gt;&lt;/a&gt;&lt;/figure&gt;
  4406.  
  4407.  
  4408.  
  4409. &lt;p&gt;Fedora will probably never enable software sources that contain proprietary software by default, but it&amp;#8217;s easy to enable searching for proprietary software if desired.&lt;/p&gt;
  4410.  
  4411.  
  4412.  
  4413. &lt;p&gt;(Technically, Fedora actually has a filter in place to allow hiding any Flathub applications we don&amp;#8217;t want users to see. But since Fedora 38, this filter is empty, so no apps are hidden in practice. The downstream filter was quite unpopular with users, and the mechanism still exists only as a safety hatch in case there is some unanticipated future emergency.)&lt;/p&gt;
  4414.  
  4415.  
  4416.  
  4417. &lt;h2 class="wp-block-heading"&gt;The Future&lt;/h2&gt;
  4418.  
  4419.  
  4420.  
  4421. &lt;p&gt;Here are my proposed requirements for Fedora Workstation to become a successful image-based OS.&lt;/p&gt;
  4422.  
  4423.  
  4424.  
  4425. &lt;p&gt;This proposal applies only to Fedora Workstation (Fedora&amp;#8217;s GNOME edition). These proposals &lt;em&gt;could&lt;/em&gt; just as well apply to other Fedora editions and spins, like Fedora KDE Plasma Desktop, but different Fedora variants have different needs, so each should be handled separately.&lt;/p&gt;
  4426.  
  4427.  
  4428.  
  4429. &lt;h3 class="wp-block-heading"&gt;Flathub is Enabled by Default&lt;/h3&gt;
  4430.  
  4431.  
  4432.  
  4433. &lt;p&gt;Since Flathub includes proprietary software, we cannot include &lt;em&gt;all&lt;/em&gt; of Flathub by default. But Flathub already supports &lt;a class="external" href="https://docs.flathub.org/docs/for-users/installation#subsets"&gt;subsets&lt;/a&gt;. Fedora can safely enable the floss subset by default, and replace the &amp;#8220;Enable Third-Party Repositories&amp;#8221; button with an &amp;#8220;Enable Proprietary Software Sources&amp;#8221; button that would allow users to switch from the floss subset to the full Flathub if they so choose.&lt;/p&gt;
  4434.  
  4435.  
  4436.  
  4437. &lt;p&gt;This goal &lt;em&gt;can&lt;/em&gt; be implemented today, but we should wait because Flathub has some problems that we ought to fix first. More on that below.&lt;/p&gt;
  4438.  
  4439.  
  4440.  
  4441. &lt;h3 class="wp-block-heading"&gt;All Default Applications are Fedora Flatpak Applications&lt;/h3&gt;
  4442.  
  4443.  
  4444.  
  4445. &lt;p&gt;All applications installed by default in Fedora Workstation should be Fedora Flatpaks. (Or almost all. Certain exceptions, like gnome-control-center, would make more sense as part of the OS image rather than as a Flatpak.)&lt;/p&gt;
  4446.  
  4447.  
  4448.  
  4449. &lt;p&gt;Notice that I said &lt;em&gt;Fedora&lt;/em&gt; Flatpaks, not Flathub. Fedora surely does need to control the handful of applications that are shipped by default. We don&amp;#8217;t want to be at the mercy of Flathub to provide the core user experience.&lt;/p&gt;
  4450.  
  4451.  
  4452.  
  4453. &lt;p&gt;&lt;a class="external" href="https://pagure.io/fedora-workstation/issue/269#comment-954053"&gt;There has been recent progress towards this goal&lt;/a&gt;, although it&amp;#8217;s not ready yet.&lt;/p&gt;
  4454.  
  4455.  
  4456.  
  4457. &lt;h3 class="wp-block-heading"&gt;All Other Applications are Flathub Flatpaks&lt;/h3&gt;
  4458.  
  4459.  
  4460.  
  4461. &lt;p&gt;With the exception of the default Fedora Flatpak applications, Flathub should be the only source of applications in GNOME Software.&lt;/p&gt;
  4462.  
  4463.  
  4464.  
  4465. &lt;p&gt;It will soon be time to turn off GNOME Software&amp;#8217;s support for installing RPM applications, making it a Flatpak-only software center by default. (Because GNOME Software uses a plugin architecture, users of traditional package-based Fedora who want to use GNOME Software to install RPM applications would still be able to do so by installing a subpackage providing a plugin, if desired.)&lt;/p&gt;
  4466.  
  4467.  
  4468.  
  4469. &lt;p&gt;This requirement is an end goal. It can be done today, but it doesn&amp;#8217;t necessarily need to be an immediate&lt;em&gt; &lt;/em&gt;next step.&lt;/p&gt;
  4470.  
  4471.  
  4472.  
  4473. &lt;h3 class="wp-block-heading" id="flathub-improvements"&gt;Flathub Must Improve&lt;/h3&gt;
  4474.  
  4475.  
  4476.  
  4477. &lt;p&gt;Flathub has a few serious problems, and needs to make some policy changes &lt;em&gt;before&lt;/em&gt; Fedora enables it by default. I&amp;#8217;ll discuss this in more detail next.&lt;/p&gt;
  4478.  
  4479.  
  4480.  
  4481. &lt;h3 class="wp-block-heading"&gt;Fedora Must Help&lt;/h3&gt;
  4482.  
  4483.  
  4484.  
  4485. &lt;p&gt;We should not make demands of Flathub without helping to implement them. Fedora has a large developer community and significant resources. We must not barge in and attempt to take over the Flathub project; instead, let&amp;#8217;s increase our activity in the Flathub community somewhat, and lend a hand where requested.&lt;/p&gt;
  4486.  
  4487.  
  4488.  
  4489. &lt;h2 class="wp-block-heading"&gt;The Case for Fedora Flatpaks&lt;/h2&gt;
  4490.  
  4491.  
  4492.  
  4493. &lt;p&gt;Earlier this year, Yaakov presented &lt;a class="external" href="https://yselkowitz.github.io/blog/2025/02/25/the-case-for-fedora-flatpaks.html"&gt;The Case for Fedora Flatpaks&lt;/a&gt;. This is the strongest argument I&amp;#8217;ve seen in favor of Fedora Flatpaks. It complains about five problems with Flathub:&lt;/p&gt;
  4494.  
  4495.  
  4496.  
  4497. &lt;ul class="wp-block-list"&gt;
  4498. &lt;li&gt;Lack of source and build system provenance: on this point, Yaakov is completely right. This is a serious problem, and it would be unacceptable for Fedora to embrace Flathub before it is fixed. More on this below.&lt;/li&gt;
  4499.  
  4500.  
  4501.  
  4502. &lt;li&gt;Lack of separation between FOSS, legally encumbered, and proprietary software: this is not a real problem. Flathub already has a floss subset to separate open source vs. proprietary software; it may not be a separate repository, but that hardly matters because subsets allow us to achieve an equivalent user experience. Then there is indeed no separate subset for legally-encumbered software, but this also does not matter. Desktop users invariably wish to install encumbered software; I have yet to meet a user who does not want multimedia playback to work, after all. Fedora cannot offer encumbered multimedia codecs, but Flathub can, and that&amp;#8217;s a major advantage for Flathub. Users and operating systems can block the multimedia extensions if truly desired. Lastly, some of the plainly-unlicensed proprietary software currently available on Flathub does admittedly seem pretty clearly outrageous, but if this is a concern for you, simply stick to the floss subset.&lt;/li&gt;
  4503.  
  4504.  
  4505.  
  4506. &lt;li&gt;Lack of systemic upgrading of applications to the latest runtime: again, Yaakov is correct. This is a serious problem, and it would be unacceptable for Fedora to embrace Flathub before it is fixed. More on this below.&lt;/li&gt;
  4507.  
  4508.  
  4509.  
  4510. &lt;li&gt;Lack of coordination of changes to non-runtime dependencies: this is a &lt;em&gt;difference&lt;/em&gt; from Fedora, but it&amp;#8217;s not necessarily a problem. In fact, allowing applications to have different versions of dependencies can be quite convenient, since upgrading dependencies can sometimes break applications. It does become a problem when bundled dependencies become significantly outdated, though, as this creates security risk. More on this below.&lt;/li&gt;
  4511.  
  4512.  
  4513.  
  4514. &lt;li&gt;Lack of systemic community engagement: it&amp;#8217;s silly to claim that Flathub has no community. Unresponsive Flathub maintainers are a real problem, but Fedora has an unresponsive maintainer problem too, so this can hardly count as a point against Flathub. That said, yes, Flathub needs a better way to flag unresponsive maintainers.&lt;/li&gt;
  4515. &lt;/ul&gt;
  4516.  
  4517.  
  4518.  
  4519. &lt;p&gt;So now we have some good reasons to create Fedora Flatpaks. But maintaining Flatpaks is a tremendous effort. Is it really worth doing if we can improve Flathub instead?&lt;/p&gt;
  4520.  
  4521.  
  4522.  
  4523. &lt;h2 class="wp-block-heading"&gt;Flathub Must Improve&lt;/h2&gt;
  4524.  
  4525.  
  4526.  
  4527. &lt;p&gt;I propose the following improvements:&lt;/p&gt;
  4528.  
  4529.  
  4530.  
  4531. &lt;ul class="wp-block-list"&gt;
  4532. &lt;li&gt;Open source software must be built from source on trusted infrastructure.&lt;/li&gt;
  4533.  
  4534.  
  4535.  
  4536. &lt;li&gt;Applications must not depend on end-of-life runtimes.&lt;/li&gt;
  4537.  
  4538.  
  4539.  
  4540. &lt;li&gt;Applications must use flatpak-external-data-checker to monitor bundled dependencies wherever possible.&lt;/li&gt;
  4541.  
  4542.  
  4543.  
  4544. &lt;li&gt;Sandbox holes must be phased out, except where this is fundamentally technically infeasible.&lt;/li&gt;
  4545. &lt;/ul&gt;
  4546.  
  4547.  
  4548.  
  4549. &lt;p&gt;Let&amp;#8217;s discuss each point in more detail.&lt;/p&gt;
  4550.  
  4551.  
  4552.  
  4553. &lt;h3 class="wp-block-heading"&gt;Build Open Source from Source&lt;/h3&gt;
  4554.  
  4555.  
  4556.  
  4557. &lt;p&gt;Open source software can contain all manner of vulnerabilities. Although unlikely, it might even contain malicious backdoors. Building from source does nothing to guarantee that the software is in any way safe to use (and if it&amp;#8217;s written in C or C++, then &lt;a class="external" href="http://wingolog.org/archives/2011/10/13/whats-your-c-migration-plan"&gt;it&amp;#8217;s definitely not safe&lt;/a&gt;). But it sets an essential baseline: you can at least be confident that the binary you install on your computer actually corresponds to the provided source code, assuming the build infrastructure is trusted and not compromised. And if the package supports &lt;a class="external" href="https://lwn.net/Articles/1014979/"&gt;reproducible builds&lt;/a&gt;, then you can reliably detect malicious infrastructure, too!&lt;/p&gt;
  4558.  
  4559.  
  4560.  
  4561. &lt;p&gt;In contrast, when shipping a prebuilt binary, whoever built the binary can easily insert an undetectable backdoor; there is no need to resort to &lt;a class="external" href="https://en.wikipedia.org/wiki/XZ_Utils_backdoor"&gt;stealthy obfuscation tactics&lt;/a&gt;. With proprietary software, this risk is inherent and unavoidable: users just have to accept the risk and trust that whoever built the software is not malicious. Fine. But users generally do not expect this risk to extend to open source software, because all Linux operating systems fortunately require open source software to be built from source. Open source software not built from source is unusual and is invariably treated as a &lt;a class="external" href="https://bugs.webkit.org/show_bug.cgi?id=273435"&gt;serious bug&lt;/a&gt;.&lt;/p&gt;
  4562.  
  4563.  
  4564.  
  4565. &lt;p&gt;Flathub is different. On Flathub, shipping prebuilt binaries of open source software is, sadly, a common accepted practice. &lt;a class="external" href="https://pagure.io/fedora-workstation/issue/463#comment-964849"&gt;Here are several examples.&lt;/a&gt; &lt;s&gt;Flathub itself admits that &lt;a class="external" href="https://docs.flathub.org/blog/app-safety-layered-approach-source-to-user#special-cases"&gt;around 6% of its software is not built from source&lt;/a&gt;, so this problem is pervasive, not an isolated issue. (Although that percentage unfortunately considers proprietary software in addition to open source software, overstating the badness of the problem, because building proprietary software from source is impossible and not doing so is not a problem.)&lt;/s&gt; &lt;strong&gt;Update:&lt;/strong&gt; I&amp;#8217;ve been advised that I misunderstood the purpose of extra-data. Most apps that ship prebuilt binaries do not use extra-data. I&amp;#8217;m not sure how many apps are shipping prebuilt binaries, but the problem is pervasive.&lt;/p&gt;
  4566.  
  4567.  
  4568.  
  4569. &lt;p&gt;Security is not the only problem. In practice, Flathub applications that do not build from source sometimes package binaries only for x86_64, leaving aarch64 users entirely out of luck, even though Flathub normally supports aarch64, an architecture that is important for Fedora. This is frequently cited by Flathub&amp;#8217;s opponents as major disadvantage relative to Fedora Flatpaks.&lt;/p&gt;
  4570.  
  4571.  
  4572.  
  4573. &lt;p&gt;A plan to fix this should exist before Fedora enables Flathub by default. I can think of a few possible solutions:&lt;/p&gt;
  4574.  
  4575.  
  4576.  
  4577. &lt;ul class="wp-block-list"&gt;
  4578. &lt;li&gt;Create a new subset for open source software not built from source, so Fedora can filter out this subset. Users can enable the subset at their own risk. This is hardly ideal, but it would allow Fedora to enable Flathub without exposing users to prebuilt open source software.&lt;/li&gt;
  4579.  
  4580.  
  4581.  
  4582. &lt;li&gt;Declare that any software not built from source should be treated equivalent to proprietary software, and moved out of the floss subset. This is not quite right, because it &lt;em&gt;is&lt;/em&gt; open source, but it has the same security and trust characteristics of proprietary software, so it&amp;#8217;s not unreasonable either.&lt;/li&gt;
  4583.  
  4584.  
  4585.  
  4586. &lt;li&gt;Set a flag date by which any open source software not built from source must be delisted from Flathub. I&amp;#8217;ll arbitrarily propose July 1, 2027, which should be a generous amount of time to fix apps. This is my preferred solution. It can also be combined with either of the above.&lt;/li&gt;
  4587. &lt;/ul&gt;
  4588.  
  4589.  
  4590.  
  4591. &lt;p&gt;Some of the apps not currently built from source are Electron packages. Electron takes a long time to build, and I wonder if building every Electron app from source might overwhelm Flathub&amp;#8217;s existing build infrastructure. We will need some sort of solution to this. I wonder if it would be possible to build Electron runtimes to provide a few common versions of Electron. Alternatively, Flathub might just need more infrastructure funding.&lt;/p&gt;
  4592.  
  4593.  
  4594.  
  4595. &lt;p&gt;Tangent time: a few applications on Flathub are built on non-Flathub infrastructure, notably Firefox and OBS Studio. It would be &lt;em&gt;better&lt;/em&gt; to build everything on Flathub&amp;#8217;s infrastructure to reduce risk of infrastructure compromise, but as long as this practice is limited to only a few well-known applications using trusted infrastructure, then the risk is lower and it&amp;#8217;s not necessarily a serious problem. The third-party infrastructure should be designed thoughtfully, and only the infrastructure should be able to upload binaries; it should not be possible for a human to manually upload a build. It&amp;#8217;s unfortunately not always easy to assess whether an application complies with these guidelines or not. &lt;a class="external" href="https://github.com/obsproject/obs-studio/pull/10934"&gt;Let&amp;#8217;s consider OBS Studio.&lt;/a&gt; I appreciate that it &lt;em&gt;almost&lt;/em&gt; follows my guidelines, because the binaries are normally built by GitHub Actions and will therefore correspond with the project&amp;#8217;s source code, but I think a malicious maintainer &lt;em&gt;could&lt;/em&gt; bypass that by uploading a malicious GitHub binary release? This is not ideal, but fortunately custom infrastructure is an unusual edge case, rather than a pervasive problem.&lt;/p&gt;
  4596.  
  4597.  
  4598.  
  4599. &lt;h3 class="wp-block-heading"&gt;Penalize End-of-life Runtimes&lt;/h3&gt;
  4600.  
  4601.  
  4602.  
  4603. &lt;p&gt;When a Flatpak runtime reaches end-of-life (EOL), it stops receiving all updates, including security updates. How pervasive are EOL runtimes on Flathub? Using the Runtime Distribution section of &lt;a class="external" href="https://flathub.org/statistics"&gt;Flathub Statistics&lt;/a&gt; and &lt;a class="external" href="https://discussion.fedoraproject.org/t/proposal-enable-flathub-by-default/157011/86"&gt;some knowledge of which runtimes are still supported&lt;/a&gt;, I determined that 994 out of 3,438 apps are currently using an EOL runtime. Ouch. (Note that the statistics page says there are 3,063 total desktop apps, but for whatever reason, the number of apps presented in the Runtime Distribution graph is higher. Could there really be 375 command line apps on Flathub?)&lt;/p&gt;
  4604.  
  4605.  
  4606.  
  4607. &lt;p&gt;Using an EOL runtime is dangerous and irresponsible, and developers who claim otherwise are not good at risk assessment. Some developers will say that security does not matter because their app is not security-critical. It&amp;#8217;s true that most security vulnerabilities are not actually terribly important or worth panicking over, but this does not mean it&amp;#8217;s acceptable to stop fixing vulnerabilities altogether. In fact, security matters for &lt;em&gt;most&lt;/em&gt; apps. A few exceptions would be apps that do not open files and also do not use the network, but that’s really probably not many apps.&lt;/p&gt;
  4608.  
  4609.  
  4610.  
  4611. &lt;p&gt;I recently saw a developer use the example of a music player application to argue that EOL runtimes are not actually a serious problem. This developer picked a terrible example. Our hypothetical music player application can notably open audio files. Applications that parse files are inherently high risk because users love to open untrusted files. If you give me a file, the first thing I’m going to do is open it to see what it is. Who wouldn&amp;#8217;t? Curiosity is human nature. And a music player probably uses GStreamer, which puts it at the &lt;a class="external" href="https://scarybeastsecurity.blogspot.com/2016/11/0day-poc-risky-design-decisions-in.html"&gt;very&lt;/a&gt; &lt;a class="external" href="https://scarybeastsecurity.blogspot.com/2016/11/0day-exploit-advancing-exploitation.html"&gt;highest&lt;/a&gt; &lt;a class="external" href="https://github.blog/security/vulnerability-research/uncovering-gstreamer-secrets/"&gt;tier&lt;/a&gt; of security risk (alongside your PDF reader, email client, and web browser). I know of exactly one case of a GNOME user being exploited in the wild: it happened when the user &lt;a class="external" href="https://www.vice.com/en/article/facebook-helped-fbi-hack-child-predator-buster-hernandez/"&gt;opened a booby-trapped video&lt;/a&gt; using Totem, GNOME&amp;#8217;s GStreamer-based video player. At least your web browser is guaranteed to be heavily sandboxed; your music player &lt;a class="external" href="https://gitlab.gnome.org/neithern/g4music/-/issues/153"&gt;might very well not be&lt;/a&gt;.&lt;/p&gt;
  4612.  
  4613.  
  4614.  
  4615. &lt;p&gt;The Flatpak sandbox certainly helps to mitigate the impact of vulnerabilities, but sandboxes are intended to be a defense in depth measure. They should not be treated as a primary security mechanism or as an excuse to not fix security bugs. Also, too Flatpak many apps subvert the sandbox entirely.&lt;/p&gt;
  4616.  
  4617.  
  4618.  
  4619. &lt;p&gt;Of course, each app has a different risk level. The risk of you being attacked via GNOME Calculator is pretty low. It does not open files, and the only untrusted input it parses is currency conversion data provided by the International Monetary Fund. Life goes on if your calculator is unmaintained. Any number of other applications are &lt;em&gt;probably&lt;/em&gt; generally safe. But it would be entirely impractical to assess 3000 different apps individually to determine whether they are a significant security risk or not. And independent of security considerations, use of an EOL runtime is a good baseline to determine whether the application is adequately maintained, so that abandoned apps can be eventually delisted. It would not be useful to make exceptions.&lt;/p&gt;
  4620.  
  4621.  
  4622.  
  4623. &lt;p&gt;The solution here is simple enough:&lt;/p&gt;
  4624.  
  4625.  
  4626.  
  4627. &lt;ul class="wp-block-list"&gt;
  4628. &lt;li&gt;It should not be possible to build an application that depends on an EOL runtime, to motivate active maintainers to update to a newer runtime. Flathub already implemented this rule in the past, but it got dropped at some point.&lt;/li&gt;
  4629.  
  4630.  
  4631.  
  4632. &lt;li&gt;An application that depends on an EOL runtime for too long should eventually be delisted. Perhaps 6 months or 1 year would be good deadlines.&lt;/li&gt;
  4633.  
  4634.  
  4635.  
  4636. &lt;li&gt;A monitoring dashboard would make it easier to see which apps are using maintained runtimes and which need to be fixed.&lt;/li&gt;
  4637. &lt;/ul&gt;
  4638.  
  4639.  
  4640.  
  4641. &lt;h3 class="wp-block-heading"&gt;Monitor Bundled Dependencies&lt;/h3&gt;
  4642.  
  4643.  
  4644.  
  4645. &lt;p&gt;Flatpak apps have to bundle any dependencies not present in their runtime. This creates considerable security risk if the maintainer of the Flathub packaging does not regularly update the dependencies. The negative consequences are identical to using an EOL runtime.&lt;/p&gt;
  4646.  
  4647.  
  4648.  
  4649. &lt;p&gt;Fortunately, Flathub already has a tool to deal with this problem: &lt;a class="external" href="https://github.com/flathub-infra/flatpak-external-data-checker"&gt;flatpak-external-data-checker&lt;/a&gt;. This tool automatically opens pull requests to update bundled dependencies when a new version is available. However, not all applications use flatpak-external-data-checker, and not all applications that &lt;em&gt;do&lt;/em&gt; use it do so for all dependencies, and none of this matters if the app&amp;#8217;s packaging is no longer maintained.&lt;/p&gt;
  4650.  
  4651.  
  4652.  
  4653. &lt;p&gt;I don&amp;#8217;t know of any easy ways to monitor Flathub for outdated bundled dependencies, but given the number of apps using EOL runtimes, I assume the status quo is pretty bad. The next step here is to build better monitoring tools so we can better understand the scope of this problem.&lt;/p&gt;
  4654.  
  4655.  
  4656.  
  4657. &lt;h3 class="wp-block-heading"&gt;Phase Out Most Sandbox Holes (Eventually)&lt;/h3&gt;
  4658.  
  4659.  
  4660.  
  4661. &lt;p&gt;Applications that parse data are full of security vulnerabilities, like buffer overflows and use-after-frees. Skilled attackers can turn these vulnerabilities into exploits, using carefully-crafted malicious data to gain total control of your user account on your computer. They can then install malware, read all the files in your home directory, use your computer in a botnet, and do whatever else they want with it. But if the application is sandboxed, then a second type of exploit, called a sandbox escape, is needed before the app can harm your host operating system and access your personal data, so the attacker now has to exploit two vulnerabilities instead of just one. And while app vulnerabilities are extremely common, sandbox escapes are, &lt;a class="external" href="https://github.com/flatpak/flatpak/security"&gt;in theory&lt;/a&gt;, rare.&lt;/p&gt;
  4662.  
  4663.  
  4664.  
  4665. &lt;p&gt;In theory, Flatpak apps are drastically safer than distro-packaged apps because Flatpak provides a strong sandbox by default. The security benefit of the sandbox cannot be understated: it is amazing technology and greatly improves security relative to distro-packaged apps. But in practice, Flathub applications routinely subvert the sandbox by using expansive static permissions to open sandbox holes. Flathub &lt;a class="external" href="https://docs.flathub.org/blog/app-safety-layered-approach-source-to-user#submission--human-review"&gt;claims&lt;/a&gt; that it carefully reviews apps&amp;#8217; use of static permissions and allows only the most narrow permissions that are possible for the app to function properly. This claim is dubious because, in practice, the permissions of actual apps on Flathub are &lt;a class="external" href="https://flatkill.org/2020/"&gt;extremely broad&lt;/a&gt;, as often as not making a total mockery of the sandbox.&lt;/p&gt;
  4666.  
  4667.  
  4668.  
  4669. &lt;figure class="wp-block-image aligncenter size-full"&gt;&lt;a href="https://blogs.gnome.org/mcatanzaro/files/2025/07/Screenshot-From-2025-07-22-10-01-01.png"&gt;&lt;img alt="" class="wp-image-11072" height="574" src="https://blogs.gnome.org/mcatanzaro/files/2025/07/Screenshot-From-2025-07-22-10-01-01.png" width="639" /&gt;&lt;/a&gt;&lt;/figure&gt;
  4670.  
  4671.  
  4672.  
  4673. &lt;p&gt;While some applications use sandbox holes out of laziness, in many cases it&amp;#8217;s currently outright impossible to sandbox the application without breaking key functionality. For example, Sophie has documented &lt;a class="external" href="https://gitlab.gnome.org/GNOME/loupe/-/issues/61"&gt;many problems&lt;/a&gt; that necessitate sandbox holes in GNOME&amp;#8217;s image viewer, Loupe. These problems are fixable, but they require significant development work that has not happened yet. Should we punish the application by requiring it to break itself to conform to the requirements of the sandbox? The Flathub community has decided that the answer is no: application developers can, in practice, use whatever permissions they need to make the app work, even if this entirely subverts the sandbox.&lt;/p&gt;
  4674.  
  4675.  
  4676.  
  4677. &lt;p&gt;This was originally a good idea. By allowing flexibility with sandbox permissions, Flathub made it very easy to package apps, became extremely popular, and allowed Flatpak itself to become successful. But the original understanding of the Flatpak community was that this laxity would be temporary: eventually, the rules would be tightened and apps would be held to progressively higher standards, until sandbox holes would eventually become rare. Unfortunately, this is taking too long. Flatpak has been around for a decade now, but this goal is not within reach.&lt;/p&gt;
  4678.  
  4679.  
  4680.  
  4681. &lt;p&gt;Tightening sandbox holes does &lt;em&gt;not&lt;/em&gt; need to be a blocker for adopting Flathub in Fedora because it&amp;#8217;s not a problem relative to the status quo in Fedora. Fedora Flatpaks have the exact same problem, and Fedora&amp;#8217;s distro-packaged apps are not sandboxed at all (with only a few exceptions, like your web browser). But it&amp;#8217;s long past time to at least make a plan for how to eventually phase out sandbox holes wherever possible. (In some cases, it won&amp;#8217;t ever be possible; e.g. sandboxing a file manager or disk usage analyzer does not make any sense.) It&amp;#8217;s currently too soon to use sticks to punish applications for having too many sandbox holes, but sticks will be necessary eventually, hopefully within the next 5 years. In the meantime, we &lt;em&gt;can&lt;/em&gt; immediately begin to use carrots to reward app developers for eliminating holes. We will need to discuss specifics.&lt;/p&gt;
  4682.  
  4683.  
  4684.  
  4685. &lt;p&gt;We also need more developers to help improve xdg-desktop-portal, the component that allows sandboxed apps to safely access resources on the host system without using sandbox holes. This is too much work for any individual; it will require many developers working together.&lt;/p&gt;
  4686.  
  4687.  
  4688.  
  4689. &lt;h2 class="wp-block-heading"&gt;Software Source Prioritization&lt;/h2&gt;
  4690.  
  4691.  
  4692.  
  4693. &lt;p&gt;So, let&amp;#8217;s say we successfully engage with the Flathub project and make some good progress on solving the above problems. What should happen next?&lt;/p&gt;
  4694.  
  4695.  
  4696.  
  4697. &lt;p&gt;Fedora is a community of doers. We cannot tell Fedora contributors to stop doing work they wish to do. Accordingly, it&amp;#8217;s unlikely that anybody will propose to shut down the Fedora Flatpak project so long as developers are still working on it. Don&amp;#8217;t expect that to happen.&lt;/p&gt;
  4698.  
  4699.  
  4700.  
  4701. &lt;p&gt;However, this doesn&amp;#8217;t mean Fedora contributors have a divine right for their packaged applications to be presented to users by default. Each Fedora edition (or spin) should be allowed to decide for itself what should be presented to the user in its software center. It&amp;#8217;s time for the Fedora Engineering Steering Committee (FESCo) to allow Fedora editions to prefer third-party content over content from Fedora itself.&lt;/p&gt;
  4702.  
  4703.  
  4704.  
  4705. &lt;p&gt;We have a few options as to how exactly this should work:&lt;/p&gt;
  4706.  
  4707.  
  4708.  
  4709. &lt;ul class="wp-block-list"&gt;
  4710. &lt;li&gt;We could choose to unconditionally prioritize all Flathub Flatpaks over Fedora Flatpaks, as I proposed earlier this year (&lt;a class="external" href="https://pagure.io/fedora-workstation/issue/463"&gt;Workstation ticket&lt;/a&gt;, &lt;a class="external" href="https://lwn.net/Articles/1011511"&gt;LWN coverage&lt;/a&gt;). The precedence in GNOME Software would be &lt;strong&gt;Flathub &amp;gt; Fedora Flatpaks&lt;/strong&gt;.&lt;/li&gt;
  4711.  
  4712.  
  4713.  
  4714. &lt;li&gt;Alternatively, we could leave Fedora Flatpaks with highest priority, and instead apply a filter such that only Fedora Flatpaks that are installed by default are visible in GNOME Software. This is my preferred solution; there is already an active change proposal for Fedora 43 (&lt;a class="external" href="https://fedoraproject.org/wiki/Changes/FilterFedoraFlatpaksAtomicDesktops"&gt;proposal&lt;/a&gt;, &lt;a class="external" href="https://discussion.fedoraproject.org/t/f43-change-proposal-filter-fedora-flatpaks-for-atomic-desktops-self-contained/157262"&gt;discussion&lt;/a&gt;), and it has received considerable support from the Fedora community. Although the proposal only targets atomic editions like Silverblue and Kinoite for now, it makes sense to extend it to Fedora Workstation as well. The precedence would be &lt;strong&gt;Filtered Fedora Flatpaks &amp;gt; Flathub&lt;/strong&gt;.&lt;/li&gt;
  4715. &lt;/ul&gt;
  4716.  
  4717.  
  4718.  
  4719. &lt;p&gt;When considering our desired end state, we can stop there; those are the only two options because of my &amp;#8220;All Other Applications are Flathub Flatpaks&amp;#8221; requirement: in an atomic OS, it&amp;#8217;s no longer possible to install RPM-packaged applications, after all. But in the meantime, as a transitional measure, we still need to consider where RPMs fit in until such time that Fedora Workstation is ready to remove RPM applications from GNOME Software.&lt;/p&gt;
  4720.  
  4721.  
  4722.  
  4723. &lt;p&gt;We have several possible precedence options. The most obvious option, consistent with my proposals above, is: &lt;strong&gt;Flathub &amp;gt; Fedora RPMs &amp;gt; Fedora Flatpaks&lt;/strong&gt;. And that would be fine, certainly a huge improvement over the status quo, which is Fedora Flatpaks &amp;gt; Fedora RPMs &amp;gt; Flathub.&lt;/p&gt;
  4724.  
  4725.  
  4726.  
  4727. &lt;p&gt;But we could also &lt;em&gt;conditionally&lt;/em&gt; prioritize Flathub Flatpaks over Fedora Flatpaks or Fedora RPMs, such that the Flathub Flatpak is preferred only if it meets certain criteria. This makes sense if we want to nudge Flathub maintainers towards adopting certain best practices we might wish to encourage. Several Fedora users have proposed that we prefer Flathub only if the app has Verified status, indicating that the Flathub maintainer is the same as the upstream maintainer. But I do not care very much whether the app is verified or not; it&amp;#8217;s perfectly acceptable for a third-party developer to maintain the Flathub packaging if the upstream developers do not wish to do so, and I don&amp;#8217;t see any need to discourage this. Instead, I would rather consider whether the app receives a Probably Safe safety rating in GNOME Software. This would be a nice carrot to encourage app developers to tighten sandbox permissions. (Of course, this would be a transitional measure only, because eventually the goal is for Flathub to be the only software source.)&lt;/p&gt;
  4728.  
  4729.  
  4730.  
  4731. &lt;p&gt;There are many possible outcomes here, but here are my three favorites, in order:&lt;/p&gt;
  4732.  
  4733.  
  4734.  
  4735. &lt;ol class="wp-block-list"&gt;
  4736. &lt;li&gt;My favorite option: &lt;strong&gt;Filtered Fedora Flatpaks &amp;gt; Probably Safe Flathub &amp;gt; Fedora RPMs &amp;gt; Potentially Unsafe Flathub&lt;/strong&gt;. Fedora Flatpaks take priority, but this won&amp;#8217;t hurt anything because only applications shipped by default will be available, and those will be the ones that receive the most testing. This is not a desirable end state because it is complicated and it will be confusing to explain to users why a certain software source was preferred. But in the long run, when Fedora RPMs are eventually removed, it will simplify to Filtered Fedora Flatpaks &amp;gt; Flathub, which is elegant.&lt;/li&gt;
  4737.  
  4738.  
  4739.  
  4740. &lt;li&gt;A simple option, the same thing but without the conditional prioritization: &lt;strong&gt;Filtered Fedora Flatpaks &amp;gt; Flathub &amp;gt; Fedora RPMs&lt;/strong&gt;.&lt;/li&gt;
  4741.  
  4742.  
  4743.  
  4744. &lt;li&gt;Alternative option: &lt;strong&gt;Probably Safe Flathub &amp;gt; Fedora RPMs &amp;gt; Potentially Unsafe Flathub &amp;gt; Unfiltered Fedora Flatpaks&lt;/strong&gt;. When Fedora RPMs are eventually removed, this will simplify to Flathub &amp;gt; Unfiltered Fedora Flatpaks. This alternative option behaves almost the same as the above, except allows users to manually select the Fedora Flatpak if they wish to do so, rather than filtering them out. But there is a significant disadvantage: if you uninstall an application that is installed by default, then reinstall the application, it would come from Flathub rather than Fedora Flatpaks, which is unexpected. So we&amp;#8217;ll probably want to hardcode exceptions for default apps to prefer Fedora Flatpaks.&lt;/li&gt;
  4745.  
  4746.  
  4747.  
  4748. &lt;li&gt;The corresponding simple option without conditional prioritization: &lt;strong&gt;Flathub &amp;gt; Fedora RPMs &amp;gt; Unfiltered Fedora Flatpaks&lt;/strong&gt;.&lt;/li&gt;
  4749. &lt;/ol&gt;
  4750.  
  4751.  
  4752.  
  4753. &lt;p&gt;Any of these options would be fine.&lt;/p&gt;
  4754.  
  4755.  
  4756.  
  4757. &lt;h2 class="wp-block-heading"&gt;Conclusion&lt;/h2&gt;
  4758.  
  4759.  
  4760.  
  4761. &lt;p&gt;Flathub is, frankly, not safe enough to be enabled by default in Fedora Workstation today. But these problems are fixable. Helping Flathub become more trustworthy will be far easier than competing against it by maintaining thousands of Fedora Flatpaks. Enabling Flathub by default should be a strategic priority for Fedora Workstation.&lt;/p&gt;
  4762.  
  4763.  
  4764.  
  4765. &lt;p&gt;I anticipate a lively debate on social media, on Matrix, and in the comments. And I am especially eager to see whether the Fedora and Flathub communities accept my arguments as persuasive. FESCo will be considering the &lt;a class="external" href="https://fedoraproject.org/wiki/Changes/FilterFedoraFlatpaksAtomicDesktops"&gt;Filter Fedora Flatpaks for Atomic Desktops proposal&lt;/a&gt; imminently, so the first test is soon.&lt;/p&gt;</description><author>Michael Catanzaro</author><dc:creator>Michael Catanzaro</dc:creator><pubDate>Tue, 22 Jul 2025 04:10:53 GMT</pubDate><guid isPermaLink="true">https://blogs.gnome.org/mcatanzaro/2025/07/21/fedora-must-carefully-embrace-flathub/</guid></item><item><title>Alley Chaggar: YAML Research</title><link>https://alleych.github.io/gnome/yaml-research-init/</link><description>&lt;h1 id="intro"&gt;Intro&lt;/h1&gt;
  4766.  
  4767. &lt;p&gt;Hi everyone, sorry for the late post. Midterms are this week for GSoC, which means I’m halfway through GSoC. It’s been an incredible experience so far, and I know it’s going to continue to be great.&lt;/p&gt;
  4768.  
  4769. &lt;h1 id="api-vs-abi"&gt;API vs. ABI&lt;/h1&gt;
  4770.  
  4771. &lt;p&gt;What is the difference between an application programming interface versus an application binary interface? In the beginning, this question tripped me out and confused me, because I wasn’t familiar with ABIs. Understanding what an ABI is has helped me decide which libraries I should consider using in the codegen phase. When talking about Vala, Vala is designed to use a C ABI. First, let’s understand what an API and ABI are separately and then compare them.&lt;/p&gt;
  4772.  
  4773. &lt;h2 id="api"&gt;API&lt;/h2&gt;
  4774.  
  4775. &lt;p&gt;Personally, I think the understanding of APIs is more popular and well-known than ABIs. An API is usually, at a high level, defined by two software components or computers communicating with each other using a set of definitions and protocols. This definition I always thought was pretty vague and expansive. When dealing with code-level APIs, I like to understand it as APIs are existing entities in the user code (source code) that have functions, constants, structures, etc. You can think of it as when you write code, you access libraries through an API. For example, when you write &lt;code class="language-vala highlighter-rouge"&gt;&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="n"&gt;hello&lt;/span&gt; &lt;span class="n"&gt;world&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;/code&gt; in Python, &lt;code class="language-vala highlighter-rouge"&gt;&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;/code&gt; is a part of Python’s standard library API.&lt;/p&gt;
  4776.  
  4777. &lt;h2 id="abi"&gt;ABI&lt;/h2&gt;
  4778.  
  4779. &lt;p&gt;ABI, on the other hand, is very similar, but instead of the compiler time, they are executed during runtime. Runtime means when your program is done compiling (going through the lexical, syntax, semantic analysis, etc) and the machine is actually running your executable. Its goals are very low-level and entails how compiled code should interact, particularly in the context of operating systems and libraries. It has protocols and standards for how the OS handles your program, such as storage, memory, hardware, and about how your compiled binary works with other compiled components.&lt;/p&gt;
  4780.  
  4781. &lt;h1 id="yaml-libraries"&gt;YAML Libraries&lt;/h1&gt;
  4782.  
  4783. &lt;p&gt;I’ve started to look into YAML and XML (mainly YAML). I’ve looked into many different libraries dealing with YAML, some such as &lt;a href="https://github.com/pluie-org/lib-yaml"&gt;pluie-yaml&lt;/a&gt;, &lt;a href="https://github.com/rainwoodman/libyaml-glib"&gt;libyaml-glib&lt;/a&gt;, &lt;a href="https://github.com/jimmuhk/glib-yaml"&gt;glib-yaml&lt;/a&gt;, and &lt;a href="https://github.com/yaml/libyaml/tree/master"&gt;libyaml&lt;/a&gt;. To my understanding and research, there are no well-maintained YAML libraries that integrate GObject or GLib. The goal is to find a well-maintained library that I can use in the codegen.&lt;/p&gt;
  4784.  
  4785. &lt;h2 id="pluie-yaml"&gt;Pluie yaml&lt;/h2&gt;
  4786.  
  4787. &lt;p&gt;I mentioned &lt;a href="https://github.com/pluie-org/lib-yaml"&gt;pluie-yaml&lt;/a&gt;, but this library isn’t a C library like &lt;a href="https://gitlab.gnome.org/GNOME/json-glib/-/tree/main?ref_type=heads"&gt;json-glib&lt;/a&gt;, it’s a shared Vala library. The good thing is that the codegen can use pure Vala libraries because Vala libraries have a C ABI, however, the bad part is that this library is not well-maintained. The last activity was 7 years ago.&lt;/p&gt;
  4788.  
  4789. &lt;h2 id="libyaml-glib"&gt;Libyaml glib&lt;/h2&gt;
  4790.  
  4791. &lt;p&gt;&lt;a href="https://github.com/rainwoodman/libyaml-glib"&gt;Libyaml-glib&lt;/a&gt; is a GLib binding of libyaml, plus a GObject builder that understands YAML. Just like pluie, it’s not a C library. It’s written in Vala. And just like pluie, it’s not well-maintained, with the last activity even stretching to longer, 9 years ago.&lt;/p&gt;
  4792.  
  4793. &lt;h2 id="glib-yaml"&gt;Glib yaml&lt;/h2&gt;
  4794.  
  4795. &lt;p&gt;&lt;a href="https://github.com/jimmuhk/glib-yaml"&gt;Glib-yaml&lt;/a&gt; is a GLib-based YAML parser written in C. It again, just like the other libraries, doesn’t pass the maintenance check since it’s been years of no updates or commits in the repo. Going all the way back to 13 years ago. It’s also only a parser, and it doesn’t serialize or emit YAML, so even if it were well-maintained, I’d still need to emit YAML either manually or find another library that does so.&lt;/p&gt;
  4796.  
  4797. &lt;h2 id="libyaml"&gt;Libyaml&lt;/h2&gt;
  4798.  
  4799. &lt;p&gt;In conclusion, &lt;a href="https://github.com/yaml/libyaml/tree/master"&gt;libyaml&lt;/a&gt; is the C library that I will be using for parsing and emitting YAML. It has a C ABI, and it’s the most well-maintained out of all of the other libraries. Vala already has a VAPI file binding it, &lt;a href="https://gitlab.gnome.org/GNOME/vala-extra-vapis/-/blob/master/yaml-0.1.vapi?ref_type=heads"&gt;yaml-0.1.vapi&lt;/a&gt;. However, there is no GObject or GLib integration, unlike json-glib, but that should be fine.&lt;/p&gt;</description><author>Alley Chaggar</author><dc:creator>Alley Chaggar</dc:creator><pubDate>Fri, 18 Jul 2025 05:00:00 GMT</pubDate><guid isPermaLink="true">https://alleych.github.io/gnome/yaml-research-init/</guid></item><item><title>Victor Ma: My first design doc</title><link>https://victorma.ca/posts/gsoc-5/</link><description>&lt;p&gt;In the last two weeks, I investigated some bugs, tested some fonts, and started working on a design doc.&lt;/p&gt;
  4800. &lt;h2 id="bugs"&gt;Bugs&lt;/h2&gt;
  4801. &lt;p&gt;I found two more UI-related bugs (&lt;a href="https://gitlab.gnome.org/jrb/crosswords/-/issues/280"&gt;1&lt;/a&gt;, &lt;a href="https://gitlab.gnome.org/jrb/crosswords/-/issues/282"&gt;2&lt;/a&gt;). These are in addition to the ones I mentioned in my last blog post&amp;mdash;and they&amp;rsquo;re all related. They have to do with GTK and sidebars and resizing.&lt;/p&gt;
  4802. &lt;p&gt;I looked into them briefly, but in the end, my mentor decided that the bugs are complicated enough that he should &lt;a href="https://gitlab.gnome.org/jrb/crosswords/-/merge_requests/258"&gt;handle them himself&lt;/a&gt;. His fix was to replace all the &lt;code&gt;.ui&lt;/code&gt; files with &lt;a href="https://gitlab.gnome.org/GNOME/blueprint-compiler"&gt;Blueprint&lt;/a&gt; files, and then make changes from there to squash all the bugs. The port to Blueprint also makes it much easier to edit the UI in the future.&lt;/p&gt;
  4803. &lt;h2 id="font-testing"&gt;Font testing&lt;/h2&gt;
  4804. &lt;p&gt;Currently, GNOME Crosswords uses the default GNOME font, Cantarell. But we&amp;rsquo;ve never really explored the possibility of using other fonts. For example, what would Crosswords look like with a monospace font? Or with a handwriting font? This is what I set out to discover.&lt;/p&gt;
  4805. &lt;p&gt;To change the font, I used GTK Inspector, combined with this CSS selector, which targets the grid and word suggestions list:&lt;/p&gt;
  4806. &lt;pre tabindex="0"&gt;&lt;code&gt;edit-grid, wordlist {
  4807. font-family: FONT;
  4808. }
  4809. &lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This let me dynamically change the font, without having to recompile each time. I created a document with all the &lt;a href="https://pad.gnome.org/s/6mTne5Ehs"&gt;fonts that I tried&lt;/a&gt;.&lt;/p&gt;
  4810. &lt;p&gt;Here&amp;rsquo;s what &lt;em&gt;Source Code Pro&lt;/em&gt;, a monospace font, looks like. It gives a more rigid look&amp;mdash;especially for the word suggestion list, where all the letters line up vertically.
  4811.  
  4812. &lt;figure&gt;
  4813. &lt;img alt="Monospace font" src="https://victorma.ca/posts/gsoc-5/monospace.png" /&gt;
  4814. &lt;/figure&gt;
  4815.  
  4816.  
  4817. &lt;/p&gt;
  4818. &lt;p&gt;And here&amp;rsquo;s what &lt;em&gt;Annie Use Your Telescope&lt;/em&gt;, a handwriting font, looks like. It gives a fun, charming look to the crossword grid&amp;mdash;like it&amp;rsquo;s been filled out by hand. It&amp;rsquo;s a bit too unconventional to use as the default font, but it would definitely be cool to add as an option that the user can enable.
  4819.  
  4820. &lt;figure&gt;
  4821. &lt;img alt="Handwriting font" src="https://victorma.ca/posts/gsoc-5/handwriting.png" /&gt;
  4822. &lt;/figure&gt;
  4823.  
  4824.  
  4825. &lt;/p&gt;
  4826. &lt;h2 id="design-doc"&gt;Design doc&lt;/h2&gt;
  4827. &lt;p&gt;My current task is to improve the word suggestion algorithm for the Crosswords Editor. Last week, I starting working on a &lt;a href="https://pad.gnome.org/s/OAL239g-o"&gt;design doc&lt;/a&gt; that explains my intended change. Here&amp;rsquo;s a short snippet from the doc, which highlights the problem with our current word suggestion algorithm:&lt;/p&gt;
  4828. &lt;blockquote&gt;
  4829. &lt;p&gt;Consider the following grid:&lt;/p&gt;
  4830. &lt;pre tabindex="0"&gt;&lt;code&gt;+---+---+---+---+
  4831. | | | | Z |
  4832. +---+---+---+---+
  4833. | | | | E |
  4834. +---+---+---+---+
  4835. | | | | R |
  4836. +---+---+---+---+
  4837. | W | O | R | | &amp;lt; current slot
  4838. +---+---+---+---+
  4839. &lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The 4-Down slot begins with &lt;em&gt;ZER&lt;/em&gt;, so the only word it can be is &lt;em&gt;ZERO&lt;/em&gt;. This means that the cell in the bottom-right corner must be the letter &lt;em&gt;O&lt;/em&gt;.&lt;/p&gt;
  4840. &lt;p&gt;But 4-Across starts with &lt;em&gt;WOR&lt;/em&gt;. And &lt;em&gt;WORO&lt;/em&gt; is not a word. So the bottom-right corner cannot actually be the letter &lt;em&gt;O&lt;/em&gt;. This means that the slot is unfillable.&lt;/p&gt;
  4841. &lt;p&gt;If the cursor is on the bottom right cell, then our word suggestion algorithm correctly recognizes that the slot is unfillable and returns an empty list.&lt;/p&gt;
  4842. &lt;p&gt;But suppose the cursor is on one of the other cells in 4-Across. Then, the algorithm has no idea about 4-Down and the constraint it imposes. So, the algorithm returns all words that match the filter &lt;em&gt;WOR?&lt;/em&gt;, like &lt;em&gt;WORD&lt;/em&gt; and &lt;em&gt;WORM&lt;/em&gt;&amp;mdash;even though they do not actually fit the slot.&lt;/p&gt;&lt;/blockquote&gt;
  4843. &lt;h3 id="csps"&gt;CSPs&lt;/h3&gt;
  4844. &lt;p&gt;In the process of writing the doc, I came across the concept of a &lt;a href="https://cs.uwaterloo.ca/~jhoey/teaching/cs486/lecture4-nup.pdf"&gt;constraint satisfaction problem (CSP)&lt;/a&gt;, and the related AC-3 algorithm. A CSP is a formalization of a problem that&amp;hellip;well&amp;hellip;involves satisfying a constraint. And the AC-3 algorithm is an algorithm that&amp;rsquo;s sometimes used when solving CSPs.&lt;/p&gt;
  4845. &lt;p&gt;The problem of filling a crossword grid can be formulated as a CSP. And we can use the AC-3 algorithm to generate perfect word suggestion lists for every cell.&lt;/p&gt;
  4846. &lt;p&gt;This isn&amp;rsquo;t the approach I will be taking. However, we may decide to implement it in the future. So, I documented the &lt;a href="https://pad.gnome.org/s/OAL239g-o#Grid-Level-algorithm"&gt;AC-3 approach&lt;/a&gt; in my design doc.&lt;/p&gt;</description><author>Victor Ma</author><dc:creator>Victor Ma</dc:creator><pubDate>Tue, 15 Jul 2025 00:00:00 GMT</pubDate><guid isPermaLink="true">https://victorma.ca/posts/gsoc-5/</guid></item><item><title>Toluwaleke Ogundipe: Profiling Crosswords’ Rendering Pipeline</title><link>https://blogs.gnome.org/anonymoux47/2025/07/14/profiling-crosswords-rendering-pipeline/</link><description>&lt;p&gt;For the sake of formality, if you&amp;#8217;re yet to read the [brief] introduction of my GSoC project, &lt;a href="https://blogs.gnome.org/anonymoux47/2025/06/14/hello-gnome-and-gsoc/"&gt;here&lt;/a&gt; you go.&lt;/p&gt;
  4847.  
  4848.  
  4849.  
  4850. &lt;h2 class="wp-block-heading" id="rendering-puzzles-in-gnome-crosswords"&gt;Rendering Puzzles in GNOME Crosswords&lt;/h2&gt;
  4851.  
  4852.  
  4853.  
  4854. &lt;p&gt;&lt;a class="external" href="https://gitlab.gnome.org/jrb/crosswords"&gt;GNOME Crosswords&lt;/a&gt; currently renders puzzles in two layers. The first is a grid of what we call the &lt;strong&gt;layout items:&lt;/strong&gt; the grid cells, borders between cells and around the grid, and the intersections between borders. This is as illustrated below:&lt;/p&gt;
  4855.  
  4856.  
  4857. &lt;div class="wp-block-image is-style-default"&gt;
  4858. &lt;figure class="aligncenter size-large"&gt;&lt;a href="https://blogs.gnome.org/anonymoux47/files/2025/07/layout-items.png"&gt;&lt;img alt="" class="wp-image-102" height="1024" src="https://blogs.gnome.org/anonymoux47/files/2025/07/layout-items-930x1024.png" width="930" /&gt;&lt;/a&gt;&lt;figcaption class="wp-element-caption"&gt;Layout items&lt;/figcaption&gt;&lt;/figure&gt;&lt;/div&gt;
  4859.  
  4860.  
  4861. &lt;p&gt;The game and editor implement this item grid using a set of custom Gtk widgets: &lt;code&gt;PlayCell&lt;/code&gt; for the cells and &lt;code&gt;PlayBorder&lt;/code&gt; for the borders and intersections, all contained and aligned in a grid layout within a &lt;code&gt;PlayGrid&lt;/code&gt; widget. For instance, the &lt;a class="external" href="https://gitlab.gnome.org/jrb/crosswords/-/blob/9cf9bab35c79e7f42f4eca4f7609414df0ffa946/puzzle-sets/devel-puzzles/simple.ipuz"&gt;simple.ipuz&lt;/a&gt; puzzle, with just its item grid, looks like this:&lt;/p&gt;
  4862.  
  4863.  
  4864. &lt;div class="wp-block-image"&gt;
  4865. &lt;figure class="aligncenter size-full"&gt;&lt;a href="https://blogs.gnome.org/anonymoux47/files/2025/07/layout-items-only.png"&gt;&lt;img alt="" class="wp-image-86" height="512" src="https://blogs.gnome.org/anonymoux47/files/2025/07/layout-items-only.png" width="512" /&gt;&lt;/a&gt;&lt;figcaption class="wp-element-caption"&gt;Rendered puzzle with layout item grid only&lt;/figcaption&gt;&lt;/figure&gt;&lt;/div&gt;
  4866.  
  4867.  
  4868. &lt;p&gt;Then it renders another layer, of what we call the &lt;a class="external" href="https://jrb.pages.gitlab.gnome.org/crosswords/devel-docs/overlays.html"&gt;&lt;strong&gt;layout overlays&lt;/strong&gt;&lt;/a&gt;&lt;strong&gt;,&lt;/strong&gt; above the grid. Overlays are elements of a puzzle which do not exactly fit into the grid, such as barred borders, enumerations (for word breaks, hyphens, etc), cell dividers and arrows (for arrowword puzzles), amongst others. The fact that overlays do not fit into the grid layout makes it practically impossible to render them using widgets. Hence, the need for another layer, and the term β€œoverlay”.&lt;/p&gt;
  4869.  
  4870.  
  4871.  
  4872. &lt;p&gt;Overlays are currently implemented in the game and editor by generating an SVG and rendering it onto a &lt;code&gt;GtkSnapshot&lt;/code&gt; of the &lt;code&gt;PlayGrid&lt;/code&gt; using librsvg. The overlays for the same puzzle, the item grid of which is shown above, look like this:&lt;/p&gt;
  4873.  
  4874.  
  4875. &lt;div class="wp-block-image"&gt;
  4876. &lt;figure class="aligncenter size-full"&gt;&lt;a href="https://blogs.gnome.org/anonymoux47/files/2025/07/layout-overlays-only.png"&gt;&lt;img alt="" class="wp-image-87" height="512" src="https://blogs.gnome.org/anonymoux47/files/2025/07/layout-overlays-only.png" width="512" /&gt;&lt;/a&gt;&lt;figcaption class="wp-element-caption"&gt;Rendered layout overlays&lt;/figcaption&gt;&lt;/figure&gt;&lt;/div&gt;
  4877.  
  4878.  
  4879. &lt;p&gt;When laid over the item grid, they together look like this:&lt;/p&gt;
  4880.  
  4881.  
  4882. &lt;div class="wp-block-image"&gt;
  4883. &lt;figure class="aligncenter size-full"&gt;&lt;a href="https://blogs.gnome.org/anonymoux47/files/2025/07/layout-itemsoverlays.png"&gt;&lt;img alt="" class="wp-image-88" height="512" src="https://blogs.gnome.org/anonymoux47/files/2025/07/layout-itemsoverlays.png" width="512" /&gt;&lt;/a&gt;&lt;figcaption class="wp-element-caption"&gt;Rendered puzzle with layout item grid and overlays&lt;/figcaption&gt;&lt;/figure&gt;&lt;/div&gt;
  4884.  
  4885.  
  4886. &lt;p&gt;All these &lt;strong&gt;elements&lt;/strong&gt; (items and overlays) and their various properties and styles are stored in a &lt;a class="external" href="https://jrb.pages.gitlab.gnome.org/crosswords/devel-docs/grid-layout.html"&gt;&lt;code&gt;GridLayout&lt;/code&gt;&lt;/a&gt; instance, which encapsulates the appearance of a puzzle in a given state. Instances of this class can then be rendered into widgets, SVG, or any other kind of output.&lt;/p&gt;
  4887.  
  4888.  
  4889.  
  4890. &lt;p&gt;The project’s main source includes &lt;a class="external" href="https://gitlab.gnome.org/jrb/crosswords/-/blob/9cf9bab35c79e7f42f4eca4f7609414df0ffa946/src/svg.c"&gt;&lt;em&gt;svg.c&lt;/em&gt;&lt;/a&gt;, a source file containing code to generate an SVG string from a &lt;code&gt;GridLayout&lt;/code&gt;. It provides a function to render overlays only, another for the entire layout, and a function to create an &lt;code&gt;RsvgHandle&lt;/code&gt; from the generated SVG string.&lt;/p&gt;
  4891.  
  4892.  
  4893.  
  4894. &lt;p&gt;Crosswords, the game, uses the SVG code to display thumbnails of puzzles (though currently only for the &lt;strong&gt;Cats and Dogs&lt;/strong&gt; puzzle set), and Crossword Editor displays thumbnails of puzzle templates in its greeter for users to create new puzzles from.&lt;/p&gt;
  4895.  
  4896.  
  4897. &lt;div class="wp-block-image"&gt;
  4898. &lt;figure class="aligncenter size-large"&gt;&lt;a href="https://blogs.gnome.org/anonymoux47/files/2025/07/crosswords_puzzle_picker_grid.png"&gt;&lt;img alt="" class="wp-image-89" height="910" src="https://blogs.gnome.org/anonymoux47/files/2025/07/crosswords_puzzle_picker_grid-1024x910.png" width="1024" /&gt;&lt;/a&gt;&lt;figcaption class="wp-element-caption"&gt;Crosswords&amp;#8217; puzzle picker grid&lt;/figcaption&gt;&lt;/figure&gt;&lt;/div&gt;
  4899.  
  4900. &lt;div class="wp-block-image"&gt;
  4901. &lt;figure class="aligncenter size-large"&gt;&lt;a href="https://blogs.gnome.org/anonymoux47/files/2025/07/crossword_editor_greeter.png"&gt;&lt;img alt="" class="wp-image-90" height="913" src="https://blogs.gnome.org/anonymoux47/files/2025/07/crossword_editor_greeter-1024x913.png" width="1024" /&gt;&lt;/a&gt;&lt;figcaption class="wp-element-caption"&gt;Crossword Editor’s greeter&lt;/figcaption&gt;&lt;/figure&gt;&lt;/div&gt;
  4902.  
  4903.  
  4904. &lt;p&gt;Other than the game and editor, puzzles are also rendered by the &lt;code&gt;crosswords-thumbnailer&lt;/code&gt; utility. This renders an entire puzzle layout by generating an SVG string containing both the layout items and overlays, and rendering/writing it to a PNG or SVG file. There is also my GSoC project, which ultimately aims to add support for printing puzzles in the game and editor.&lt;/p&gt;
  4905.  
  4906.  
  4907.  
  4908. &lt;h2 class="wp-block-heading" id="the-problem"&gt;The Problem&lt;/h2&gt;
  4909.  
  4910.  
  4911.  
  4912. &lt;p&gt;Whenever a sizeable, yet practical number of puzzles are to be displayed at the same time, or in quick succession, there is a noticeable lag in the user interface resulting from the rendering of thumbnails. In other words, &lt;strong&gt;thumbnails take too long to render!&lt;/strong&gt;&lt;/p&gt;
  4913.  
  4914.  
  4915.  
  4916. &lt;p&gt;There are also &lt;a class="external" href="https://gitlab.gnome.org/jrb/crosswords/-/merge_requests/223"&gt;ongoing efforts&lt;/a&gt; to add thumbnails to the list view in the game, for every puzzle, but the current rendering facility just can’t cut it. As for printing, this doesn’t really affect it since it’s not particularly a performance-critical operation.&lt;/p&gt;
  4917.  
  4918.  
  4919.  
  4920. &lt;h2 class="wp-block-heading" id="the-task"&gt;The Task&lt;/h2&gt;
  4921.  
  4922.  
  4923.  
  4924. &lt;p&gt;My task was to profile the puzzle rendering pipeline to determine what the bottleneck(s) was/were. We would later use this information to determine the way forward, whether it be optimising the slow stages of the pipeline, replacing them or eliminating them altogether.&lt;/p&gt;
  4925.  
  4926.  
  4927.  
  4928. &lt;h2 class="wp-block-heading" id="the-rendering-pipeline"&gt;The Rendering Pipeline&lt;/h2&gt;
  4929.  
  4930.  
  4931.  
  4932. &lt;p&gt;The following is an outline of the main/top-level stages involved in rendering a puzzle from an IPUZ file to an image (say, an in-memory image buffer):&lt;/p&gt;
  4933.  
  4934.  
  4935.  
  4936. &lt;ol class="wp-block-list"&gt;
  4937. &lt;li&gt;&lt;strong&gt;ipuz_puzzle_from_file():&lt;/strong&gt; parses a file in the IPUZ format and returns an &lt;code&gt;IpuzPuzzle&lt;/code&gt; object representing the puzzle.&lt;/li&gt;
  4938.  
  4939.  
  4940.  
  4941. &lt;li&gt;&lt;strong&gt;grid_state_new():&lt;/strong&gt; creates a &lt;code&gt;GridState&lt;/code&gt; instance which represents the state of a puzzle grid at a particular instant, and contains a reference to the &lt;code&gt;IpuzPuzzle&lt;/code&gt; object. This class is at the core of Crosswords and is pretty quick to instantiate.&lt;/li&gt;
  4942.  
  4943.  
  4944.  
  4945. &lt;li&gt;&lt;strong&gt;grid_layout_new():&lt;/strong&gt; creates a &lt;code&gt;GridLayout&lt;/code&gt; instance (as earlier described) from the &lt;code&gt;GridState&lt;/code&gt;.&lt;/li&gt;
  4946.  
  4947.  
  4948.  
  4949. &lt;li&gt;&lt;strong&gt;svg_from_layout():&lt;/strong&gt; generates an &lt;strong&gt;SVG string&lt;/strong&gt; from the &lt;code&gt;GridLayout&lt;/code&gt;.&lt;/li&gt;
  4950.  
  4951.  
  4952.  
  4953. &lt;li&gt;&lt;strong&gt;svg_handle_from_string():&lt;/strong&gt; creates an &lt;code&gt;RsvgHandle&lt;/code&gt; (from &lt;a class="external" href="https://gitlab.gnome.org/GNOME/librsvg"&gt;librsvg&lt;/a&gt;) from the generated &lt;strong&gt;SVG string&lt;/strong&gt; and sets a stylesheet on the handle, defining the colours of layout items and overlays.&lt;/li&gt;
  4954.  
  4955.  
  4956.  
  4957. &lt;li&gt;&lt;strong&gt;rsvg_handle_render_document():&lt;/strong&gt; renders the generated SVG onto a &lt;a class="external" href="https://www.cairographics.org/"&gt;Cairo&lt;/a&gt; surface (say, an image surface, which is essentially an image buffer), via a Cairo context.&lt;/li&gt;
  4958. &lt;/ol&gt;
  4959.  
  4960.  
  4961.  
  4962. &lt;h2 class="wp-block-heading" id="profiling"&gt;Profiling&lt;/h2&gt;
  4963.  
  4964.  
  4965.  
  4966. &lt;p&gt;To profile the rendering pipeline specifically, I wrote up a little program to fit my purposes, which can be found &lt;a class="external" href="https://gitlab.gnome.org/-/snippets/7089"&gt;here&lt;/a&gt; (&lt;em&gt;puzzle-render-profiler.c&lt;/em&gt;).&lt;/p&gt;
  4967.  
  4968.  
  4969.  
  4970. &lt;h3 class="wp-block-heading" id="the-attempt-with-sysprof"&gt;The Attempt with Sysprof&lt;/h3&gt;
  4971.  
  4972.  
  4973.  
  4974. &lt;p&gt;Initially, I used &lt;a class="external" href="https://wiki.gnome.org/Apps/Sysprof"&gt;Sysprof&lt;/a&gt;, executing the profiler program under it. Unfortunately, because Sysprof is a sampling profiler and probably also due to its system-wide nature, the results weren’t satisfactory. Also, the functions of interest aren’t long-running functions and each run only once per execution. So the results weren’t accurate enough and somewhat incomplete (missed many nested calls).&lt;/p&gt;
  4975.  
  4976.  
  4977.  
  4978. &lt;p&gt;Don’t get me wrong, Sysprof has its strengths and stands strong amongst profilers of its kind. I tried a couple of others, and Sysprof is the only one even worthy of mention here. Most importantly, I’m glad I got to use and explore Sysprof. The next time I use it won’t be my first!&lt;/p&gt;
  4979.  
  4980.  
  4981.  
  4982. &lt;h3 class="wp-block-heading" id="using-callgrind"&gt;Using Callgrind&lt;/h3&gt;
  4983.  
  4984.  
  4985.  
  4986. &lt;p&gt;&lt;a class="external" href="https://valgrind.org/docs/manual/cl-manual.html"&gt;Callgrind&lt;/a&gt; + QCachegrind is sincerely such a godsend!&lt;/p&gt;
  4987.  
  4988.  
  4989.  
  4990. &lt;blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow"&gt;
  4991. &lt;p&gt;Callgrind is a profiling tool that records the call history among functions in a program&amp;#8217;s run as a call-graph. By default, the collected data consists of the number of instructions executed, their relationship to source lines, the caller/callee relationship between functions, and the numbers of such calls.&lt;/p&gt;
  4992. &lt;/blockquote&gt;
  4993.  
  4994.  
  4995.  
  4996. &lt;blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow"&gt;
  4997. &lt;p&gt;QCachegrind is a GUI to visualise profiling data. It&amp;#8217;s mainly used as a visualisation frontend for data measured by Cachegrind/Callgrind tools from the Valgrind package.&lt;/p&gt;
  4998. &lt;/blockquote&gt;
  4999.  
  5000.  
  5001.  
  5002. &lt;p&gt;After a short while of reading through Callgrind’s manual, I pieced together the combination of options (not much at all) that I needed for the task, and the rest is a story of exploration, learning and excitement. With the following command line, I was all set.&lt;/p&gt;
  5003.  
  5004.  
  5005.  
  5006. &lt;pre class="wp-block-code"&gt;&lt;code&gt;valgrind --tool=callgrind --toggle-collect=render_puzzle ./profiler puzzles/puzzle-sets/cats-and-dogs/doghouse.ipuz&lt;/code&gt;&lt;/pre&gt;
  5007.  
  5008.  
  5009.  
  5010. &lt;p&gt;where:&lt;/p&gt;
  5011.  
  5012.  
  5013.  
  5014. &lt;ul class="wp-block-list"&gt;
  5015. &lt;li&gt;&lt;code&gt;valgrind&lt;/code&gt; is the &lt;a class="external" href="https://valgrind.org"&gt;Valgrind&lt;/a&gt;.&lt;/li&gt;
  5016.  
  5017.  
  5018.  
  5019. &lt;li&gt;&lt;code&gt;--tool=callgrind&lt;/code&gt; selects the Callgrind tool to be run.&lt;/li&gt;
  5020.  
  5021.  
  5022.  
  5023. &lt;li&gt;&lt;code&gt;--toggle-collect=render_puzzle&lt;/code&gt; sets &lt;code&gt;render_puzzle&lt;/code&gt;, the core function in the profiler program, as the target for Callgrind’s data collection.&lt;/li&gt;
  5024.  
  5025.  
  5026.  
  5027. &lt;li&gt;&lt;code&gt;./profiler&lt;/code&gt; is a symlink to the executable of the profiler program.&lt;/li&gt;
  5028.  
  5029.  
  5030.  
  5031. &lt;li&gt;&lt;code&gt;puzzles/puzzle-sets/cats-and-dogs/doghouse.ipuz&lt;/code&gt; is a considerably large puzzle.&lt;/li&gt;
  5032. &lt;/ul&gt;
  5033.  
  5034.  
  5035.  
  5036. &lt;p&gt;I visualised Callgrind’s output in QCachegrind, which is pretty intuitive to use. My focus is the Call Graph feature, which helps to graphically and interactively analyse the profile data. The graph can also be exported as an image or DOT (a graph description format in the Graphviz language) file. The following is a top-level view of the result of the profile run.&lt;/p&gt;
  5037.  
  5038.  
  5039. &lt;div class="wp-block-image"&gt;
  5040. &lt;figure class="aligncenter size-large"&gt;&lt;a href="https://blogs.gnome.org/anonymoux47/files/2025/07/profile-top_level.png"&gt;&lt;img alt="" class="wp-image-85" height="338" src="https://blogs.gnome.org/anonymoux47/files/2025/07/profile-top_level-1024x338.png" width="1024" /&gt;&lt;/a&gt;&lt;figcaption class="wp-element-caption"&gt;Top-level profile of the rendering pipeline&lt;/figcaption&gt;&lt;/figure&gt;&lt;/div&gt;
  5041.  
  5042.  
  5043. &lt;blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow"&gt;
  5044. &lt;p&gt;Note that Callgrind measures the number of instructions executed, not exactly execution time, but typically, the former translates proportionally to the latter. The percentages shown in the graph are instruction ratios, i.e the ratio of instructions executed within each function (and its callees) to the total number of instructions executed (within the portions of the program where data was collected).&lt;/p&gt;
  5045. &lt;/blockquote&gt;
  5046.  
  5047.  
  5048.  
  5049. &lt;p&gt;This graph shows that loading the generated SVG (&lt;code&gt;svg_handle_from_string&lt;/code&gt;) takes up the highest percentage of time, followed by rendering the SVG (&lt;code&gt;rsvg_handle_render_document&lt;/code&gt;). Note that the SVG is simply being rendered to an image buffer, so no encoding, compression, or IO is taking place. The call with the HEX number, instead of a name, simply calls &lt;code&gt;g_object_unref&lt;/code&gt;&lt;strong&gt;,&lt;/strong&gt; under which dropping the &lt;code&gt;rsvg::document::Document&lt;/code&gt; (owned by the &lt;code&gt;RsvgHandle&lt;/code&gt;) takes the highest percentage. Probing further into &lt;code&gt;svg_handle_from_string&lt;/code&gt; and &lt;code&gt;rsvg_handle_render_document&lt;/code&gt;:&lt;/p&gt;
  5050.  
  5051.  
  5052. &lt;div class="wp-block-image"&gt;
  5053. &lt;figure class="aligncenter size-large"&gt;&lt;a href="https://blogs.gnome.org/anonymoux47/files/2025/07/profile-svg_handle_from_string.png"&gt;&lt;img alt="" class="wp-image-82" height="856" src="https://blogs.gnome.org/anonymoux47/files/2025/07/profile-svg_handle_from_string-1024x856.png" width="1024" /&gt;&lt;/a&gt;&lt;figcaption class="wp-element-caption"&gt;Profile of &lt;code&gt;svg_handle_from_string&lt;/code&gt;&lt;/figcaption&gt;&lt;/figure&gt;&lt;/div&gt;
  5054.  
  5055. &lt;div class="wp-block-image"&gt;
  5056. &lt;figure class="aligncenter size-large"&gt;&lt;a href="https://blogs.gnome.org/anonymoux47/files/2025/07/profile-rsvg_handle_render_document.png"&gt;&lt;img alt="" class="wp-image-83" height="1024" src="https://blogs.gnome.org/anonymoux47/files/2025/07/profile-rsvg_handle_render_document-718x1024.png" width="718" /&gt;&lt;/a&gt;&lt;figcaption class="wp-element-caption"&gt;Profile of &lt;code&gt;rsvg_handle_render_document&lt;/code&gt;&lt;/figcaption&gt;&lt;/figure&gt;&lt;/div&gt;
  5057.  
  5058.  
  5059. &lt;p&gt;The performance of &lt;code&gt;rsvg_handle_render_document&lt;/code&gt; improves very slightly after its first call within a program due to some one-time initialisation that occurs in the first call, but it is almost insignificant.&lt;strong&gt; &lt;/strong&gt;From these results, it can be seen that the root of the problem is beyond the scope of Crosswords, as it is something to fix in librsvg. My mentors and I thought it would be quicker and easier to replace or eliminate these stages, but before making a final decision, we needed to see real timings first. For anyone who cares to see a colourful, humongous call graph, be my guest:&lt;/p&gt;
  5060.  
  5061.  
  5062. &lt;div class="wp-block-image"&gt;
  5063. &lt;figure class="aligncenter size-large"&gt;&lt;a href="https://blogs.gnome.org/anonymoux47/files/2025/07/profile-fuller.png"&gt;&lt;img alt="" class="wp-image-84" height="1024" src="https://blogs.gnome.org/anonymoux47/files/2025/07/profile-fuller-982x1024.png" width="982" /&gt;&lt;/a&gt;&lt;figcaption class="wp-element-caption"&gt;Full (sort of) profile of the rendering pipeline&lt;/figcaption&gt;&lt;/figure&gt;&lt;/div&gt;
  5064.  
  5065.  
  5066. &lt;h3 class="wp-block-heading" id="getting-real-timings"&gt;Getting Real Timings&lt;/h3&gt;
  5067.  
  5068.  
  5069.  
  5070. &lt;p&gt;The next step was to get real timings for puzzles of various kinds and sizes to make sense of Callgrind’s percentages, determine the relationship between puzzle sizes and render times, and know the maximum amount of time we can save by replacing or eliminating the SVG stages of the pipeline.&lt;/p&gt;
  5071.  
  5072.  
  5073.  
  5074. &lt;p&gt;To achieve this, I ran the profiler program over a considerably large collection of puzzles available to the GNOME Crosswords project and piped the output (it&amp;#8217;s in CSV format) to a file, the sorted version of which can be found &lt;a class="external" href="https://gitlab.gnome.org/-/snippets/7089"&gt;here&lt;/a&gt; (&lt;em&gt;puzzle_render_profile-sorted_by_path.csv&lt;/em&gt;). Then, I wrote a Python script (&lt;em&gt;visualize_results.py&lt;/em&gt;), which can also be found at the same URL, to plot a couple of graphs from the results.&lt;/p&gt;
  5075.  
  5076.  
  5077. &lt;div class="wp-block-image"&gt;
  5078. &lt;figure class="aligncenter size-large"&gt;&lt;a href="https://blogs.gnome.org/anonymoux47/files/2025/07/time_vs_size.png"&gt;&lt;img alt="" class="wp-image-81" height="576" src="https://blogs.gnome.org/anonymoux47/files/2025/07/time_vs_size-1024x576.png" width="1024" /&gt;&lt;/a&gt;&lt;figcaption class="wp-element-caption"&gt;Time vs Puzzle size (width * height) &lt;em&gt;(lesser is better)&lt;/em&gt;&lt;/figcaption&gt;&lt;/figure&gt;&lt;/div&gt;
  5079.  
  5080. &lt;div class="wp-block-image"&gt;
  5081. &lt;figure class="aligncenter size-large"&gt;&lt;a href="https://blogs.gnome.org/anonymoux47/files/2025/07/time_vs_n_elements.png"&gt;&lt;img alt="" class="wp-image-80" height="576" src="https://blogs.gnome.org/anonymoux47/files/2025/07/time_vs_n_elements-1024x576.png" width="1024" /&gt;&lt;/a&gt;&lt;figcaption class="wp-element-caption"&gt;Time vs Number of layout elements (items + overlays) &lt;em&gt;(lesser is better)&lt;/em&gt;&lt;/figcaption&gt;&lt;/figure&gt;&lt;/div&gt;
  5082.  
  5083.  
  5084. &lt;p&gt;Both show a very similar profile from which I made the following deductions:&lt;/p&gt;
  5085.  
  5086.  
  5087.  
  5088. &lt;ul class="wp-block-list"&gt;
  5089. &lt;li&gt;Total render times increase almost linearly with puzzle size, and so do the component stages of the rendering pipeline. The exceptions (those steep valleys in the graphs) are puzzles with significant amounts of NULL cells, the corresponding layout items of which are omitted during SVG generation since NULL cells are not to be rendered.&lt;/li&gt;
  5090.  
  5091.  
  5092.  
  5093. &lt;li&gt;The stages of loading the generated SVG and rendering it take most of the time, significantly more than the other stages, just as the Callgrind graphs above show.&lt;/li&gt;
  5094. &lt;/ul&gt;
  5095.  
  5096.  
  5097.  
  5098. &lt;p&gt;Below is a stacked bar chart showing the cumulative render times for all puzzles. Note that these timings are only valid relative to one another; comparison with timings from another machine or even the same machine under different conditions would be invalid.&lt;/p&gt;
  5099.  
  5100.  
  5101. &lt;div class="wp-block-image"&gt;
  5102. &lt;figure class="aligncenter size-large"&gt;&lt;a href="https://blogs.gnome.org/anonymoux47/files/2025/07/cumulative_render_times.png"&gt;&lt;img alt="" class="wp-image-91" height="1024" src="https://blogs.gnome.org/anonymoux47/files/2025/07/cumulative_render_times-354x1024.png" width="354" /&gt;&lt;/a&gt;&lt;figcaption class="wp-element-caption"&gt;Cumulative render times for all puzzles &lt;em&gt;(lesser is better)&lt;/em&gt;&lt;/figcaption&gt;&lt;/figure&gt;&lt;/div&gt;
  5103.  
  5104.  
  5105. &lt;h2 class="wp-block-heading" id="the-way-forward"&gt;The Way Forward&lt;/h2&gt;
  5106.  
  5107.  
  5108.  
  5109. &lt;p&gt;Thankfully, the maintainer of librsvg, &lt;a class="external" href="https://gitlab.gnome.org/federico"&gt;Federico Mena Quintero&lt;/a&gt;, happens to be one of my mentors. He looked into the profiling results and ultimately recommended that we cut out the SVG stages (&lt;code&gt;svg_from_layout&lt;/code&gt;, &lt;code&gt;svg_handle_from_string&lt;/code&gt; and &lt;code&gt;rsvg_handle_render_document&lt;/code&gt;) entirely and render using Cairo directly. For context, librsvg renders SVGs using Cairo. He also pointed out some specific sources of the librsvg bottlenecks and intends to fix them. The initial work in librsvg is in &lt;a class="external" href="https://gitlab.gnome.org/GNOME/librsvg/-/issues/1178"&gt;!1178&lt;/a&gt; and the MR linked from there.&lt;/p&gt;
  5110.  
  5111.  
  5112.  
  5113. &lt;p&gt;This is actually no small feat, but is already in the works (actually more complete than the SVG rendering pipeline is, at the point of writing this) and is showing great promise! I&amp;#8217;ll be writing about this soon, but here&amp;#8217;s a little sneak peek to keep your taste buds wet.&lt;/p&gt;
  5114.  
  5115.  
  5116. &lt;div class="wp-block-image"&gt;
  5117. &lt;figure class="aligncenter size-large"&gt;&lt;a href="https://blogs.gnome.org/anonymoux47/files/2025/07/timings_comparison.png"&gt;&lt;img alt="" class="wp-image-92" height="508" src="https://blogs.gnome.org/anonymoux47/files/2025/07/timings_comparison-1024x508.png" width="1024" /&gt;&lt;/a&gt;&lt;figcaption class="wp-element-caption"&gt;Comparison of the new and old render pipeline timings with puzzles of varying sizes &lt;em&gt;(lesser is better)&lt;/em&gt;&lt;/figcaption&gt;&lt;/figure&gt;&lt;/div&gt;
  5118.  
  5119.  
  5120. &lt;figure class="wp-block-gallery aligncenter has-nested-images columns-default is-cropped wp-block-gallery-1 is-layout-flex wp-block-gallery-is-layout-flex"&gt;
  5121. &lt;figure class="wp-block-image size-full"&gt;&lt;a href="https://blogs.gnome.org/anonymoux47/files/2025/07/doghouse-before.png"&gt;&lt;img alt="" class="wp-image-94" height="512" src="https://blogs.gnome.org/anonymoux47/files/2025/07/doghouse-before.png" width="512" /&gt;&lt;/a&gt;&lt;figcaption class="wp-element-caption"&gt;before&lt;/figcaption&gt;&lt;/figure&gt;
  5122.  
  5123.  
  5124.  
  5125. &lt;figure class="wp-block-image size-full"&gt;&lt;a href="https://blogs.gnome.org/anonymoux47/files/2025/07/doghouse-after.png"&gt;&lt;img alt="" class="wp-image-96" height="512" src="https://blogs.gnome.org/anonymoux47/files/2025/07/doghouse-after.png" width="512" /&gt;&lt;/a&gt;&lt;figcaption class="wp-element-caption"&gt;after&lt;/figcaption&gt;&lt;/figure&gt;
  5126. &lt;figcaption class="blocks-gallery-caption wp-element-caption"&gt;Render of &lt;a class="external" href="https://gitlab.gnome.org/jrb/crosswords/-/blob/192cccb8d048d7403ee2d042fcddcce19fde17f8/puzzle-sets/cats-and-dogs/doghouse.ipuz"&gt;doghouse.ipuz&lt;/a&gt;&lt;/figcaption&gt;&lt;/figure&gt;
  5127.  
  5128.  
  5129.  
  5130. &lt;figure class="wp-block-gallery aligncenter has-nested-images columns-default is-cropped wp-block-gallery-2 is-layout-flex wp-block-gallery-is-layout-flex"&gt;
  5131. &lt;figure class="wp-block-image size-full"&gt;&lt;a href="https://blogs.gnome.org/anonymoux47/files/2025/07/catatonic-before.png"&gt;&lt;img alt="" class="wp-image-93" height="512" src="https://blogs.gnome.org/anonymoux47/files/2025/07/catatonic-before.png" width="512" /&gt;&lt;/a&gt;&lt;figcaption class="wp-element-caption"&gt;before&lt;/figcaption&gt;&lt;/figure&gt;
  5132.  
  5133.  
  5134.  
  5135. &lt;figure class="wp-block-image size-full"&gt;&lt;a href="https://blogs.gnome.org/anonymoux47/files/2025/07/catatonic-after.png"&gt;&lt;img alt="" class="wp-image-95" height="512" src="https://blogs.gnome.org/anonymoux47/files/2025/07/catatonic-after.png" width="512" /&gt;&lt;/a&gt;&lt;figcaption class="wp-element-caption"&gt;after&lt;/figcaption&gt;&lt;/figure&gt;
  5136. &lt;figcaption class="blocks-gallery-caption wp-element-caption"&gt;Render of &lt;a class="external" href="https://gitlab.gnome.org/jrb/crosswords/-/blob/192cccb8d048d7403ee2d042fcddcce19fde17f8/puzzle-sets/cats-and-dogs/catatonic.ipuz"&gt;catatonic.ipuz&lt;/a&gt;&lt;/figcaption&gt;&lt;/figure&gt;
  5137.  
  5138.  
  5139.  
  5140. &lt;p&gt;I&amp;#8217;ll leave you to figure it out in the meantime.&lt;/p&gt;
  5141.  
  5142.  
  5143.  
  5144. &lt;h2 class="wp-block-heading" id="conclusion"&gt;Conclusion&lt;/h2&gt;
  5145.  
  5146.  
  5147.  
  5148. &lt;p&gt;Every aspect of the task was such a great learning experience. This happened to be my first time profiling C code and using tools like Valgrind and Sysprof; the majority of C code I had written in the past was for bare-metal embedded systems. Now, I’ve got these under my belt, and nothing is taking them away.&lt;/p&gt;
  5149.  
  5150.  
  5151.  
  5152. &lt;p&gt;That said, I will greatly appreciate your comments, tips, questions, and what have you; be it about profiling, or anything else discussed herein, or even blog writing (this is only my second time ever, so I need all the help I can get).&lt;/p&gt;
  5153.  
  5154.  
  5155.  
  5156. &lt;p&gt;Finally, but by far not the least important, a wise man once told me:&lt;/p&gt;
  5157.  
  5158.  
  5159.  
  5160. &lt;blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow"&gt;
  5161. &lt;p&gt;The good thing about profiling is that it can overturn your expectations :-)&lt;/p&gt;
  5162.  
  5163.  
  5164.  
  5165. &lt;p&gt;&amp;#8212; &lt;a class="external" href="http://hpjansson.org"&gt;H.P. Jansson&lt;/a&gt;&lt;/p&gt;
  5166. &lt;/blockquote&gt;
  5167.  
  5168.  
  5169.  
  5170. &lt;h2 class="wp-block-heading" id="thanks"&gt;Thanks&lt;/h2&gt;
  5171.  
  5172.  
  5173.  
  5174. &lt;p&gt;Very big thank yous to my mentors, &lt;a class="external" href="https://gitlab.gnome.org/jrb"&gt;Jonathan Blandford&lt;/a&gt; and &lt;a class="external" href="https://gitlab.gnome.org/federico"&gt;Federico Mena Quintero&lt;/a&gt;, for their guidance all through the accomplishment of this task and my internship in general. I’ve learnt a great deal from them so far.&lt;/p&gt;
  5175.  
  5176.  
  5177.  
  5178. &lt;p&gt;Also, thank you (yes, you reading) so much for your time. Till next time… Anticipate!!!&lt;/p&gt;
  5179.  
  5180.  
  5181.  
  5182. &lt;p&gt;&lt;/p&gt;</description><author>Toluwaleke Ogundipe</author><dc:creator>Toluwaleke Ogundipe</dc:creator><pubDate>Mon, 14 Jul 2025 19:10:10 GMT</pubDate><guid isPermaLink="true">https://blogs.gnome.org/anonymoux47/2025/07/14/profiling-crosswords-rendering-pipeline/</guid></item><item><title>Gedit Technology blog: Mid-July News</title><link>https://gedit-text-editor.org/blog/2025-07-14-mid-july-news.html</link><description>&lt;p&gt;
  5183.  Misc news about the
  5184.  &lt;a href="https://gedit-text-editor.org/"&gt;gedit&lt;/a&gt;
  5185.  text editor, mid-July edition! (Some sections are a bit technical).
  5186. &lt;/p&gt;
  5187.  
  5188. &lt;h3&gt;gedit 49 in preparation&lt;/h3&gt;
  5189. &lt;p&gt;
  5190.  About version numbers: if all goes well, gedit 49 will match GNOME 49 and are
  5191.  expected for September! So gedit will be back on track to follow the GNOME
  5192.  version numbers. This is after some trials to &lt;em&gt;not&lt;/em&gt; follow them, to
  5193.  release gedit versions when ready and more often, but at the end of the day
  5194.  this doesn't change much.
  5195. &lt;/p&gt;
  5196.  
  5197. &lt;h3&gt;Gedit Development Guidelines&lt;/h3&gt;
  5198. &lt;p&gt;
  5199.  I've written the
  5200.  &lt;a href="https://gitlab.gnome.org/World/gedit/gedit/-/tree/master/docs/guidelines"&gt;Gedit Development Guidelines&lt;/a&gt;,
  5201.  a series of small Markdown documents. It regroups the information in one
  5202.  place. It also documents stuff that needed to be written in commit messages,
  5203.  so to avoid repetition and to have more complete explanations, commit messages
  5204.  can now refer to the guideline documents.
  5205. &lt;/p&gt;
  5206. &lt;p&gt;
  5207.  Be warned that some of the gedit guidelines don't follow the more modern GNOME
  5208.  development practices. The rationales are clearly explained. In my humble
  5209.  opinion, what is deemed "modern" doesn't automatically make something better.
  5210.  (If you read this and &lt;em&gt;are&lt;/em&gt; a developer of one of the modern things not
  5211.  used by gedit, take it as a user feedback, and remember that what truly
  5212.  matters is that the GTK development platform is still appreciated!).
  5213. &lt;/p&gt;
  5214.  
  5215. &lt;h3&gt;Improved preferences&lt;/h3&gt;
  5216. &lt;p&gt;
  5217.  &lt;a href="https://gedit-text-editor.org/blog/images/2025-07-14/prefs-reset.png" target="_blank"&gt;&lt;img alt="gedit screenshot - reset all preferences" src="https://gedit-text-editor.org/blog/images/2025-07-14/prefs-reset.png" width="250" /&gt;&lt;/a&gt;
  5218. &lt;/p&gt;
  5219. &lt;p&gt;
  5220.  &lt;a href="https://gedit-text-editor.org/blog/images/2025-07-14/prefs-spell-checker.png" target="_blank"&gt;&lt;img alt="gedit screenshot - spell-checker preferences" src="https://gedit-text-editor.org/blog/images/2025-07-14/prefs-spell-checker.png" width="250" /&gt;&lt;/a&gt;
  5221. &lt;/p&gt;
  5222. &lt;p&gt;
  5223.  There is now a "Reset All..." button in the Preferences dialog. And it is now
  5224.  possible to configure the default language used by the spell-checker.
  5225. &lt;/p&gt;
  5226.  
  5227. &lt;h3&gt;Python plugins&lt;/h3&gt;
  5228. &lt;p&gt;
  5229.  Most Linux distributions ship some older versions of some GNOME modules to
  5230.  make Python plugins in gedit (and other apps) still work. There has been some
  5231.  disturbances in the air, but my hope is that everything will be back to normal
  5232.  for GNOME 49.
  5233. &lt;/p&gt;
  5234.  
  5235. &lt;h3&gt;File loading and saving&lt;/h3&gt;
  5236. &lt;p&gt;
  5237.  There are some known issues with the file loading implementation, and I'm
  5238.  working on it.
  5239. &lt;/p&gt;
  5240. &lt;p&gt;
  5241.  About the old &lt;code&gt;iconv()&lt;/code&gt; function, I've created a utility class called
  5242.  &lt;a href="https://gitlab.gnome.org/World/gedit/libgedit-gtksourceview/-/blob/main/gtksourceview/file-loading-and-saving/gtksourceiconv.c" target="_blank"&gt;GtkSourceIconv&lt;/a&gt;
  5243.  to (try to) tame the beast, except that the beast &lt;em&gt;cannot be fully
  5244.  tamed&lt;/em&gt;, by design! Some aspects of its API are broken, and what I
  5245.  recommend for new code is to look at the libICU to see if it's better.
  5246.  (gtksourceiconv.c has a comment at the top with more details).
  5247. &lt;/p&gt;
  5248. &lt;p&gt;
  5249.  But instead of doing ground-breaking changes in the file loading and saving
  5250.  code (to switch to the libICU), I prefer a more incremental approach, and this
  5251.  means to still use &lt;code&gt;iconv()&lt;/code&gt; (for the time being at least). The
  5252.  advantage is that incremental changes are directly useful and used; progress
  5253.  is made on gedit!
  5254. &lt;/p&gt;
  5255. &lt;p&gt;
  5256.  I attempted a while back a new implementation in libgedit-tepl (TeplEncoding,
  5257.  TeplFile, etc). The problem is that I created a different API, so switching
  5258.  the whole of gedit to it was more difficult. And the new implementation lacked
  5259.  several important features used by gedit.
  5260. &lt;/p&gt;
  5261. &lt;p&gt;
  5262.  Now I'm moving some of my experiments done in libgedit-tepl back to
  5263.  libgedit-gtksourceview, because it's the latter (GtkSourceEncoding,
  5264.  GtkSourceFile, etc) that is used by gedit's file loading and saving machinery.
  5265.  libgedit-gtksourceview doesn't guarantee a stable API, and I consider it part
  5266.  of gedit, so it's more &lt;em&gt;malleable code&lt;/em&gt; :-)
  5267. &lt;/p&gt;</description><author>Gedit Technology blog</author><dc:creator>Gedit Technology blog</dc:creator><pubDate>Mon, 14 Jul 2025 10:00:00 GMT</pubDate><guid isPermaLink="true">https://gedit-text-editor.org/blog/2025-07-14-mid-july-news.html</guid></item><item><title>Philip Withnall: GUADEC handbook</title><link>https://tecnocode.co.uk/2025/07/12/guadec-handbook/</link><description>&lt;p&gt;I was reminded today that I &lt;a href="https://gitlab.gnome.org/pwithnall/guadec-handbook"&gt;put together some notes last year&lt;/a&gt; with people’s feedback about what worked well at the last GUADEC. The idea was that this could be built on, and eventually become another part of the GNOME handbook, so that we have a good checklist to organise events from each year.&lt;/p&gt;
  5268.  
  5269.  
  5270.  
  5271. &lt;p&gt;I’m not organising GUADEC, so this is about as far as I can push this proto-handbook, but if anyone wants to fork it and build on it then please feel free.&lt;/p&gt;
  5272.  
  5273.  
  5274.  
  5275. &lt;p&gt;Most of the notes so far relate to A/V things, remote participation, and some climate considerations. Obviously a lot more about on-the-ground organisation would have to be added to make it a full handbook, but it’s a start.&lt;/p&gt;</description><author>Philip Withnall</author><dc:creator>Philip Withnall</dc:creator><pubDate>Sat, 12 Jul 2025 16:25:38 GMT</pubDate><guid isPermaLink="true">https://tecnocode.co.uk/2025/07/12/guadec-handbook/</guid></item><item><title>Ignacy KuchciΕ„ski: Digital Wellbeing Contract</title><link>https://blogs.gnome.org/ignapk/2025/07/11/digital-wellbeing-contract/</link><description>&lt;p&gt;This month I have been accepted as a contractor to work on the Parental Controls frontent and integration as part of the Digital Wellbeing project. I&amp;#8217;m very happy to take part in this cool endeavour, and very grateful to the GNOME Foundation for giving me this opportunity &amp;#8211; special thanks to Steven Deobald and Allan Day for interviewing me and helping me connect with the team, despite our timezone compatibility &lt;img alt="πŸ˜‰" class="wp-smiley" src="https://s.w.org/images/core/emoji/16.0.1/72x72/1f609.png" style="height: 1em;" /&gt;&lt;/p&gt;
  5276. &lt;p&gt;The idea is to redesign the Parental Controls app UI to bring it on par with modern GNOME apps, and integrate the parental controls in the GNOME Shell lock screen with collaboration with gnome-control-center. There also new features to be added, such as Screen Time monitoring and setting limits, Bedtime Schedule and Web Filtering support. The project has been going for quite some time, and there has been a lot of great work put into both designs by Sam Hewitt and the backend by Philip Withnall, who&amp;#8217;s been really helpful teaching me about the project code practices and reviewing my MR. See the designs in the &lt;a class="external" href="https://gitlab.gnome.org/Teams/Design/app-mockups/-/issues/118#note_2449797"&gt;app-mockups ticket&lt;/a&gt; and the &lt;a class="external" href="https://gitlab.gnome.org/Teams/Design/os-mockups/-/issues/271#note_2445908"&gt;os-mockups ticket&lt;/a&gt;.&lt;/p&gt;
  5277. &lt;p&gt;We started implementing the design mockup MVP for Parental Controls, which you can find in the app-mockup ticket. We&amp;#8217;re trying to meet the GNOME 49 release deadlines, but as always it&amp;#8217;s a goal rather than certain milestone. So far we have finished the redesign of the current Parent Controls app without adding any new features, which is to refresh the UI for unlock page, rework the user selector to be a list rather than a carousel, and changed navigation to use pages. This will be followed by adding pages for Screen Time and Web Filtering.&lt;/p&gt;
  5278. &lt;figure class="wp-caption aligncenter" id="attachment_82" style="width: 590px;"&gt;&lt;img alt="" class="wp-image-82 size-full" height="630" src="https://blogs.gnome.org/ignapk/files/2025/07/Screenshot-From-2025-07-11-16-11-56.png" width="590" /&gt;&lt;figcaption class="wp-caption-text" id="caption-attachment-82"&gt;Refreshed unlock page&lt;/figcaption&gt;&lt;/figure&gt;
  5279. &lt;figure class="wp-caption aligncenter" id="attachment_83" style="width: 590px;"&gt;&lt;img alt="" class="size-full wp-image-83" height="630" src="https://blogs.gnome.org/ignapk/files/2025/07/Screenshot-From-2025-07-11-16-12-23.png" width="590" /&gt;&lt;figcaption class="wp-caption-text" id="caption-attachment-83"&gt;Reworked user selector&lt;/figcaption&gt;&lt;/figure&gt;
  5280. &lt;figure class="wp-caption aligncenter" id="attachment_84" style="width: 590px;"&gt;&lt;img alt="" class="size-full wp-image-84" height="630" src="https://blogs.gnome.org/ignapk/files/2025/07/Screenshot-From-2025-07-11-16-13-13.png" width="590" /&gt;&lt;figcaption class="wp-caption-text" id="caption-attachment-84"&gt;Navigation using pages, Screen Time and Web Filtering to be added&lt;/figcaption&gt;&lt;/figure&gt;
  5281. &lt;p&gt;I want to thank the team for helping me get on board and being generally just awesome to work with &lt;img alt="πŸ˜€" class="wp-smiley" src="https://s.w.org/images/core/emoji/16.0.1/72x72/1f600.png" style="height: 1em;" /&gt; Until next update!&lt;/p&gt;</description><author>Ignacy KuchciΕ„ski</author><dc:creator>Ignacy KuchciΕ„ski</dc:creator><pubDate>Fri, 11 Jul 2025 16:53:06 GMT</pubDate><guid isPermaLink="true">https://blogs.gnome.org/ignapk/2025/07/11/digital-wellbeing-contract/</guid></item></channel></rss>
Copyright © 2002-9 Sam Ruby, Mark Pilgrim, Joseph Walton, and Phil Ringnalda