Search Results: "angel"

11 July 2026

Reproducible Builds: Reproducible Builds in June 2026

Welcome to the June 2026 report from the Reproducible Builds project! In these reports, we outline the most important things that we have been up to over the past month. As a quick recap about what problem our project intends to solve, whilst anyone may inspect the source code of free software for malicious flaws, almost all software is distributed to end users as pre-compiled binaries. The motivation behind the reproducible builds effort is to ensure no flaws have been introduced during this compilation process by promising identical results are always generated from a given source, thus allowing multiple third-parties to come to a consensus on whether a build was compromised or not. If you are interested in contributing to the project, please visit our Contribute page on our website. In this month s report, we cover:

  1. Only installing reproducible packages with repro-threshold
  2. Distribution work
  3. diffoscope development
  4. From our mailing list
  5. Documentation updates
  6. Patches
  7. Four new scholarly papers


Only installing reproducible packages with repro-threshold A very interesting demonstration is now available showing how you might configure your Debian system to only install packages that have been reproduced by m/n rebuilders. This is implemented via a reproduced+https:// APT transport ( a mechanism for communicating between the APT client and its repository source commonly HTTP):
Every package download is intercepted by repro-threshold, which queries two independent rebuilders for a signed attestation before allowing installation to proceed. [It] is important to note that [an] install will only succeed if all package dependencies are also reproducible.
The demo gives examples of how to quickly experiment with this using a Docker container.

Distribution work In Debian this month: The IzzyOnDroid Android APK repository reached its next milestone this month, now covering 2 out of every 3 apps (66.7%) with reproducible builds. Their documentation for debugging and fixing failed builds has steadily grown as well. More clients have picked up showing reproducibility results (e.g. Droid-ify), and Neo Store now can be configured to stick to only reproducible applications. Further, an independent builder has been added to the build farm, increasing the trust level even more as APK builds can have multiple confirmations now. At the same time, IzzyOnDroid s rbtlog got several new features. The most outstanding is caching for frequently used resources such as reproducible-apk-tools, command-line tools and NodeJS in order to counter ongoing issues with GitHub availability, while at the same time saving bandwidth and build time. This change also enables some other some smaller enhancements such as being able to configure build timeouts per recipe for those builds running longer than the average, release pattern filtering for update checks or having a field for maintainer notes to shortly summing up e.g. why a reproducible build failed.
Lastly, Bernhard M. Wiedemann posted another openSUSE monthly update for their reproducibility work there.

diffoscope development diffoscope is our in-depth and content-aware diff utility that can locate and diagnose reproducibility issues. This month, Chris Lamb made the following changes, including preparing and uploading versions 319, 320, 321, 322 and 323 to Debian:
  • Debian adds an extra Flags: line in the output of ocamlobjinfo, so adjust the test for cross-distribution compatibility. [ ]
  • Bump debhelper compatibility level to 14. [ ]
  • Fix compatibility with Ocaml 5.4.1. [ ]
  • Use --long-form-style arguments when calling apktool in order to support apktool version 3. [ ]
  • Support Androguard version 4 and previous versions at the same time. [ ]
  • Update copyright years. [ ]
In addition, Jochen Sprickerhof added better header detection for the Sphinx documentation system [ ], Michael Daniels fixed the tests when run with zipdetails version 4.006 [ ] and Zbigniew J drzejewski-Szmek added a version of the deprecated os.path.commonprefix method [ ]. In addition, Vagrant Cascadian updated diffoscope in GNU Guix to version 321 and 323.
Chris Lamb also made the following changes to strip-nondeterminism, our tool to remove specific non-deterministic results from a completed build:
  • Skip symlinks when manually called via /usr/bin/strip-nondeterminism. (#1139000)
  • Update debian/watch format. [ ]
  • Drop Rules-Requires-Root: no and Priority: optional fields. [ ]
  • Bump Standards-Version to version 4.7.4. [ ]

From our mailing list On our mailing list this month:
  • kpcyrd posted to our mailing list regarding the waves of malware uploads to aur.archlinux.org . Curiously, every incident I looked at used npmjs.com for malware delivery , specifically where the npm package includes an (automatically executed) preinstall script that is an ELF binary.
  • kpcyrd also announced the release of debian-repro-status version 0.4.0, a tool written to give you an approximate idea of how viable it would be to enforce a reproducible packages only update policy for the computer system you ve built :
    The change updates dependencies to the latest versions, and adds support for multiple -H options, to query results from multiple rebuilderd instances. The results are also now fetched concurrently.
  • kpcyrd also reported that, whilst taking a screenshot for the above release, they noticed that the debian:sid container now is 100% reproducible.
  • Finally, kpcyrd also created a pull request against the add-determinism package to update the itertools and zip Python dependencies.

Documentation updates Yet again, there were a number of improvements made to our website this month including:
  • Chris Lamb added a reminder re. using the UTC variants of the Javascript Date methods. [ ]
  • Mattia Rizzolo moved OTF to the old sponsors list. Thank you for your support!. [ ]
  • kpcyrd updated the Rust documentation to recommend using the --release argument for consistency. [ ]

Patches The Reproducible Builds project detects, dissects and attempts to fix as many currently-unreproducible packages as possible. We endeavour to send all of our patches upstream where applicable or possible. This month, we wrote a large number of such patches, including:

Four new scholarly papers Kenichiro Muto and Kuniyasu Suzaki of the Institute of Information Security in Yokohama, Japan published an interesting paper this month titled Attestable Build Chain: Enabling Trust in Reproducible Builds (PDF). Their abstract is as follows: Ensuring trust in software supply chains requires verifying not only artifacts but also the processes that produce them. Although Reproducible Builds (R-B) require rebuilding to validate artifacts, they cannot verify whether the build was executed with the intended toolchain and inputs and may reproduce unintended or compromised builds without detection. We present Attestable Build Chain, a framework for externally verifying build-time execution without rebuilding. Rather than preventing compromise, it provides verifiable, tamper-evident evidence of actual build-time execution, enabling verification of build process integrity from observed file accesses during the build. [ ]
Julien Malka, Stefano Zacchiroli and Th o Zimmermann published a 50-page report detailing A Decade of Software Reproducibility in the Nix Package Ecosystem:
We find that functional package management enables extremely high rebuildability over time (near-universal ability to reconstitute historical build environments and rebuild software packages), while bitwise reproducibility has steadily improved and reaches a high point in recent years (up to 93% in 2024). Early years show substantially lower bitwise reproducibility, indicating that functional package management alone does not guarantee bitwise-identical outputs, and that the observed high level of bitwise reproducibility is not solely due to the package management approach. Common causes of unreproducibility, both in the rebuildability and bitwise reproducibility dimensions, include management of dates in build and test processes; we quantify their prevalence and other common causes using manual analysis of logs of rebuild failures and automated analysis of diffoscope.
A PDF of their report is available online
Tim Bastin of L3montree GmbH and Jacek Galowicz of Applicative Systems GmbH from DevGuard published a paper detailing How We Built a Sovereign, Reproducible Container Supply Chain for DevGuard:
This paper presents how the DevGuard project rebuilt its OCI container pipeline around reproducible Nix builds and independent dual-platform digest verification. DevGuard images are built hermetically from pinned source revisions, signed with Sigstore/Cosign, and verified through digest comparison across GitHub Actions and sovereign GitLab infrastructure hosted on container.gov.de. We describe the practical integration of reproducible OCI image builds into existing CI/CD workflows and argue that independently reproducible container digests provide a stronger integrity guarantee against build tampering than provenance alone. The paper further discusses remaining trust assumptions and the relevance of sovereign build infrastructure for government and regulated environments.

Finally, Yiseul Choi, Junga Kim, Jun-Ho Hong and Seongmin Kim of the Department of Convergence Security Engineering at the Sungshin Women s University in Seoul, Korea titled Attestation-based verification of SBOM integrity via consumer-side reproducibility:
Software bills of materials (SBOMs) support supply chain transparency, but they do not prove that a delivered SBOM reproducibly corresponds to its software artifact. Existing signing and provenance mechanisms protect integrity and traceability, yet lack consumer-side reproducible verification. We propose an SBOM integrity verification framework combining procedure disclosure, consumer-side reproduction, authority-generated reference evidence, and digest comparison. A trusted authority records a reference digest, and consumers compare it with locally reproduced and delivered SBOM digests. Experiments on 100 real-world container images show detection of artifact tampering, SBOM substitution, distribution modification, and adaptive tampering beyond signature-based approaches


Finally, if you are interested in contributing to the Reproducible Builds project, please visit our Contribute page on our website. However, you can get in touch with us via:

6 July 2026

Russ Allbery: INN 2.7.4

This is a bug fix and minor feature release over INN 2.7.3, and the upgrade should be painless. You can download the new release from ISC or my personal INN pages. The latter also has links to the full changelog and the other INN documentation. For the full list of changes, see the INN 2.7.4 NEWS file. As always, thanks to Julien LIE for preparing this release and doing most of the maintenance work on INN!

26 June 2026

Jonathan Wiltshire: Streamlining Debian Updates with AI: The Stable Update Adviser

Please consider supporting my work in Debian and elsewhere through Liberapay.


Some months ago I started working on a small experiment: a specialised LLM assistant for Debian maintainers preparing proposed updates for stable and oldstable.

The aim is not to replace review by the Stable Release Managers, nor to automate away maintainer judgement. It is meant to be a pre-advice tool: something maintainers can use while they are still preparing a proposed update, before sending it to other people to review.

If you ve uploaded to stable before you may have some experience with our acceptance criteria. Does the version sort correctly? Is the changelog clear enough? Is the delta focused? Are the patches documented? Are the right bugs or CVEs mentioned? Are they of sufficient severity? What is the risk to users of stable? Is there an ABI change hiding in the diff?

These are the sort of checks where a structured first pass can be useful, provided it is treated as advice rather than authority.

To explain what the tool does, what could be more appropriate in an AI age than to ask it?

Describe yourself and your abilities and limitations for a blog post.

It answers:

I m Debian Stable Update Adviser, a specialised ChatGPT assistant for Debian maintainers preparing proposed updates for Debian stable and oldstable.

My main job is to review a single .debdiff and give practical, policy-aware feedback before a maintainer submits or uploads a proposed update. I focus on whether the update appears suitable for Debian stable or oldstable, especially under the Stable Release Managers expectations for minimal, targeted, low-risk changes.

I can inspect a debdiff for Debian changelog quality, including suite, version, bug references, CVE mentions, and rationale. I can check version correctness using Debian dpkg version semantics. I can review packaging changes under debian/, such as patches, maintscripts, symbols files, conffiles, Build-Depends, autopkgtests, and control metadata. I can also group upstream source changes into behavioural and non-behavioural categories, and flag areas that may carry regression risk.

My reports are intended to be concise and useful to maintainers. A typical review includes a quick summary, behavioural and non-behavioural change breakdown, mechanical policy checks, risk assessment, stable-policy checklist, acceptance likelihood, rationale, maintainer advice, and a clear conclusion.

My limitations are important. I cannot prove that a package builds, that tests pass, that the bug is fully fixed, or that the Stable Release Managers will accept the upload. I also do not replace maintainer judgment, archive review, autopkgtest results, reproducibility checks, or real-world upgrade testing. I can only review the provided debdiff and point out likely issues, risks, and improvements.

I also do not proceed without a valid debdiff. The whole point of my review is to assess the actual proposed delta, not to speculate from package names or bug descriptions alone.

In short, I act as a focused pre-review assistant for Debian stable and oldstable proposed updates: useful for catching policy, versioning, changelog, packaging, and regression-risk issues early, but not a substitute for the Debian review process itself.

That last point is an important one. This is not an oracle, and it is not part of the Debian archive machinery. It is a checklist with some language-model assistance, aimed at reading a debdiff and pointing out things that might be worth another look.

I know that commercial AI and LLM tools are not universally welcome in Debian. That is understandable. Debian depends on transparency, human responsibility, licensing clarity, and technical correctness. LLMs have obvious problems in all of those areas. They can be wrong, and worse, they can be wrong in a fluent and plausible way. They are impossible to reproduce and their training is opaque.

But I see this as a useful first pass for a maintainer who is unused to working in stable, and would benefit from a virtual mentor giving their proposal a quick check and reassurance. Perhaps they don t have a more experienced co-maintainer to ask. Perhaps they are conscious that stable reviews are presently a two-man effort and want to avoid adding round trips to that load. Perhaps they just need some reassurance.

So despite my reservations I am today opening the adviser up for general use, and I m interested in feedback about how it responds to real world proposals in various states. Most of the examples I have tested with already had a green light, so the value added by the adviser is limited. I would especially be interested in seeing a transcript alongside the submitted debdiff.

Try it out

I would dearly love to build this in a more Debian-ish environment, but for now I m limited in resources and skill to do that (help is welcome). Until that s a reality, you can try out the ChatGPT implementation: Debian Stable Update Adviser

23 June 2026

Dirk Eddelbuettel: tl-0.0.1 on CRAN: New Package

A new small package of mine just hit CRAN. The tl package wraps the (also very new) rspdlite package (announced last week) to offer a lightweight and consistent logging interface from both R and C++ that is also tiny, fast, capable thanks to rspdlite. The rspdlite announcement is a good place to get a first glimpse at that package; the upstream spdlite repo has all the details (for the C++ side of things). With tl we follow the same idea that our spdl package introduced: a simple consistent interface via just the tl:: prefix and the appropropriate logging level. In other words tl::debug("Alert -- foo is at ' '", foo) will work from both R and C++ (given a variable foo, and in the case of C++ an extra semicolon). Just give it a try, and see how it goes. The package is still young and small. The NEWS entry for this release is also very simple and just announces that we have a release. More details are in the ChangeLog and the GitHub repo.

Changes in version 0.0.1 (2025-06-17)
  • Initial CRAN upload

This post by Dirk Eddelbuettel originated on his Thinking inside the box blog. If you like this or other open-source work I do, you can sponsor me at GitHub.

17 June 2026

Dirk Eddelbuettel: rspdlite 0.1.0-1 on CRAN: New Package!

Very happy to share that a new package rspdlite arrived on CRAN today in its inaugural version 0.1.0-1. It wraps and provides the (header-only) C++20 library spdlite which its author describes (aptly) as tiny, fast, capable. Just like its bigger sibbling spdlog (which we wrapped as rcppspdlog), it is written by Gabi Melman. However, with a focus on C++20 and compile-time configuration, it is lighter, nimbler and faster. It is also still a fairly young project so changes may occur. I have been working on this for about a month, and it is ready for use by R and C++. It contains the initial upstream release 0.1.0, and I plan to follow the upstream versioning making this first release as 0.1.0-1. The package itself provides the headers for use from other C++ projects (i.e. mostly other packages), as well as a simple R wrapper so that logging can occur from either C++ or R. It will generally access the single logger instance in a compilation unit. So for a package built against these header it would be shared library of that package. At present we provide the basic logging level setters and getters, formatting accessors, and two (compile-time) options of a null logger and a file-based logger. More options are availble from the C++ level, multiple logging sinks are but one example. Some examples are provided in the package as an R example and a C++ example; these are probably best examined from the sources. The NEWS entry for this release is simply and just announces that we have a release. More details are in the ChangeLog and the GitHub repo.

Changes in version 0.1.0-1 (2025-06-08)
  • Initial complete version and CRAN upload

This post by Dirk Eddelbuettel originated on his Thinking inside the box blog. If you like this or other open-source work I do, you can sponsor me at GitHub. You can also sponsor my Tour de Shore 2026 ride in support of the Maywood Fine Arts Center.

15 June 2026

Dirk Eddelbuettel: rbenchmark 1.0.1 on CRAN: New(ly Adopted) Package!

Quick note to share that rbenchmark is back on CRAN! The rbenchmark package makes it easy to benchmark (and compare) simple R expressions. This package has been on CRAN for many years. At one point fourteen years ago it appeared to be rudderless so I offered help but things realigned. Now it was just tossed off CRAN, taking a number of packages depending on it with it (as shown in this CRANberries skeet listing a set of removed packages) so I offered again to help, and CRAN agreed. So here we are. So far I just made a number of small editing changes, added CI support, and enable dbsr-universe coverage . I do not expect to change the package materially. So far the package has no NEWS file either so maybe glance at the ChangeLog at the git repo.

This post by Dirk Eddelbuettel originated on his Thinking inside the box blog. If you like this or other open-source work I do, you can sponsor me at GitHub. You can also sponsor my Tour de Shore 2026 ride in support of the Maywood Fine Arts Center.

31 May 2026

Russell Coker: Links May 2026

Ron Garrett wrote an interesting blog post about the mathematical possibility of abiogenesis [1]. Cory Doctorow wrote an interesting blog post about the way the current antics of right wing extremists are forcing permanent changes in society away from the old systems [2]. William Angel wrote an insightful blog post comparing the costs of a Macbook and the Openrouter hosted service for LLMs [3]. The Register has an informative article about the threat that management systems built in to Intel and AMD CPUs pose to data sovereignty in EU owned cloud providers [4]. But this is just the first stage of building sovereign clouds, all significaant cloud services run at least 2 types of CPU and adding EU manufactured CPUs at a future time will be easy. Benn Jordan made an interesting YouTube video about the infrasound problems caused by data centers, we need FOSS to measure infrasound [5]. amarok on the Purism forum made a great post about how to setup profiles in Firefox for different uses [6]. fralb5 wrote an informative post on the Purism forum about how to use a Librem 5 (or any other FOSS Linux phone) to firewall spyware on an Android phone [7]. Michael Prokop wrote an interesting blog post about debugging input event problems on Linux which turned out to be due to an analogue headphone connection [8]. This gave me some useful pointers to investigating an input device problem which is probably very different. Patrick Boyle made an insightful youtube video about the ridiculous IPO of SpaceX, it seems like a scam from start to finish [9]. Anarcat wrote an insightful blog post about the LLM apocalypse comparing it to the horsemen of the apocalypse [10]. The Wikimedia Foundation (that runs wikipedia.org among other things) is sacking union organisers and trying to corporatise the organisation which means stealing the donations from the community [11]. Tianon Gravi wrote an informative blog post about containers, Debian, and Docker options [12]. We need a lot more work on these sorts of things in Debian. Memory Tagging and how it improves C/C++ memory safety is an interesting paper from Google researchers giving an overview of the benefits of tagged memory hardware for pointer validation on SPARC and ARM64 [13]. In 2013 a faulty beer fridge motor acted as a spark gap transmitter and blocked mobile phone service for several Melbourne suburbs [14].

21 May 2026

Amin Bandali: ffs 0.2.2 released

ffs provides a minor mode for simple plain text presentations in Emacs, where the slides are separated using the page-delimiter, by default the form feed character (^L). I wrote ffs in early 2022 for my LibrePlanet 2022 presentation the Net beyond the Web, and earlier this year decided to polish it towards being a proper package and submit it to GNU ELPA. The manual still needs some more work, but the overall package is in pretty good shape so I submitted for inclusion in GNU ELPA. ffs and I owe a debt of gratitude to Protesilaos for rounds of code review and feedback for improving and polishing the package in preparation for submission to GNU ELPA. You can watch videos of these sessions posted earlier on my website: Further, inspiration for parts of ffs's implementation was gratefully drawn from Protesilaos's Logos package for Emacs. Dedicated to the loving memory of Farangis Yousefinia. Below are the release notes.

Version 0.2.2 on 2026-05-21
First release of ffs on GNU ELPA. The attempted build of ffs 0.2.1 within GNU ELPA build sandbox failed with an Error: void-function (org-texinfo-kbd-macro) due to use of #+macro: kbd (eval (org-texinfo-kbd-macro $1)) in ffs.org for better formatting of key sequences in the exported Texinfo copy. This seems to have happened for the specific case of generating a plain text README using ox-ascii where ELPA didn't load ox-texinfo. To try and mitigate this, a README.md has been added for use as the package README instead of ffs.org. If not sufficient, a Texinfo copy of the ffs manual will be shipped instead of the Org one in the next release. ffs 0.2.2 also includes small fixes and improvements throughout ffs.el from Stefan Monnier, and additional feedback to be addressed in future releases.

Version 0.2.1 on 2026-05-20
The attempted build of ffs 0.2.0 within GNU ELPA build sandbox failed with a "Cannot include file" error on the "#+include: fdl.org" in the manual. So, as a workaround, we switch to using the official Texinfo copy of the GNU FDL license rather than an Org copy.

Version 0.2.0 on 2026-05-19
First release of ffs intended for GNU ELPA. After a few years of inactivity, in early 2026 I decided to dust off ffs.el, polish and document it, and offer for inclusion in GNU ELPA as a proper package.

Default value of ffs-default-face-height changed to nil
To minimize unexpected and/or unnecessary changes out-of-the-box, the default value of ffs-default-face-height has been changed to nil.

ffs-edit-buffer-name demoted from user option to variable
This is not an important user-facing setting, so to help avoid overwhelming users with many options, this has been demoted from a user option to a variable.

Several new user options for customizing ffs's behaviour
As part of the effort to bring ffs more in line with the conventions of other existing Emacs packages, the mechanisms for toggling various parts of Emacs's interface to minimize visual clutter were changed from being minor modes to being customizable user options. These are the replacement new user options, with a default value of nil:
  • ffs-hide-cursor
  • ffs-hide-mode-line
  • ffs-hide-header-line
Their value is buffer-local, and may be set globally using setq-default. See the sample configuration in the manual for an example of how to customize them. The new ffs-page-delimiter user option defines the page delimiter inserted by ffs-edit-done when inserting a new slide. Emacs's page-delimiter regexp should be able to match ffs-page-delimiter's value, so if you use a custom page-delimiter be sure to customize ffs-page-delimiter accordingly. The new ffs-echo-progress user option controls whether to display in echo area the progress through the slides. When non-nil, changing slides will also display the progress through the slides in the echo area. The format of the displayed progress can be customized using the new ffs-echo-progress-format user option. The new ffs-edit-display-buffer-alist user option may be used to control the Window configuration for the ffs-edit buffer. By default, it will display the ffs-edit buffer in the same window. The new ffs-edit-done-hook user option may be used to define hooks to be run at the end of ffs-edit-done after returning to the main ffs presentation buffer. Lastly, a new ffs-find-speaker-notes-function variable was added to allow customizing the find function used for opening the speaker's notes file, defaulting to find-file-other-frame.

Version 0.1.0 on 2022-05-19
Initial publication of ffs.el as part of my personal configurations for GNU Emacs. My first attempt at this concept was a now-archived ffsanim.el, a major mode implementation that used Emacs's animate library to animate slide texts onto the screen. Shortly after realizing the shortcomings of that approach, I abandoned it in favour a minor mode implementation and published version 0.1.0 of what is now ffs in my personal configs repository. I used this implementation for presenting my LibrePlanet 2022 talk, The Net beyond the Web. I picked "ffs" as the package name, the acronym for form feed slides.

18 May 2026

Sergio Durigan Junior: Fixing a 20+ year old bug in Debian curl

I have been helping co-maintain the Debian curl package for a few years now, and even though Samuel and Charles do most of the work, I'm happy to jump in and help when needed. This is one of those cases. Nowadays the package is maintained by 3 people (with help from others occasionally), but it hasn't always been like this. Samuel adopted the package back in 2021, and since then it has received a lot of love and care to make sure it lives up to Debian's standards. Again, kudos to both him and Charles who have been doing great work on this front. But a little more than 20 years ago, the situation in Debian (and curl!) was "a bit" different.

Once upon a time... According to d/changelog, the Debian curl maintainer in 2005 introduced changes to the packaging that allowed it to generate a version of libcurl for each TLS backend available: OpenSSL and GnuTLS. This meant that curl would have two binary library packages:
  • libcurl3-openssl and its respective -dev variant, for libcurl linked against OpenSSL; and
  • libcurl3-gnutls and its respective -dev variant, for libcurl linked against GnuTLS.
But then, around 2006/2007 or so, upstream curl decided to bump the SONAME version of libcurl from 3 to 4. At the time, they apparently did not version their library symbols like they do now, which was... less than ideal. I don't judge them: curl and a lot of other important projects have come a long way when we consider best practices to write shared libraries. Meanwhile, on Debian land, the release team was having trouble with other transitions going on at the time. For those who are not versed in Debian's vocabulary, a transition happens when a shared library gets its SONAME version bumped: when this happens, we have to make sure that all reverse dependencies of that library still build with the new version, and fix things that fail. The more reverse dependencies the library has, the harder this work gets. When upstream curl bumping the SONAME version of libcurl, the Debian curl maintainer at the time correctly renamed the binary packages from libcurl3- openssl,gnutls (and their -dev variants) to libcurl4- openssl,gnutls (and their -dev variants), which obviously triggered a transition. And a big one, because libcurl is used by several projects. Long story short, the Debian release team found themselves between a rock and a hard place. According to the late Steve Langasek at the time:
We talked a while back about the curl transition, and about how upstream's change from libcurl.so.3 to libcurl.so.4 is gratuitously painful for us in light of the large number of reverse dependencies. The libcurl transition has at this point gotten tangled with soname transitions in jasper, exiv2, kexiv2, and God only knows what else. So I'd like to revisit this question, because tracking this transition is costing the release team a lot of time that would be better spent elsewhere, and removing the need for a libcurl transition promises to reduce the complexity of the other components by an order of magnitude. On looking at the curl package, I've come to understand that the symbol versioning in place in this library is the result of a Debian-local patch. That's great news, because it suggests a solution to this quandary that doesn't require an unreasonable amount of developer time.
Yeah, it wasn't pretty. Here's what was proposed:
I am proposing the following:
  • Keep the library soname the same as it currently is upstream. Because upstream uses unversioned symbols, our package will be binary-compatible with applications built against the upstream libcurl regardless of what we do with symbol versioning, so leaving the soname alone minimizes the amount of patching to be done against upstream code here.
  • Revert the Debian symbol versioning to the libcurl3 version, and make libcurl.so.3 a symlink to libcurl.so.4. We have already established that libcurl.so.4 is still API-compatible with libcurl.so.3, in spite of the soname change upstream; reverting the symbol versioning will make it fully ABI-compatible with libcurl.so.3, and adding the symlink lets previously-built binaries find it.
  • Revert the Debian package names to the curl 7.15.5 versions. Because compatibility has been restored with libcurl3 and libcurl3-gnutls, restoring the package names provides the best upgrade path from etch to lenny; and because the symbol versions have been reverted, the libraries are not binary-compatible with the Debian packages currently named libcurl4/libcurl4-gnutls/libcurl4-openssl (in spite of being binary-compatible with upstream), so it would be wrong to keep the current names regardless.
  • Drop the SSL-less variant of the library, which was not present in curl 7.15.5; AFAICS, there is no use case where a user of curl needs to not have SSL support, so this split seems to be unnecessary overhead. Please correct me if I'm mistaken.
  • Leave the -dev package names alone otherwise, to simplify binNMUing of the reverse-dependencies (some packages have already added versioned build-deps on libcurl4.*-dev -- I have no idea why -- so reverting the names would mean more work to chase down those packages). Drop libcurl4-dev as a binary package, though, in favor of being Provided by libcurl4-gnutls-dev. Many of the packages currently build-depending on libcurl4-dev -- including some that wrongly used libcurl3-dev before -- are GPL, and these are apparently all packages where having SSL support missing in libcurl4 wasn't hurting them, so libcurl4-gnutls-dev seems to be the reasonable "default" here.
  • Schedule binNMUs for all reverse-dependencies.
Again, no judgement here: this was what needed to be done at the time, and I believe it was a good solution given the circumstances. In the end, the binary library packages got renamed again: from libcurl4- openssl,gnutls back to libcurl3- openssl,gnutls (but not their -dev variants!), but they continued shipping libcurl libraries whose SONAME version was 4. This solved the immediate problem of untangling the transitions mentioned by Steve, but introduced a technical debt that would stick with the package literally for decades. The situation at the end of 2007 was:
  • libcurl3-openssl with libcurl4-openssl-dev; and
  • libcurl3-gnutls with libcurl4-gnutls-dev.

More discrepancy is added Eventually the libcurl3-openssl package got renamed to libcurl3, but aside from that the situation with mismatched library names vs. SONAME versions stayed relatively unchanged until around 2018, when the Debian curl maintainer at the time (a different person) renamed libcurl3 to libcurl4 to fix a bug. This was the right thing to do for libcurl3, and at the time upstream curl was already properly versioning their symbols, but for some reason libcurl3-gnutls got left behind. So now we had:
  • libcurl4 with libcurl4-dev; and
  • libcurl3-gnutls with libcurl4-gnutls-dev.
In other words, we now have a discrepancy between the OpenSSL and GnuTLS variants' names. Yeah, confusing. And this is the situation right now, on May 2026, while I write this post. To make matters worse, the Debian curl package has been carrying a patch to facilitate the split of OpenSSL and GnuTLS flavours for decades now, and, for some reason I didn't bother to investigate, the patch pins the SONAME version of libcurl3-gnutls to CURL_GNUTLS_3, effectively overriding upstream's decision to version the symbols as CURL_GNUTLS_4.

A call to make things right Back in 2022, Simon McVittie filed a Debian bug to try and call our attention to the fact that we were shipping this messy set of curl packages. I had just started to get involved in the package maintenance and Samuel asked me to take a look at the bug. I noticed it was going to take more time than I had available, so I decided to put it in my TODO list (TM). Simon was generous enough to lay out a possible plan to tackle the problem, but I had a feeling that this was going to be harder than it looked. I kept postponing working on the bug, but also kept thinking about it now and then because it's an interesting thing to solve. Then, a month or so ago the Debian Brasil community got together for MiniDebConf Campinas 2026 and we decided to do a bug squashing party there. I started working on a few FTBFS bugs with GCC 16, but then got remembered about the curl bug and thought that that was the perfect time and place to start working on it, for a few reasons:
  • Samuel and Charles were also attending the conference, so I could talk to them about my plans and show them a PoC.
  • I was going to give a presentation about symbols (in pt_BR), so I could use this bug as an example of symbol versioning.
  • I wanted to have fun.

The initial plan The plan I had in mind was a variant of Simon's proposed plan:
  • I would have to adjust our GnuTLS-specific patch so that it did not override the SONAME version for libcurl-gnutls. Then,
  • For each symbol from libcurl3-gnutls I would have to:
    • Explicitly version it as curl_symbol_name@@CURL_GNUTLS_4.
    • Create an alias for the symbol (let's call it __curl_compat_symbol_name).
    • Explicitly version this alias as __curl_compat_symbol_name@CURL_GNUTLS_3.
  • Have a separate version of curl's linker script to make it possible to create a hierarchy between CURL_GNUTLS_3 and CURL_GNUTLS_4 symbols.
Note that this whole dance is needed because it is a hard requirement that programs linked against libcurl3-gnutls keep working when we ship libcurl4-gnutls, without needing to recompile them. Due to the fact that we will not really bump the SONAME of libcurl-gnutls (but instead fix the symbol versions shipped by it), we cannot expect programs to break given that they are actually using the exact same ABI as before. Unfortunately (as it is common with low level tools) the documentation for ld's versioning syntax is quite incomplete and hard to find. One of the best sources I found was this blog post. For this reason, let me quickly explain the different notations for symbol versioning used above.

curl_symbol_name@@CURL_GNUTLS_4 When we use curl_symbol_name@@CURL_GNUTLS_4 (note the @@) we are telling the linker that this should be considered the default version of curl_symbol_name. In other words, when a binary that links against libcurl-gnutls calls curl_symbol_name, the linker should use curl_symbol_name@@CURL_GNUTLS_4 to resolve the symbol. There are a few ways to specify a symbol version in C/C++:
__attribute__((__symver__("curl_symbol_name@@CURL_GNUTLS_4")))
void curl_symbol_name()
 
  /* ... */
 

/* or... */
void curl_symbol_name()
 
  /* ... */
 
__asm__(".symver curl_symbol_name, curl_symbol_name@@CURL_GNUTLS_4");

Function alias Creating an alias for a function is basically saying that a function can be called by another name. You can do that in C/C++ like:
void curl_symbol_name()
 
  /* ... */
 

void __curl_compat_symbol_name()
  __attribute__((alias("curl_symbol_name")));

__curl_compat_symbol_name@CURL_GNUTLS_3 Finally, when we use __curl_compat_symbol_name@CURL_GNUTL_3 (note the single @) we are telling the linker that this symbol exists, but it should not be used as the default symbol. In fact, this notation will basically hide the symbol and make it only available for those programs that have already been linked against it. It's a way of saying "don't offer this symbol when linking, but it's here in case a program needs it to run" (it's a bit more complicated than that, but you get the point). The reason I had to create an alias to the function before versioning the symbol with @CURL_GNUTLS_3 is because, once I've versioned the main symbol as @@CURL_GNUTLS_4, I can't create another version of it. It's also important to mention that to be able to create a version for the alias I also had to change its visibility to default. In the end, the alias ended up being defined as:
extern void __curl_compat_symbol_name()
  __attribute__((alias("curl_symbol_name"), visibility("default")));

First attempt and lessons learned For my PoC I decided to tackle a small subset of the problem. The symbols file for libcurl3-gnutls contains around 100 symbols that need to be fixed, so I chose two of them and started trying to write a patch to see if I could make things work. And after some time struggling with GCC's syntax and inspecting nm -D's output I finally got something that looked like it was going to work. The two symbols I had chosen to work with got correctly versioned (both as @@CURL_GNUTLS_4 and @CURL_GNUTLS_3), and a quick-and-dirty C program that used those symbols correctly compiled and ran with the expected symbols. I showed the results to Samuel and Charles, we got excited about what we saw, and then the conference ended.

Second attempt and some adjustments After getting back home I resumed the work on my branch and wrote an Emacs function that semi-automatically adjusted all 100+ symbols listed in the symbols file so that they all looked like:
__attribute__((__symver__("curl_symbol_name@@CURL_GNUTLS_4")))
void curl_symbol_name()
 
  /* ... */
 

extern void __curl_compat_symbol_name()
  __attribute__((alias("curl_symbol_name"), visibility("default"),
                 symver("__curl_compat_symbol_name@CURL_GNUTLS_3")));
The patch was big but mostly repetitive, and I was happy to have come up with a solution that looked clean. Until I tried to build the package, that is. I started seeing some strange errors that happened when ld was trying to link the final libcurl4-gnutls object (yes, at that point I had already renamed the binary package). This is one of the errors I was getting from ld (I got variants of this error as I was trying to fix the approach):
/usr/bin/x86_64-linux-gnu-ld.bfd: .libs/libcurl_gnutls_la-easy.o: in function  dupeasy_meta_freeentry':
./debian/build-gnutls/lib/./debian/build-gnutls/lib/easy.c:1024: multiple definition of  curl_easy_cleanup'; .libs/libcurl_gnutls_la-easy.o:./debian/build-gnutls/lib/./debian/build-gnutls/lib/easy.c:908: first defined here
/usr/bin/x86_64-linux-gnu-ld.bfd: .libs/libcurl-gnutls.so.4.8.0: version node not found for symbol curl_easy_duphandle@CURL_GNUTLS3
/usr/bin/x86_64-linux-gnu-ld.bfd: failed to set dynamic section sizes: bad value
This was strange. I did some tests with very simple versions of a shared library using the versioning mechanism I had implemented and it all worked. I could not reproduce the problem, and that's not a great feeling to have. Then, after reading a lot of documentation and blog posts throughout the internet I found something interesting. Apparently ld has a limitation when it comes to dealing with symbols versioned with @@. If there is a single symbol versioned like that in a source file (the actual term is TU, which means Translation Unit, but let's simplify), then ld is happy and generates the expected version without issues. But when we're dealing with multiple definitions of @@ symbols in a source file (which is exactly what happens in curl), then ld can get confused and start giving errors during the link stage. To solve that limitation, we have to resort to yet another symbol versioning notation: @@@. Yes, three at signs. For example:
void curl_symbol_name()
 
  /* ... */
 
__asm__(".symver curl_symbol_name, curl_symbol_name@@@CURL_GNUTLS_4");
Note that we have to use __asm__ because GCC's __attribute__ doesn't support the triple-at notation. What this does is tell the linker to create a versioned symbol for curl_symbol_name, set it as the default symbol when linking, but also remove the unversioned curl_symbol_name symbol. This makes ld happy and allows it to successfully link libcurl-gnutls. As usual, you won't find any mention of the @@@ notation inside ld's documentation. With libcurl-gnutls compiling again, I had to adjust libcurl's linker script to create a hierarchy between CURL_GNUTLS_3 and CURL_GNUTLS_4 symbols. Here's the final version of the file:
CURL_GNUTLS_3
 
  global:
    curl_easy_cleanup;
    /* lots of other symbols here */
  local: *;
 ;

CURL_GNUTLS_4
 
  global: curl_*;
  local: *;
  CURL_GNUTLS_3;

Debian package adjustments After getting the hard part out of the way, the rest was easy. It was time to finally rename libcurl3-gnutls to libcurl4-gnutls. Initially I was thinking that I'd need to ask the release team for a transition to happen, but as it turns out that won't be necessary. Because we are effectively shipping the same exact library/ABI and the only difference is the inclusion of the extra CURL_GNUTLS_4 versioned symbols, and given that we will be shipping CURL_GNUTLS_3 versioned symbols to guarantee backwards compatibility, packages won't need to get rebuild just to pick up the new dependency. Instead, we can safely turn libcurl3-gnutls into a transitional package that depends on libcurl4-gnutls.

Merge request and next steps This is the merge request where I am working on the fix. As of this writing it is in a draft state, but I expect to merge in the next couple of days. Once the fixed curl package is uploaded, we should keep an eye on the archive to make sure no unexpected bugs happen. I would like to carry this patch downstream at least until forky is released. It doesn't make sense to propose it upstream because this problem is Debian-specific and should be fixed there. We will need to make sure that all reverse dependencies of libcurl3-gnutls are recompiled before we can get rid of the transitional package, too. This was a fun bug to investigate and fix, and I am happy that we will finally have sensible names (and symbol versions!) for both of our libcurl variants. Stay tuned for the next challenge!

Sergio Durigan Junior: Fixing a 20+ year old bug in Debian curl

I have been helping co-maintain the Debian curl package for a few years now, and even though Samuel and Charles do most of the work, I'm happy to jump in and help when needed. This is one of those cases. Nowadays the package is maintained by 3 people (with help from others occasionally), but it hasn't always been like this. Samuel adopted the package back in 2021, and since then it has received a lot of love and care to make sure it lives up to Debian's standards. Again, kudos to both him and Charles who have been doing great work on this front. But a little more than 20 years ago, the situation in Debian (and curl!) was "a bit" different.

Once upon a time... According to d/changelog, the Debian curl maintainer in 2005 introduced changes to the packaging that allowed it to generate a version of libcurl for each TLS backend available: OpenSSL and GnuTLS. This meant that curl would have two binary library packages:
  • libcurl3-openssl and its respective -dev variant, for libcurl linked against OpenSSL; and
  • libcurl3-gnutls and its respective -dev variant, for libcurl linked against GnuTLS.
But then, around 2006/2007 or so, upstream curl decided to bump the SONAME version of libcurl from 3 to 4. At the time, they apparently did not version their library symbols like they do now, which was... less than ideal. I don't judge them: curl and a lot of other important projects have come a long way when we consider best practices to write shared libraries. Meanwhile, on Debian land, the release team was having trouble with other transitions going on at the time. For those who are not versed in Debian's vocabulary, a transition happens when a shared library gets its SONAME version bumped: when this happens, we have to make sure that all reverse dependencies of that library still build with the new version, and fix things that fail. The more reverse dependencies the library has, the harder this work gets. When upstream curl bumping the SONAME version of libcurl, the Debian curl maintainer at the time correctly renamed the binary packages from libcurl3- openssl,gnutls (and their -dev variants) to libcurl4- openssl,gnutls (and their -dev variants), which obviously triggered a transition. And a big one, because libcurl is used by several projects. Long story short, the Debian release team found themselves between a rock and a hard place. According to the late Steve Langasek at the time:
We talked a while back about the curl transition, and about how upstream's change from libcurl.so.3 to libcurl.so.4 is gratuitously painful for us in light of the large number of reverse dependencies. The libcurl transition has at this point gotten tangled with soname transitions in jasper, exiv2, kexiv2, and God only knows what else. So I'd like to revisit this question, because tracking this transition is costing the release team a lot of time that would be better spent elsewhere, and removing the need for a libcurl transition promises to reduce the complexity of the other components by an order of magnitude. On looking at the curl package, I've come to understand that the symbol versioning in place in this library is the result of a Debian-local patch. That's great news, because it suggests a solution to this quandary that doesn't require an unreasonable amount of developer time.
Yeah, it wasn't pretty. Here's what was proposed:
I am proposing the following:
  • Keep the library soname the same as it currently is upstream. Because upstream uses unversioned symbols, our package will be binary-compatible with applications built against the upstream libcurl regardless of what we do with symbol versioning, so leaving the soname alone minimizes the amount of patching to be done against upstream code here.
  • Revert the Debian symbol versioning to the libcurl3 version, and make libcurl.so.3 a symlink to libcurl.so.4. We have already established that libcurl.so.4 is still API-compatible with libcurl.so.3, in spite of the soname change upstream; reverting the symbol versioning will make it fully ABI-compatible with libcurl.so.3, and adding the symlink lets previously-built binaries find it.
  • Revert the Debian package names to the curl 7.15.5 versions. Because compatibility has been restored with libcurl3 and libcurl3-gnutls, restoring the package names provides the best upgrade path from etch to lenny; and because the symbol versions have been reverted, the libraries are not binary-compatible with the Debian packages currently named libcurl4/libcurl4-gnutls/libcurl4-openssl (in spite of being binary-compatible with upstream), so it would be wrong to keep the current names regardless.
  • Drop the SSL-less variant of the library, which was not present in curl 7.15.5; AFAICS, there is no use case where a user of curl needs to not have SSL support, so this split seems to be unnecessary overhead. Please correct me if I'm mistaken.
  • Leave the -dev package names alone otherwise, to simplify binNMUing of the reverse-dependencies (some packages have already added versioned build-deps on libcurl4.*-dev -- I have no idea why -- so reverting the names would mean more work to chase down those packages). Drop libcurl4-dev as a binary package, though, in favor of being Provided by libcurl4-gnutls-dev. Many of the packages currently build-depending on libcurl4-dev -- including some that wrongly used libcurl3-dev before -- are GPL, and these are apparently all packages where having SSL support missing in libcurl4 wasn't hurting them, so libcurl4-gnutls-dev seems to be the reasonable "default" here.
  • Schedule binNMUs for all reverse-dependencies.
Again, no judgement here: this was what needed to be done at the time, and I believe it was a good solution given the circumstances. In the end, the binary library packages got renamed again: from libcurl4- openssl,gnutls back to libcurl3- openssl,gnutls (but not their -dev variants!), but they continued shipping libcurl libraries whose SONAME version was 4. This solved the immediate problem of untangling the transitions mentioned by Steve, but introduced a technical debt that would stick with the package literally for decades. The situation at the end of 2007 was:
  • libcurl3-openssl with libcurl4-openssl-dev; and
  • libcurl3-gnutls with libcurl4-gnutls-dev.

More discrepancy is added Eventually the libcurl3-openssl package got renamed to libcurl3, but aside from that the situation with mismatched library names vs. SONAME versions stayed relatively unchanged until around 2018, when the Debian curl maintainer at the time (a different person) renamed libcurl3 to libcurl4 to fix a bug. This was the right thing to do for libcurl3, and at the time upstream curl was already properly versioning their symbols, but for some reason libcurl3-gnutls got left behind. So now we had:
  • libcurl4 with libcurl4-dev; and
  • libcurl3-gnutls with libcurl4-gnutls-dev.
In other words, we now have a discrepancy between the OpenSSL and GnuTLS variants' names. Yeah, confusing. And this is the situation right now, on May 2026, while I write this post. To make matters worse, the Debian curl package has been carrying a patch to facilitate the split of OpenSSL and GnuTLS flavours for decades now, and, for some reason I didn't bother to investigate, the patch pins the SONAME version of libcurl3-gnutls to CURL_GNUTLS_3, effectively overriding upstream's decision to version the symbols as CURL_GNUTLS_4.

A call to make things right Back in 2022, Simon McVittie filed a Debian bug to try and call our attention to the fact that we were shipping this messy set of curl packages. I had just started to get involved in the package maintenance and Samuel asked me to take a look at the bug. I noticed it was going to take more time than I had available, so I decided to put it in my TODO list (TM). Simon was generous enough to lay out a possible plan to tackle the problem, but I had a feeling that this was going to be harder than it looked. I kept postponing working on the bug, but also kept thinking about it now and then because it's an interesting thing to solve. Then, a month or so ago the Debian Brasil community got together for MiniDebConf Campinas 2026 and we decided to do a bug squashing party there. I started working on a few FTBFS bugs with GCC 16, but then got remembered about the curl bug and thought that that was the perfect time and place to start working on it, for a few reasons:
  • Samuel and Charles were also attending the conference, so I could talk to them about my plans and show them a PoC.
  • I was going to give a presentation about symbols (in pt_BR), so I could use this bug as an example of symbol versioning.
  • I wanted to have fun.

The initial plan The plan I had in mind was a variant of Simon's proposed plan:
  • I would have to adjust our GnuTLS-specific patch so that it did not override the SONAME version for libcurl-gnutls. Then,
  • For each symbol from libcurl3-gnutls I would have to:
    • Explicitly version it as curl_symbol_name@@CURL_GNUTLS_4.
    • Create an alias for the symbol (let's call it __curl_compat_symbol_name).
    • Explicitly version this alias as __curl_compat_symbol_name@CURL_GNUTLS_3.
  • Have a separate version of curl's linker script to make it possible to create a hierarchy between CURL_GNUTLS_3 and CURL_GNUTLS_4 symbols.
Note that this whole dance is needed because it is a hard requirement that programs linked against libcurl3-gnutls keep working when we ship libcurl4-gnutls, without needing to recompile them. Due to the fact that we will not really bump the SONAME of libcurl-gnutls (but instead fix the symbol versions shipped by it), we cannot expect programs to break given that they are actually using the exact same ABI as before. Unfortunately (as it is common with low level tools) the documentation for ld's versioning syntax is quite incomplete and hard to find. One of the best sources I found was this blog post. For this reason, let me quickly explain the different notations for symbol versioning used above.

curl_symbol_name@@CURL_GNUTLS_4 When we use curl_symbol_name@@CURL_GNUTLS_4 (note the @@) we are telling the linker that this should be considered the default version of curl_symbol_name. In other words, when a binary that links against libcurl-gnutls calls curl_symbol_name, the linker should use curl_symbol_name@@CURL_GNUTLS_4 to resolve the symbol. There are a few ways to specify a symbol version in C/C++:
__attribute__((__symver__("curl_symbol_name@@CURL_GNUTLS_4")))
void curl_symbol_name()
 
  /* ... */
 

/* or... */
void curl_symbol_name()
 
  /* ... */
 
__asm__(".symver curl_symbol_name, curl_symbol_name@@CURL_GNUTLS_4");

Function alias Creating an alias for a function is basically saying that a function can be called by another name. You can do that in C/C++ like:
void curl_symbol_name()
 
  /* ... */
 

void __curl_compat_symbol_name()
  __attribute__((alias("curl_symbol_name")));

__curl_compat_symbol_name@CURL_GNUTLS_3 Finally, when we use __curl_compat_symbol_name@CURL_GNUTL_3 (note the single @) we are telling the linker that this symbol exists, but it should not be used as the default symbol. In fact, this notation will basically hide the symbol and make it only available for those programs that have already been linked against it. It's a way of saying "don't offer this symbol when linking, but it's here in case a program needs it to run" (it's a bit more complicated than that, but you get the point). The reason I had to create an alias to the function before versioning the symbol with @CURL_GNUTLS_3 is because, once I've versioned the main symbol as @@CURL_GNUTLS_4, I can't create another version of it. It's also important to mention that to be able to create a version for the alias I also had to change its visibility to default. In the end, the alias ended up being defined as:
extern void __curl_compat_symbol_name()
  __attribute__((alias("curl_symbol_name"), visibility("default")));

First attempt and lessons learned For my PoC I decided to tackle a small subset of the problem. The symbols file for libcurl3-gnutls contains around 100 symbols that need to be fixed, so I chose two of them and started trying to write a patch to see if I could make things work. And after some time struggling with GCC's syntax and inspecting nm -D's output I finally got something that looked like it was going to work. The two symbols I had chosen to work with got correctly versioned (both as @@CURL_GNUTLS_4 and @CURL_GNUTLS_3), and a quick-and-dirty C program that used those symbols correctly compiled and ran with the expected symbols. I showed the results to Samuel and Charles, we got excited about what we saw, and then the conference ended.

Second attempt and some adjustments After getting back home I resumed the work on my branch and wrote an Emacs function that semi-automatically adjusted all 100+ symbols listed in the symbols file so that they all looked like:
__attribute__((__symver__("curl_symbol_name@@CURL_GNUTLS_4")))
void curl_symbol_name()
 
  /* ... */
 

extern void __curl_compat_symbol_name()
  __attribute__((alias("curl_symbol_name"), visibility("default"),
                 symver("__curl_compat_symbol_name@CURL_GNUTLS_3")));
The patch was big but mostly repetitive, and I was happy to have come up with a solution that looked clean. Until I tried to build the package, that is. I started seeing some strange errors that happened when ld was trying to link the final libcurl4-gnutls object (yes, at that point I had already renamed the binary package). This is one of the errors I was getting from ld (I got variants of this error as I was trying to fix the approach):
/usr/bin/x86_64-linux-gnu-ld.bfd: .libs/libcurl_gnutls_la-easy.o: in function  dupeasy_meta_freeentry':
./debian/build-gnutls/lib/./debian/build-gnutls/lib/easy.c:1024: multiple definition of  curl_easy_cleanup'; .libs/libcurl_gnutls_la-easy.o:./debian/build-gnutls/lib/./debian/build-gnutls/lib/easy.c:908: first defined here
/usr/bin/x86_64-linux-gnu-ld.bfd: .libs/libcurl-gnutls.so.4.8.0: version node not found for symbol curl_easy_duphandle@CURL_GNUTLS3
/usr/bin/x86_64-linux-gnu-ld.bfd: failed to set dynamic section sizes: bad value
This was strange. I did some tests with very simple versions of a shared library using the versioning mechanism I had implemented and it all worked. I could not reproduce the problem, and that's not a great feeling to have. Then, after reading a lot of documentation and blog posts throughout the internet I found something interesting. Apparently ld has a limitation when it comes to dealing with symbols versioned with @@. If there is a single symbol versioned like that in a source file (the actual term is TU, which means Translation Unit, but let's simplify), then ld is happy and generates the expected version without issues. But when we're dealing with multiple definitions of @@ symbols in a source file (which is exactly what happens in curl), then ld can get confused and start giving errors during the link stage. To solve that limitation, we have to resort to yet another symbol versioning notation: @@@. Yes, three at signs. For example:
void curl_symbol_name()
 
  /* ... */
 
__asm__(".symver curl_symbol_name, curl_symbol_name@@@CURL_GNUTLS_4");
Note that we have to use __asm__ because GCC's __attribute__ doesn't support the triple-at notation. What this does is tell the linker to create a versioned symbol for curl_symbol_name, set it as the default symbol when linking, but also remove the unversioned curl_symbol_name symbol. This makes ld happy and allows it to successfully link libcurl-gnutls. As usual, you won't find any mention of the @@@ notation inside ld's documentation. With libcurl-gnutls compiling again, I had to adjust libcurl's linker script to create a hierarchy between CURL_GNUTLS_3 and CURL_GNUTLS_4 symbols. Here's the final version of the file:
CURL_GNUTLS_3
 
  global:
    curl_easy_cleanup;
    /* lots of other symbols here */
  local: *;
 ;

CURL_GNUTLS_4
 
  global: curl_*;
  local: *;
  CURL_GNUTLS_3;

Debian package adjustments After getting the hard part out of the way, the rest was easy. It was time to finally rename libcurl3-gnutls to libcurl4-gnutls. Initially I was thinking that I'd need to ask the release team for a transition to happen, but as it turns out that won't be necessary. Because we are effectively shipping the same exact library/ABI and the only difference is the inclusion of the extra CURL_GNUTLS_4 versioned symbols, and given that we will be shipping CURL_GNUTLS_3 versioned symbols to guarantee backwards compatibility, packages won't need to get rebuild just to pick up the new dependency. Instead, we can safely turn libcurl3-gnutls into a transitional package that depends on libcurl4-gnutls.

Merge request and next steps This is the merge request where I am working on the fix. As of this writing it is in a draft state, but I expect to merge in the next couple of days. Once the fixed curl package is uploaded, we should keep an eye on the archive to make sure no unexpected bugs happen. I would like to carry this patch downstream at least until forky is released. It doesn't make sense to propose it upstream because this problem is Debian-specific and should be fixed there. We will need to make sure that all reverse dependencies of libcurl3-gnutls are recompiled before we can get rid of the transitional package, too. This was a fun bug to investigate and fix, and I am happy that we will finally have sensible names (and symbol versions!) for both of our libcurl variants. Stay tuned for the next challenge!

17 May 2026

Otto Kek l inen: Balancing persistence vs pivoting is grit a virtue or wasteful?

Featured image of post Balancing persistence vs pivoting   is grit a virtue or wasteful?Being persistent, sticking to a plan and showing up to work every day is generally valued highly across all cultures as virtuous behavior. It is obvious that anything of value and worth achieving is also not easy, but requires significant and recurring effort. Learning a new language, winning a sports competition or building a successful business are all typical scenarios where grit plays a central role above everything else. However, sometimes the virtue of tenacity can result in just a waste of energy. The question is then: how does one recognize that true progress is being blocked by stubbornness and a pivot would be the correct decision, as opposed to being close to breakthrough where doing more of the same would actually be the right choice?

What is persistence actually? To think clearly about this topic, one must first grasp the concept of grit and what it looks like in practice. Research by psychologist Angela Duckworth on grit shows that sustained effort in the face of setbacks separates high achievers from those who quit too soon. Entrepreneurs who iterated through dozens of failed prototypes or writers who revised manuscripts for years understand this truth. Persistence builds resilience, deep expertise, and the kind of compounding results that shortcuts cannot deliver. It also protects against the distraction of shiny new ideas that pull focus from what actually works. Persistence is about:
  1. Believing in an outcome and working towards it despite people around you not sharing the belief, and despite your own work and experiments not being successful.
  2. Continuing to hold the belief and sticking to the decision despite other ideas, solutions and competing alternatives surfacing.
  3. The more time passes, the firmer the conviction becomes. Time, money, and emotional energy invested in a failing direction create psychological pressure to continue (sunk-cost fallacy).
Simply following through on a plan or upholding a contract is not true persistence. Grit is a personal trait one can cultivate to actually become more energized to do something precisely because it turns out to be harder than expected.

Pivoting: a calculated choice The opposite of being persistent is giving up. Pivoting is not about giving up, but about redirecting the energy and momentum towards a new goal. Pivoting requires coming to the realization that you were wrong, and going through the painful process of discovering a new truth. Ideas tend to be abundant, and doing something new isn t hard as such. The hard part is to abandon a previously held belief and adopt a new one with equal conviction. To have that conviction you need to have data and metrics. This is also the key to how to decide between persisting vs pivoting at any moment in time.

Key metrics of success Any decision is only as good as the information available at the time it was made. To be set up for success one needs to start by deciding on what the actual goal is, what one values and how progress is measured. Key metrics are usually easiest to discover by working backwards from the goal. If you want to build an electric car, you might decide that the goal is to have a car that costs 30,000 euros and can drive 300 km on one charge. From that goal you can break down what the cost structure should be, what volume of production is needed to break even, what raw materials are needed and what the battery chemistry needs to achieve to meet the goal. That can further be broken down into a rate of progress. Suppose the plan requires battery energy density to reach 150 Wh/kg to be viable. If the state of the art starts at 100 Wh/kg and funding lasts a maximum of five years, the team needs at least an 8% improvement every year (1.08^5 100 Wh/kg 150 Wh/kg). This can then be used as a guideline. Sometimes progress is not steady, but happens in jumps. Even in those cases there should be a trajectory to benchmark the jumps against. In an online business, the key metric could, for example, be one of these:
  • 7- or 30-day retention rate: Do new users who try the service actually like it?
  • Weekly or monthly active users: Is usage trending up?
  • Feature adoption rate: In an existing service, how many users are using the new feature?
  • Product-Market Fit Score (from Sean Ellis test): Percentage of users who say they would be very disappointed if the product disappeared. Above 40% is a strong early indicator. A number below that (after multiple iterations) is a good data point to pivot.
  • Revenue run rate or burn rate: The most generic metric everything eventually boils down to. Healthy markets reward good products.
Weekly metrics are better than monthly, as they make the feedback loop faster and allow you to get validation quickly and do minor course corrections along the way. A complete pivot should, however, be based on long-term data, driven by the key metric and supported by additional data points. Metrics are also needed because they can t be bribed or convinced to be anything other than what they are. Listening to other people is good, but just relying on the opinion of others is extremely dangerous because people are biased either for you or against you depending on whether they see you as a trusted leader or an outcast. Key metrics are of course domain-specific and everyone needs to come up with their own. However, you must have some key metric. You can t have the excuse that what you are doing can t be measured. If you are part of a larger organization and you need to advocate for a difficult decision for example, to kill your darlings when facing a pivot you need to have the metrics to back up your views, and those metrics need to have been established way before as something the organization values, and not cherry-picked just for this one decision. It does not matter if you are on a personal improvement journey, running a political campaign, inventing a new product, or growing a business you need to have some metric you can check at any given time to see if things are improving fast enough to predict success. Metrics can and should also be used in daily work to validate that you are on the correct path, and to optimize execution.

Famous examples of persistence and pivoting that led to breakthroughs In all of the cases below it is of course in hindsight easy to say they made the right decision. However, take a minute to try to imagine yourself in their shoes at the time of the decision. What metrics might they have had available to support their decision? What would you have wanted to measure or find out if you were in the same situation?
  • Frustrated that his vacuum lost suction, James Dyson spent five years and built thousands of failed prototypes in a backyard shed. He remortgaged his home, lived on savings, and faced rejection from every major manufacturer who wanted to protect their bag-replacement business. The 5,127th prototype based on an idea from a sawmill with a cyclone finally worked. Launched in 1993, the Dyson DC01 became Britain s best-selling vacuum within two years.
  • As a single mother on welfare in the mid-1990s, J.K. Rowling finished her manuscript for Harry Potter and the Philosopher s Stone while battling depression and poverty. She hand-typed copies and mailed them to publishers. Twelve rejected it outright, with comments like children s books about magic don t sell. She nearly quit multiple times but kept revising and submitting. Bloomsbury finally accepted it after the CEO s eight-year-old daughter read the first chapter and demanded the rest. The series has since sold hundreds of millions of copies worldwide.
  • Founded in 1997 as a mail-order DVD rental service, Netflix added unlimited subscriptions in 1999 to compete with Blockbuster. By 2007, broadband growth and declining DVD sales signaled a shift. CEO Reed Hastings pivoted aggressively toward streaming, investing in bandwidth deals and original content while de-emphasizing physical media. The move faced skepticism, but eventually changed the whole culture of how entertainment is consumed.
  • YouTube launched in 2005 as a video-dating site. Founders offered money to women who uploaded dating videos, but almost no one did. Meanwhile, users uploaded random clips. The team recognized the mismatch and pivoted within months to a general-purpose video-sharing platform with easy uploading. Google bought it just 18 months later.
  • Instagram began in 2010 as Burbn, a location-based check-in app that let users post plans, earn points, and share photos. Co-founders Kevin Systrom and Mike Krieger quickly noticed users ignored most features and mainly used it for photo-sharing. They made the tough call: scrap everything else. Within weeks, they rebuilt the app around clean, simple photography with filters. The pivot launched as Instagram in October 2010. It gained 1 million users in two months and was acquired by Facebook just 18 months later.

Insanity or conviction? English has several proverbs that warn against excessive persistence, such as banging your head against the wall . Insanity is commonly defined as Doing the same thing over and over again and expecting different results. In Finland, the national identity is practically built on the concept of sisu . It means much more than just grit . The word is derived from the word for inside or guts and represents an unexplained, almost superhuman force that makes one stoically take action despite seemingly impossible odds and somehow succeed anyway. It became a defining national mythos during the Winter War (1939 1940), where a force 10 times larger than the Finnish army tried to invade the country but was stopped and Finland just barely managed to keep its independence. The word sisu transitioned from a character trait to a pillar of national survival. I think Finns survived because the more you believe in persistence, the more likely you are to persist. I view persistence as a religion that requires faith, while pivoting is a science where you derive the truth from the numbers. When in doubt, I would always choose persistence over pivoting. Perhaps it is because of my genetic tendency towards having sisu , but I would also rather keep on going a bit more and try one more time before giving up and pivoting in order to get more data, so that when I pivot, I know it is absolutely the right thing to do at that point. Depending on the situation, the costs of postponing the pivot vary. Of course, if the main metric is the burn rate and a company is running out of money, a pivot must be done early enough that the remaining runway is enough to execute the pivot, and then some more. In some situations a business idea might simply be ahead of its time. If that is the conviction and the key metrics support it, the best way to navigate the situation is to cut down on costs and wait for competitors to appear, help build general awareness, and then ramp up again to ride the wave. Remember that success does not come from grit alone there is always an element of timing and luck as well. But if you are not persistent and stop showing up every day, you won t be able to seize the opportunities if and when they arise.

Failure is the likely outcome you have to avoid it at any cost One must also realize that most attempts end in failure. Failure is the baseline, and success is the exception. To reach a breakthrough, one must be stubbornly persistent. In particular, if you are a leader, you need to be so high in conviction that it almost becomes an aura that radiates to those around you. Postponing the decision to pivot allows you to get a bit more data for the decision, so that once you pivot, you have full belief in the new direction. Once you pivot, there is no looking back, otherwise you will undermine morale and most certainly fail with the new thing as people will execute it with hesitation. Failure is statistically always the more likely outcome. Most things end in failure and we never hear about them. If someone on your team does not believe in what you are doing, it is very easy for them to prove that something is a failure by spreading negativity, putting in less effort (perhaps unconsciously due to lack of conviction) and thus actually contributing to a self-fulfilling failure. In most areas of life, ideas are cheap and the only thing that matters is execution. To be good at executing, you need to be good at making decisions. When drafting plans it is good to have alternatives and a lot of consideration. However, when execution starts, there is no room for doubt, otherwise the chances of success decrease. Therefore, the best way of balancing persistence vs pivoting is to
  1. plan well ahead,
  2. establish the key metrics,
  3. have thresholds established for what would trigger a pivot, and
  4. do everything you can to move the metrics in the direction you want them to go.
Finally, if you decide to pivot, you must do so only with very high conviction, as you can t undo a pivot, and you should not be doing multiple pivots in a row either. If you are fully convinced yourself about the pivot, you will also be able to convince others about it, and carry the momentum.

12 April 2026

Dirk Eddelbuettel: littler 0.3.23 on CRAN: Mostly Internal Fixes

max-heap image The twentyfourth release of littler as a CRAN package landed on CRAN just now, following in the now twenty-one year history (!!) as a (initially non-CRAN) package started by Jeff in 2006, and joined by me a few weeks later. littler is the first command-line interface for R as it predates Rscript. It allows for piping as well for shebang scripting via #!, uses command-line arguments more consistently and still starts faster. It also always loaded the methods package which Rscript only began to do in later years. littler lives on Linux and Unix, has its difficulties on macOS due to some-braindeadedness there (who ever thought case-insensitive filesystems as a default were a good idea?) and simply does not exist on Windows (yet the build system could be extended see RInside for an existence proof, and volunteers are welcome!). See the FAQ vignette on how to add it to your PATH. A few examples are highlighted at the Github repo:, as well as in the examples vignette. This release, which comes just two months after the previous 0.3.22 release that brought a few new features, is mostly internal. (The previous release erroneously had 0.3.23 in its blog and social media posts, it really was 0.3.22 and this one now is is 0.3.23.) Mattias Ellert address a nag (when building for a distribution) about one example file with a shebang not have excutable modes. I accommodated the ever-changing interface the C API of R (within about twelve hours of being notified). A few other smaller changes were made as well polishing a script or two or usual, see below for more. The full change description follows.

Changes in littler version 0.3.23 (2026-04-12)
  • Changes in examples scripts
    • Correct spelling in installGithub.r to lower-case h
    • The r2u.r now recognises resolute aka 26.06
    • installRub.r can install (more easily) from r-multiverse
    • A file permission was corrected (Mattias Ellert in #131)
  • Changes in package
    • Update script count and examples in README.md
    • Continuous intgegration scripts received minor updates
    • The C level access to the R API was updated to reflect most recent standards (Dirk in #132)

My CRANberries service provides a comparison to the previous release. Full details for the littler release are provided as usual at the ChangeLog page, and also on the package docs website. The code is available via the GitHub repo, from tarballs and now of course also from its CRAN page and via install.packages("littler"). Binary packages are available directly in Debian as well as (in a day or two) Ubuntu binaries at CRAN thanks to the tireless Michael Rutter. Comments and suggestions are welcome at the GitHub repo.

This post by Dirk Eddelbuettel originated on his Thinking inside the box blog. If you like this or other open-source work I do, you can sponsor me at GitHub. You can also sponsor my Tour de Shore 2026 ride in support of the Maywood Fine Arts Center.

2 April 2026

Samuel Henrique: Bringing HTTP/3 to curl on Amazon Linux

Screenshot of the top entry of the curl package's changelog, showing the following: Changelogs for curl-8.17.0-1.amzn2023.0.2.x86_64 * Mon Mar 16 00:00:00 2026 Samuel Henrique (samueloph) <samhn@amazon.com> - 8.17.0-1.amzn2023.0.2 - Enable HTTP/3 support in the full build using ngtcp2 and nghttp3 - HTTP/3 is explicitly disabled in the minimal build - Add runtime dependencies on libnghttp3 and libngtcp2 with minimum version pinning - Run tests in parallel via upstream make test-nonflaky, with serial fallback for race-prone tests

tl;dr Starting with curl 8.17.0-1.amzn2023.0.2 in Amazon Linux 2023, you can now use HTTP/3.
dnf swap -y libcurl-minimal libcurl-full
dnf swap -y curl-minimal curl-full
curl --http3-only https://example.com
(HTTP/3 is only enabled in the curl -full builds) Or, if you would like to try it out in a container:
podman run amazonlinux:2023 /bin/sh -c 'dnf upgrade -y --releasever=latest && dnf swap -y libcurl-minimal libcurl-full && dnf swap -y curl-minimal curl-full && curl --http3-only https://example.com'
For a list of test endpoints, you can refer to https://bagder.github.io/HTTP3-test/

The Upgrade I Didn't Have to Make My teammate Steve Zarkos, who previously worked on upgrading OpenSSL in Amazon Linux from 3.0 to 3.2, spent the last few months on the complex task of bumping OpenSSL again, this time to 3.5. A bump like this only happens after extensive code analysis and testing, something that I didn't foresee happening when AL2023 was released but that was a notable request from users. Having enabled HTTP/3 on Debian, I was always keeping an eye on when I would get to do the same for Amazon Linux (mind you, I work at AWS, in the Amazon Linux org). The bump to OpenSSL 3.5 was the perfect opportunity to do that, for the first time Amazon Linux is shipping an OpenSSL version that is supported by ngtcp2 for HTTP/3 support.

Non-Intrusive Change In order to avoid any intrusive changes to existing users of AL2023, I've only enabled HTTP/3 in the full build of curl, not in the minimal one, this means there is no change for the minimal images. The way curl handles HTTP/3 today also does not lead to any behavior changes for those who have the full variants of curl installed, this is due to the fact that HTTP/3 is only used if the user explicitly asks for it with the flags --http3 or --http3-only.

Side Quests Supporting HTTP/3 on curl also requires building it with ngtcp2 and nghttp3, two packages which were not shipped in Amazon Linux, besides, my team doesn't even own the curl package, we are a security team so our packages are the security related stuff such as OpenSSL and GnuTLS. Our main focus is the services behind Amazon Linux's vulnerability handling, not package maintenance. I worked with the owners of the curl package and got approvals on a plan to introduce the two new dependencies under their ownership and to enable the feature on curl, I appreciate their responsiveness. Amazon Linux 2023 is forked from Fedora, so while introducing ngtcp2, I also sent a couple of Pull Requests upstream to keep things in sync: [ngtcp2] package latest release 1.21.0 [ngtcp2] do not skip tests While building the curl package in Amazon Linux, I've noticed the build was taking 1 hour from start to end, and the culprit was something well known to me; tests. The curl test suite is quite extensive, with more than 1600 tests, all of that running without parallelization, running two times for each build of the package; once for the minimal build and again for the full build. I had previously enabled parallel tests in Debian back in 2024 but never got around to submit the same improvements to Amazon Linux or Fedora, this is now fixed. The build times for Amazon Linux came down to 10 minutes under the same host (previously 1 hour), and Fedora promptly merged my PR to do the same there: [curl] run tests in parallel All of this uncovered a test which is timing-dependent, meaning it's not supposed to be run with high levels of parallelism, so there goes another PR, this time to curl: Flag test 766 as timing-dependent#21155 What started as enabling a single feature turned into improvements that landed in curl, Fedora, and Amazon Linux alike. I did this in a mix of work and volunteer time, mostly during work hours (work email address used when this was the case), but I'm glad I put in the extra time for the sake of improving curl for everyone.

Release Notes Amazon Linux 2023 release notes for 2023.10.20260330

25 March 2026

John Goerzen: Artificial Intelligence: Shades of Gray

AI sure is a hot topic right now, and I see a lot of people arguing about it. To a lot of people around here, I m the computer person they know and I get asked a lot about AI. I m going to suggest a lot of things can be true at once. For instance: Or how about: And: I have sympathy for the naysayers; those that say it s nothing but a stochastic parrot. But I don t have a lot of sympathy for the naysayers that deny ever using it; you can t form a credible argument against something without having an understanding of it informed by experience. I also have sympathy for the cheerleaders. I have seen some impressive things from AI; for instance, a story from an engineer who has a child with a rare disease without a credible cure. The engineer did a lot of research on it, started feeding research papers into AI to analyze, and the AI started finding correlations between different areas of research that humans hadn t yet found leading to a positive result for the child. To be fair, I have rarely seen an AI deliver a 100% correct answer on anything with any real level of complexity. I have seen it both waste more time than it saves, and save a ton of time. My point here is: It is neither always fantastic nor always terrible. Let me talk you through an example. I am a fan of inbox zero for email. That is, the inbox should be empty. Unfortunately, mine has 8000 messages in it. According to the oldest messages in my inbox, I last had inbox zero 8 years ago. But really, only a handful are older than 2020. I guess something must have happened that year I ve been chipping away at this for quite some time now. The problem is, there are certain emails in there that really do still need some action maybe it s photos to save off into our photo collection, for instance. But when looking at things sorted by date or thread, there are old shipping confirmations next to phishing attempts and family photos. One can t just scan down the list. I ve tried all the usual tricks, most of which involve selecting groups of message that are easy to bulk erase, or at least easy to scan visually for the occasional thing worth saving. Sort by sender or subject line, for instance. Then I can, for instance, delete all the old messages from the shopping sites I commonly use all at once. But then they start using different senders and different subject lines and that doesn t get all of them. I ve tried keyword searches for this sort of thing too. Still, that got me down to about 8000 messages. So I thought: why not see if an LLM could help me classify these? Maybe it could categorize them, and then I could look at emails grouped by category. I have one machine with a discrete GPU, an Nvidia RTX 4070. It s a desktop machine I don t use all that often. But I set up Ollama on it, running in a Docker container. Ollama runs models locally. I should also mention at this point that we are solar-powered, and this time of year is a time of peak production of excess solar, because it is sunny and not much heat or AC is required. So that machine is solar-powered and isn t causing environmental harm. In any case, charging the EV uses much more power than that GPU. I figured I would do this in two passes. First, ask the LLM to classify each message (or a sampling of them would probably work too), letting it pick its own categories for each. Then, look at the patterns that emerge and give it a single, much smaller, set of broad categories to use and rerun it over that. Then I can easily select messages from my Maildirs by category and process them in bulk. I used open-interpreter pointing to that GPU on my network to help me write the scripts for this. It didn t get things right on its own; for instance, it didn t call the Ollama API correctly, and insisted on appending /cur to the path to the Maildir (which was not going to fly with Python s maildir module). It took roughly an hour to classify those 8000 messages (or, as I had it do, the first 2000 characters of them), and then the same to do it a second time. I had it output lines in the form of filename\tcategory and hand-wrote the shell script that processed those. In the end, was it useful? Yes, quite. Its classifications weren t perfect (and it didn t even follow my prompt perfectly; sometimes it would give me a long discussion on why it picked a certain category rather than just that category, and occasionally it picked categories not on the list). But then, neither were my manual keyword searches. So far I ve gotten rid of nearly 1000 more messages. Several categories were a visual scan for sanity and then delete all sort of thing. My emails never left my network. I didn t rely on a cloud AI to process them. I didn t contribute to global warming (this may have even been a case of saving energy, since it no doubt will offset quite a bit of manual time that would keep screens and room lights energized and so forth). I used about as much energy as watching a movie on a TV. Did it complete the task for me entirely autonomously? Also no. AI isn t a mind reader and it can t possibly evaluate exactly what my thought process would be for a given task. But it can do a decent enough job to save me some time. Still, this didn t require hyperscaler datacenters. AI even runs on-phone (Google Translate being one of the most useful AI-driven apps I ve ever seen, and it can run on-device).

5 March 2026

Ian Jackson: Adopting tag2upload and modernising your Debian packaging

Introduction tag2upload allows authorised Debian contributors to upload to Debian simply by pushing a signed git tag to Debian s gitlab instance, Salsa. We have recently announced that tag2upload is, in our opinion, now very stable, and ready for general use by all Debian uploaders. tag2upload, as part of Debian s git transition programme, is very flexible - it needs to support a large variety of maintainer practices. And it s relatively unopinionated, wherever that s possible. But, during the open beta, various contributors emailed us asking for Debian packaging git workflow advice and recommendations. This post is an attempt to give some more opinionated answers, and guide you through modernising your workflow. (This article is aimed squarely at Debian contributors. Much of it will make little sense to Debian outsiders.) Why Ease of development git offers a far superior development experience to patches and tarballs. Moving tasks from a tarballs and patches representation to a normal, git-first, representation, makes everything simpler. dgit and tag2upload do automatically many things that have to be done manually, or with separate commands, in dput-based upload workflows. They will also save you from a variety of common mistakes. For example, you cannot accidentally overwrite an NMU, with tag2upload or dgit. These many safety catches mean that our software sometimes complains about things, or needs confirmation, when more primitive tooling just goes ahead. We think this is the right tradeoff: it s part of the great care we take to avoid our software making messes. Software that has your back is very liberating for the user. tag2upload makes it possible to upload with very small amounts of data transfer, which is great in slow or unreliable network environments. The other week I did a git-debpush over mobile data while on a train in Switzerland; it completed in seconds. See the Day-to-day work section below to see how simple your life could be. Don t fear a learning burden; instead, start forgetting all that nonsense Most Debian contributors have spent months or years learning how to work with Debian s tooling. You may reasonably fear that our software is yet more bizarre, janky, and mistake-prone stuff to learn. We promise (and our users tell us) that s not how it is. We have spent a lot of effort on providing a good user experience. Our new git-first tooling, especially dgit and tag2upload, is much simpler to use than source-package-based tooling, despite being more capable. The idiosyncrasies and bugs of source packages, and of the legacy archive, have been relentlessly worked around and papered over by our thousands of lines of thoroughly-tested defensive code. You too can forget all those confusing details, like our users have! After using our systems for a while you won t look back. And, you shouldn t fear trying it out. dgit and tag2upload are unlikely to make a mess. If something is wrong (or even doubtful), they will typically detect it, and stop. This does mean that starting to use tag2upload or dgit can involve resolving anomalies that previous tooling ignored, or passing additional options to reassure the system about your intentions. So admittedly it isn t always trivial to get your first push to succeed. Properly publishing the source code One of Debian s foundational principles is that we publish the source code. Nowadays, the vast majority of us, and of our upstreams, are using git. We are doing this because git makes our life so much easier. But, without tag2upload or dgit, we aren t properly publishing our work! Yes, we typically put our git branch on Salsa, and point Vcs-Git at it. However:
  • The format of git branches on Salsa is not standardised. They might be patches-unapplied, patches-applied, bare debian/, or something even stranger.
  • There is no guarantee that the DEP-14 debian/1.2.3-7 tag on salsa corresponds precisely to what was actually uploaded. dput-based tooling (such as gbp buildpackage) doesn t cross-check the .dsc against git.
  • There is no guarantee that the presence of a DEP-14 tag even means that that version of package is in the archive.
This means that the git repositories on Salsa cannot be used by anyone who needs things that are systematic and always correct. They are OK for expert humans, but they are awkward (even hazardous) for Debian novices, and you cannot use them in automation. The real test is: could you use Vcs-Git and Salsa to build a Debian derivative? You could not. tag2upload and dgit do solve this problem. When you upload, they:
  1. Make a canonical-form (patches-applied) derivative of your git branch;
  2. Ensure that there is a well-defined correspondence between the git tree and the source package;
  3. Publish both the DEP-14 tag and a canonical-form archive/debian/1.2.3-7 tag to a single central git depository, *.dgit.debian.org;
  4. Record the git information in the Dgit field in .dsc so that clients can tell (using the ftpmaster API) that this was a git-based upload, what the corresponding git objects are, and where to find them.
This dependably conveys your git history to users and downstreams, in a standard, systematic and discoverable way. tag2upload and dgit are the only system which achieves this. (The client is dgit clone, as advertised in e.g. dgit-user(7). For dput-based uploads, it falls back to importing the source package.) Adopting tag2upload - the minimal change tag2upload is a substantial incremental improvement to many existing workflows. git-debpush is a drop-in replacement for building, signing, and uploading the source package. So, you can just adopt it without completely overhauling your packaging practices. You and your co-maintainers can even mix-and-match tag2upload, dgit, and traditional approaches, for the same package. Start with the wiki page and git-debpush(1) (ideally from forky aka testing). You don t need to do any of the other things recommended in this article. Overhauling your workflow, using advanced git-first tooling The rest of this article is a guide to adopting the best and most advanced git-based tooling for Debian packaging. Assumptions
  • Your current approach uses the patches-unapplied git branch format used with gbp pq and/or quilt, and often used with git-buildpackage. You previously used gbp import-orig.
  • You are fluent with git, and know how to use Merge Requests on gitlab (Salsa). You have your origin remote set to Salsa.
  • Your main Debian branch name on Salsa is master. Personally I think we should use main but changing your main branch name is outside the scope of this article.
  • You have enough familiarity with Debian packaging including concepts like source and binary packages, and NEW review.
  • Your co-maintainers are also adopting the new approach.
tag2upload and dgit (and git-debrebase) are flexible tools and can help with many other scenarios too, and you can often mix-and-match different approaches. But, explaining every possibility would make this post far too confusing. Topics and tooling This article will guide you in adopting:
  • tag2upload
  • Patches-applied git branch for your packaging
  • Either plain git merge or git-debrebase
  • dgit when a with-binaries uploaded is needed (NEW)
  • git-based sponsorship
  • Salsa (gitlab), including Debian Salsa CI
Choosing the git branch format In Debian we need to be able to modify the upstream-provided source code. Those modifications are the Debian delta. We need to somehow represent it in git. We recommend storing the delta as git commits to those upstream files, by picking one of the following two approaches.
rationale Much traditional Debian tooling like quilt and gbp pq uses the patches-unapplied branch format, which stores the delta as patch files in debian/patches/, in a git tree full of unmodified upstream files. This is clumsy to work with, and can even be an alarming beartrap for Debian outsiders.
git merge Option 1: simply use git, directly, including git merge. Just make changes directly to upstream files on your Debian branch, when necessary. Use plain git merge when merging from upstream. This is appropriate if your package has no or very few upstream changes. It is a good approach if the Debian maintainers and upstream maintainers work very closely, so that any needed changes for Debian are upstreamed quickly, and any desired behavioural differences can be arranged by configuration controlled from within debian/. This is the approach documented more fully in our workflow tutorial dgit-maint-merge(7).
git-debrebase Option 2: Adopt git-debrebase. git-debrebase helps maintain your delta as linear series of commits (very like a topic branch in git terminology). The delta can be reorganised, edited, and rebased. git-debrebase is designed to help you carry a significant and complicated delta series. The older versions of the Debian delta are preserved in the history. git-debrebase makes extra merges to make a fast-forwarding history out of the successive versions of the delta queue branch. This is the approach documented more fully in our workflow tutorial dgit-maint-debrebase(7). Examples of complex packages using this approach include src:xen and src:sbcl.
Determine upstream git and stop using upstream tarballs We recommend using upstream git, only and directly. You should ignore upstream tarballs completely.
rationale Many maintainers have been importing upstream tarballs into git, for example by using gbp import-orig. But in reality the upstream tarball is an intermediate build product, not (just) source code. Using tarballs rather than git exposes us to additional supply chain attacks; indeed, the key activation part of the xz backdoor attack was hidden only in the tarball! git offers better traceability than so-called pristine upstream tarballs. (The word pristine is even a joke by the author of pristine-tar!)
First, establish which upstream git tag corresponds to the version currently in Debian. From the sake of readability, I m going to pretend that upstream version is 1.2.3, and that upstream tagged it v1.2.3. Edit debian/watch to contain something like this:
version=4
opts="mode=git" https://codeberg.org/team/package refs/tags/v(\d\S*)
You may need to adjust the regexp, depending on your upstream s tag name convention. If debian/watch had a files-excluded, you ll need to make a filtered version of upstream git.
git-debrebase From now on we ll generate our own .orig tarballs directly from git.
rationale We need some upstream tarball for the 3.0 (quilt) source format to work with. It needs to correspond to the git commit we re using as our upstream. We don t need or want to use a tarball from upstream for this. The .orig is just needed so a nice legacy Debian source package (.dsc) can be generated.
Probably, the current .orig in the Debian archive, is an upstream tarball, which may be different to the output of git-archive and possibly even have different contents to what s in git. The legacy archive has trouble with differing .origs for the same upstream version . So we must until the next upstream release change our idea of the upstream version number. We re going to add +git to Debian s idea of the upstream version. Manually make a tag with that name:
git tag -m "Compatibility tag for orig transition" v1.2.3+git v1.2.3~0
git push origin v1.2.3+git
If you are doing the packaging overhaul at the same time as a new upstream version, you can skip this part.
Convert the git branch
git merge Prepare a new branch on top of upstream git, containing what we want:
git branch -f old-master         # make a note of the old git representation
git reset --hard v1.2.3          # go back to the real upstream git tag
git checkout old-master :debian  # take debian/* from old-master
git commit -m "Re-import Debian packaging on top of upstream git"
git merge --allow-unrelated-histories -s ours -m "Make fast forward from tarball-based history" old-master
git branch -d old-master         # it's incorporated in our history now
If there are any patches, manually apply them to your main branch with git am, and delete the patch files (git rm -r debian/patches, and commit). (If you ve chosen this workflow, there should be hardly any patches,)
rationale These are some pretty nasty git runes, indeed. They re needed because we want to restart our Debian packaging on top of a possibly quite different notion of what the upstream is.
git-debrebase Convert the branch to git-debrebase format and rebase onto the upstream git:
git-debrebase -fdiverged convert-from-gbp upstream/1.2.3
git-debrebase -fdiverged -fupstream-not-ff new-upstream 1.2.3+git
If you had patches which patched generated files which are present only in the upstream tarball, and not in upstream git, you will encounter rebase conflicts. You can drop hunks editing those files, since those files are no longer going to be part of your view of the upstream source code at all.
rationale The force option -fupstream-not-ff will be needed this one time because your existing Debian packaging history is (probably) not based directly on the upstream history. -fdiverged may be needed because git-debrebase might spot that your branch is not based on dgit-ish git history.
Manually make your history fast forward from the git import of your previous upload.
dgit fetch
git show dgit/dgit/sid:debian/changelog
# check that you have the same version number
git merge -s ours --allow-unrelated-histories -m 'Declare fast forward from pre-git-based history' dgit/dgit/sid
Change the source format Delete any existing debian/source/options and/or debian/source/local-options.
git merge Change debian/source/format to 1.0. Add debian/source/options containing -sn.
rationale We are using the 1.0 native source format. This is the simplest possible source format - just a tarball. We would prefer 3.0 (native) , which has some advantages, but dpkg-source between 2013 (wheezy) and 2025 (trixie) inclusive unjustifiably rejects this configuration. You may receive bug reports from over-zealous folks complaining about the use of the 1.0 source format. You should close such reports, with a reference to this article and to #1106402.
git-debrebase Ensure that debian/source/format contains 3.0 (quilt).
Now you are ready to do a local test build. Sort out the documentation and metadata Edit README.source to at least mention dgit-maint-merge(7) or dgit-maint-debrebase(7), and to tell people not to try to edit or create anything in debian/patches/. Consider saying that uploads should be done via dgit or tag2upload. Check that your Vcs-Git is correct in debian/control. Consider deleting or pruning debian/gbp.conf, since it isn t used by dgit, tag2upload, or git-debrebase.
git merge Add a note to debian/changelog about the git packaging change.
git-debrebase git-debrebase new-upstream will have added a new upstream version stanza to debian/changelog. Edit that so that it instead describes the packaging change. (Don t remove the +git from the upstream version number there!)
Configure Salsa Merge Requests
git-debrebase In Settings / Merge requests , change Squash commits when merging to Do not allow .
rationale Squashing could destroy your carefully-curated delta queue. It would also disrupt git-debrebase s git branch structure.
Set up Salsa CI, and use it to block merges of bad changes Caveat - the tradeoff gitlab is a giant pile of enterprise crap. It is full of startling bugs, many of which reveal a fundamentally broken design. It is only barely Free Software in practice for Debian (in the sense that we are very reluctant to try to modify it). The constant-churn development approach and open-core business model are serious problems. It s very slow (and resource-intensive). It can be depressingly unreliable. That Salsa works as well as it does is a testament to the dedication of the Debian Salsa team (and those who support them, including DSA). However, I have found that despite these problems, Salsa CI is well worth the trouble. Yes, there are frustrating days when work is blocked because gitlab CI is broken and/or one has to keep mashing Retry . But, the upside is no longer having to remember to run tests, track which of my multiple dev branches tests have passed on, and so on. Automatic tests on Merge Requests are a great way of reducing maintainer review burden for external contributions, and helping uphold quality norms within a team. They re a great boon for the lazy solo programmer. The bottom line is that I absolutely love it when the computer thoroughly checks my work. This is tremendously freeing, precisely at the point when one most needs it deep in the code. If the price is to occasionally be blocked by a confused (or broken) computer, so be it. Setup procedure Create debian/salsa-ci.yml containing
include:
  - https://salsa.debian.org/salsa-ci-team/pipeline/raw/master/recipes/debian.yml
In your Salsa repository, under Settings / CI/CD , expand General Pipelines and set CI/CD configuration file to debian/salsa-ci.yml.
rationale Your project may have an upstream CI config in .gitlab-ci.yml. But you probably want to run the Debian Salsa CI jobs. You can add various extra configuration to debian/salsa-ci.yml to customise it. Consult the Salsa CI docs.
git-debrebase Add to debian/salsa-ci.yml:
.git-debrebase-prepare: &git-debrebase-prepare
  # install the tools we'll need
  - apt-get update
  - apt-get --yes install git-debrebase git-debpush
  # git-debrebase needs git user setup
  - git config user.email "salsa-ci@invalid.invalid"
  - git config user.name "salsa-ci"
  # run git-debrebase make-patches
  # https://salsa.debian.org/salsa-ci-team/pipeline/-/issues/371
  - git-debrebase --force
  - git-debrebase --noop-ok make-patches
  # make an orig tarball using the upstream tag, not a gbp upstream/ tag
  # https://salsa.debian.org/salsa-ci-team/pipeline/-/issues/541
  - git-deborig
.build-definition: &build-definition
  extends: .build-definition-common
  before_script: *git-debrebase-prepare
build source:
  extends: .build-source-only
  before_script: *git-debrebase-prepare
variables:
  # disable shallow cloning of git repository. This is needed for git-debrebase
  GIT_DEPTH: 0
rationale Unfortunately the Salsa CI pipeline currently lacks proper support for git-debrebase (salsa-ci#371) and has trouble directly using upstream git for orig tarballs (#salsa-ci#541). These runes were based on those in the Xen package. You should subscribe to the tickets #371 and #541 so that you can replace the clone-and-hack when proper support is merged.
Push this to salsa and make the CI pass. If you configured the pipeline filename after your last push, you will need to explicitly start the first CI run. That s in Pipelines : press New pipeline in the top right. The defaults will very probably be correct. Block untested pushes, preventing regressions In your project on Salsa, go into Settings / Repository . In the section Branch rules , use Add branch rule . Select the branch master. Set Allowed to merge to Maintainers . Set Allowed to push and merge to No one . Leave Allow force push disabled. This means that the only way to land anything on your mainline is via a Merge Request. When you make a Merge Request, gitlab will offer Set to auto-merge . Use that. gitlab won t normally merge an MR unless CI passes, although you can override this on a per-MR basis if you need to. (Sometimes, immediately after creating a merge request in gitlab, you will see a plain Merge button. This is a bug. Don t press that. Reload the page so that Set to auto-merge appears.) autopkgtests Ideally, your package would have meaningful autopkgtests (DEP-8 tests) This makes Salsa CI more useful for you, and also helps detect and defend you against regressions in your dependencies. The Debian CI docs are a good starting point. In-depth discussion of writing autopkgtests is beyond the scope of this article. Day-to-day work With this capable tooling, most tasks are much easier. Making changes to the package Make all changes via a Salsa Merge Request. So start by making a branch that will become the MR branch. On your MR branch you can freely edit every file. This includes upstream files, and files in debian/. For example, you can:
  • Make changes with your editor and commit them.
  • git cherry-pick an upstream commit.
  • git am a patch from a mailing list or from the Debian Bug System.
  • git revert an earlier commit, even an upstream one.
When you have a working state of things, tidy up your git branch:
git merge Use git-rebase to squash/edit/combine/reorder commits.
git-debrebase Use git-debrebase -i to squash/edit/combine/reorder commits. When you are happy, run git-debrebase conclude. Do not edit debian/patches/. With git-debrebase, this is purely an output. Edit the upstream files directly instead. To reorganise/maintain the patch queue, use git-debrebase -i to edit the actual commits.
Push the MR branch (topic branch) to Salsa and make a Merge Request. Set the MR to auto-merge when all checks pass . (Or, depending on your team policy, you could ask for an MR Review of course.) If CI fails, fix up the MR branch, squash/tidy it again, force push the MR branch, and once again set it to auto-merge. Test build An informal test build can be done like this:
apt-get build-dep .
dpkg-buildpackage -uc -b
Ideally this will leave git status clean, with no modified or un-ignored untracked files. If it shows untracked files, add them to .gitignore or debian/.gitignore as applicable. If it dirties the tree, consider trying to make it stop doing that. The easiest way is probably to build out-of-tree, if supported upstream. If this is too difficult, you can leave the messy build arrangements as they are, but you ll need to be disciplined about always committing, using git clean and git reset, and so on. For formal binaries builds, including for testing, use dgit sbuild as described below for uploading to NEW. Uploading to Debian Start an MR branch for the administrative changes for the release. Document all the changes you re going to release, in the debian/changelog.
git merge gbp dch can help write the changelog for you:
dgit fetch sid
gbp dch --ignore-branch --since=dgit/dgit/sid --git-log=^upstream/main
rationale --ignore-branch is needed because gbp dch wrongly thinks you ought to be running this on master, but of course you re running it on your MR branch. The --git-log=^upstream/main excludes all upstream commits from the listing used to generate the changelog. (I m assuming you have an upstream remote and that you re basing your work on their main branch.) If there was a new upstream version, you ll usually want to write a single line about that, and perhaps summarise anything really important.
(For the first upload after switching to using tag2upload or dgit you need --since=debian/1.2.3-1, where 1.2.3-1 is your previous DEP-14 tag, because dgit/dgit/sid will be a dsc import, not your actual history.)
Change UNRELEASED to the target suite, and finalise the changelog. (Note that dch will insist that you at least save the file in your editor.)
dch -r
git commit -m 'Finalise for upload' debian/changelog
Make an MR of these administrative changes, and merge it. (Either set it to auto-merge and wait for CI, or if you re in a hurry double-check that it really is just a changelog update so that you can be confident about telling Salsa to Merge unverified changes .) Now you can perform the actual upload:
git checkout master
git pull --ff-only # bring the gitlab-made MR merge commit into your local tree
git merge
git-debpush
git-debrebase
git-debpush --quilt=linear
--quilt=linear is needed only the first time, but it is very important that first time, to tell the system the correct git branch layout.
Uploading a NEW package to Debian If your package is NEW (completely new source, or has new binary packages) you can t do a source-only upload. You have to build the source and binary packages locally, and upload those build artifacts. Happily, given the same git branch you d tag for tag2upload, and assuming you have sbuild installed and a suitable chroot, dgit can help take care of the build and upload for you: Prepare the changelog update and merge it, as above. Then:
git-debrebase Create the orig tarball and launder the git-derebase branch:
git-deborig
git-debrebase quick
rationale Source package format 3.0 (quilt), which is what I m recommending here for use with git-debrebase, needs an orig tarball; it would also be needed for 1.0-with-diff.
Build the source and binary packages, locally:
dgit sbuild
dgit push-built
rationale You don t have to use dgit sbuild, but it is usually convenient to do so, because unlike sbuild, dgit understands git. Also it works around a gitignore-related defect in dpkg-source.
New upstream version Find the new upstream version number and corresponding tag. (Let s suppose it s 1.2.4.) Check the provenance:
git verify-tag v1.2.4
rationale Not all upstreams sign their git tags, sadly. Sometimes encouraging them to do so can help. You may need to use some other method(s) to check that you have the right git commit for the release.
git merge Simply merge the new upstream version and update the changelog:
git merge v1.2.4
dch -v1.2.4-1 'New upstream release.'
git-debrebase Rebase your delta queue onto the new upstream version:
git debrebase mew-upstream 1.2.4
If there are conflicts between your Debian delta for 1.2.3, and the upstream changes in 1.2.4, this is when you need to resolve them, as part of git merge or git (deb)rebase. After you ve completed the merge, test your package and make any further needed changes. When you have it working in a local branch, make a Merge Request, as above. Sponsorship git-based sponsorship is super easy! The sponsee can maintain their git branch on Salsa, and do all normal maintenance via gitlab operations. When the time comes to upload, the sponsee notifies the sponsor that it s time. The sponsor fetches and checks out the git branch from Salsa, does their checks, as they judge appropriate, and when satisfied runs git-debpush. As part of the sponsor s checks, they might want to see all changes since the last upload to Debian:
dgit fetch sid
git diff dgit/dgit/sid..HEAD
Or to see the Debian delta of the proposed upload:
git verify-tag v1.2.3
git diff v1.2.3..HEAD ':!debian'
git-debrebase Or to show all the delta as a series of commits:
git log -p v1.2.3..HEAD ':!debian'
Don t look at debian/patches/. It can be absent or out of date.
Incorporating an NMU Fetch the NMU into your local git, and see what it contains:
dgit fetch sid
git diff master...dgit/dgit/sid
If the NMUer used dgit, then git log dgit/dgit/sid will show you the commits they made. Normally the best thing to do is to simply merge the NMU, and then do any reverts or rework in followup commits:
git merge dgit/dgit/sid
git-debrebase You should git-debrebase quick at this stage, to check that the merge went OK and the package still has a lineariseable delta queue.
Then make any followup changes that seem appropriate. Supposing your previous maintainer upload was 1.2.3-7, you can go back and see the NMU diff again with:
git diff debian/1.2.3-7...dgit/dgit/sid
git-debrebase The actual changes made to upstream files will always show up as diff hunks to those files. diff commands will often also show you changes to debian/patches/. Normally it s best to filter them out with git diff ... ':!debian/patches' If you d prefer to read the changes to the delta queue as an interdiff (diff of diffs), you can do something like
git checkout debian/1.2.3-7
git-debrebase --force make-patches
git diff HEAD...dgit/dgit/sid -- :debian/patches
to diff against a version with debian/patches/ up to date. (The NMU, in dgit/dgit/sid, will necessarily have the patches already up to date.)
DFSG filtering (handling non-free files) Some upstreams ship non-free files of one kind of another. Often these are just in the tarballs, in which case basing your work on upstream git avoids the problem. But if the files are in upstream s git trees, you need to filter them out. This advice is not for (legally or otherwise) dangerous files. If your package contains files that may be illegal, or hazardous, you need much more serious measures. In this case, even pushing the upstream git history to any Debian service, including Salsa, must be avoided. If you suspect this situation you should seek advice, privately and as soon as possible, from dgit-owner@d.o and/or the DFSG team. Thankfully, legally dangerous files are very rare in upstream git repositories, for obvious reasons. Our approach is to make a filtered git branch, based on the upstream history, with the troublesome files removed. We then treat that as the upstream for all of the rest of our work.
rationale Yes, this will end up including the non-free files in the git history, on official Debian servers. That s OK. What s forbidden is non-free material in the Debianised git tree, or in the source packages.
Initial filtering
git checkout -b upstream-dfsg v1.2.3
git rm nonfree.exe
git commit -m "upstream version 1.2.3 DFSG-cleaned"
git tag -s -m "upstream version 1.2.3 DFSG-cleaned" v1.2.3+ds1
git push origin upstream-dfsg
And now, use 1.2.3+ds1, and the filtered branch upstream-dfsg, as the upstream version, instead of 1.2.3 and upstream/main. Follow the steps for Convert the git branch or New upstream version, as applicable, adding +ds1 into debian/changelog. If you missed something and need to filter out more a nonfree files, re-use the same upstream-dfsg branch and bump the ds version, eg v1.2.3+ds2. Subsequent upstream releases
git checkout upstream-dfsg
git merge v1.2.4
git rm additional-nonfree.exe # if any
git commit -m "upstream version 1.2.4 DFSG-cleaned"
git tag -s -m "upstream version 1.2.4 DFSG-cleaned" v1.2.4+ds1
git push origin upstream-dfsg
Removing files by pattern If the files you need to remove keep changing, you could automate things with a small shell script debian/rm-nonfree containing appropriate git rm commands. If you use git rm -f it will succeed even if the git merge from real upstream has conflicts due to changes to non-free files.
rationale Ideally uscan, which has a way of representing DFSG filtering patterns in debian/watch, would be able to do this, but sadly the relevant functionality is entangled with uscan s tarball generation.
Common issues
  • Tarball contents: If you are switching from upstream tarballs to upstream git, you may find that the git tree is significantly different. It may be missing files that your current build system relies on. If so, you definitely want to be using git, not the tarball. Those extra files in the tarball are intermediate built products, but in Debian we should be building from the real source! Fixing this may involve some work, though.
  • gitattributes: For Reasons the dgit and tag2upload system disregards and disables the use of .gitattributes to modify files as they are checked out. Normally this doesn t cause a problem so long as any orig tarballs are generated the same way (as they will be by tag2upload or git-deborig). But if the package or build system relies on them, you may need to institute some workarounds, or, replicate the effect of the gitattributes as commits in git.
  • git submodules: git submodules are terrible and should never ever be used. But not everyone has got the message, so your upstream may be using them. If you re lucky, the code in the submodule isn t used in which case you can git rm the submodule.
Further reading I ve tried to cover the most common situations. But software is complicated and there are many exceptions that this article can t cover without becoming much harder to read. You may want to look at:
  • dgit workflow manpages: As part of the git transition project, we have written workflow manpages, which are more comprehensive than this article. They re centered around use of dgit, but also discuss tag2upload where applicable. These cover a much wider range of possibilities, including (for example) choosing different source package formats, how to handle upstreams that publish only tarballs, etc. They are correspondingly much less opinionated. Look in dgit-maint-merge(7) and dgit-maint-debrebase(7). There is also dgit-maint-gbp(7) for those who want to keep using gbp pq and/or quilt with a patches-unapplied branch.
  • NMUs are very easy with dgit. (tag2upload is usually less suitable than dgit, for an NMU.) You can work with any package, in git, in a completely uniform way, regardless of maintainer git workflow, See dgit-nmu-simple(7).
  • Native packages (meaning packages maintained wholly within Debian) are much simpler. See dgit-maint-native(7).
  • tag2upload documentation: The tag2upload wiki page is a good starting point. There s the git-debpush(1) manpage of course.
  • dgit reference documentation: There is a comprehensive command-line manual in dgit(1). Description of the dgit data model and Principles of Operation is in dgit(7); including coverage of out-of-course situations. dgit is a complex and powerful program so this reference material can be overwhelming. So, we recommend starting with a guide like this one, or the dgit- (7) workflow tutorials.
  • Design and implementation documentation for tag2upload is linked to from the wiki.
  • Debian s git transition blog post from December. tag2upload and dgit are part of the git transition project, and aim to support a very wide variety of git workflows. tag2upload and dgit work well with existing git tooling, including git-buildpackage-based approaches. git-debrebase is conceptually separate from, and functionally independent of, tag2upload and dgit. It s a git workflow and delta management tool, competing with gbp pq, manual use of quilt, git-dpm and so on.
git-debrebase
  • git-debrebase reference documentation: Of course there s a comprehensive command-line manual in git-debrebase(1). git-debrebase is quick and easy to use, but it has a complex data model and sophisticated algorithms. This is documented in git-debrebase(5).

Edited 2026-03-05 18:48 UTC to add a missing --noop-ok to the Salsa CI runes. Thanks to Charlemagne Lasse for the report. Apologies if this causes Debian Planet to re-post this article as if it were new.


comment count unavailable comments

2 March 2026

Ben Hutchings: FOSS activity in February 2026

27 February 2026

Petter Reinholdtsen: Free software toolchain for the simplest RISC-V CPU in a small FPGA?

On Wednesday I had the pleasure of attending a presentation organized by the Norwegian Unix Users Group on implementing RISC-V using a small FPGA. This project is the result of a university teacher wanting to teach students assembly programming using a real instruction set, while still providing a simple and transparent CPU environment. The CPU in question implements the smallest set of opcodes needed to still call the CPU a RISC-V CPU, the RV32I base set. The author and presenter, Kristoffer Robin Stokke, demonstrated how to build both the FPGA setup and a small startup code providing a "Hello World" message over both serial port and a small LCD display. The FPGA is programmed using VHDL, the entire source code is available from github, but unfortunately the target FPGA setup is compiled using the proprietary tool Quartus. It is such a pity that such a cool little piece of free software should be chained down by non-free software, so my friend Jon Nordby set out to see if we can liberate this small RISC-V CPU. After all, it would be unforgivable sin to force students to use non-free software to study at the University of Oslo. The VHDL code for the CPU instructions itself is only 1138 lines, if I am to believe wc -l lib/riscv_common/* lib/rv32i/*. On the small FPGA used during the talk, the entire CPU, ROM, display and serial port driver only used up half the capacity. These days, there exists a free software toolchain for FPGA programming not only in Verilog but also in VHDL, and we hope the support in yosys, ghdl, and yosys-plugin-ghdl (sadly and strangely enough, removed from Debian unstable) is complete enough to at least build this small and simple project with some minor portability fixes. Or perhaps there are other approaches that work better? The first patches are already floating on github, to make the VHDL code more portable and to test out the build. If you are interested in running your own little RISC-V CPU on a FPGA chip, please get in touch. At the moment we sadly have hit a GHDL bug, which we do not quite know how to work around or fix:
******************** GHDL Bug occurred ***************************
Please report this bug on https://github.com/ghdl/ghdl/issues
GHDL release: 5.0.1 (Debian 5.0.1+dfsg-1+b1) [Dunoon edition]
Compiled with unknown compiler version
Target: x86_64-linux-gnu
/scratch/pere/src/fpga/memstick-fpga-riscv-upstream/
Command line:
Exception CONSTRAINT_ERROR raised
Exception information:
raised CONSTRAINT_ERROR : synth-vhdl_expr.adb:1763 discriminant check failed
******************************************************************
Thus more work is needed. For me, this simple project is the first stepping stone for a larger dream I have of converting the MESA machine controller system to build its firmware using a free software toolchain. I just need to learn more FPGA programming first. :) As usual, if you use Bitcoin and want to show your support of my activities, please send Bitcoin donations to my address 15oWEoG9dUPovwmUL9KWAnYRtNJEkP1u1b.

24 February 2026

John Goerzen: Screen Power Saving in the Linux Console

I just made up a Debian trixie setup that has no need for a GUI. In fact, I rarely use the text console either. However, because the machine is dual boot and also serves another purpose, it s connected to my main monitor and KVM switch. The monitor has three inputs, and when whatever display it s set to goes into powersave mode, it will seek out another one that s active and automatically switch to it. You can probably see where this is heading: it s really inconvenient if one of the inputs never goes into powersave mode. And, of course, it wastes energy. I have concluded that the Linux text console has lost the ability to enter powersave mode after an inactivity timeout. It can still do screen blanking setting every pixel to black but that is a distinct and much less useful thing. You can do a lot of searching online that will tell you what to do. Almost all of it is wrong these days. For instance, none of these work: Why is this? Well, we are on at least the third generation of Linux text console display subsystems. (Maybe more than 3, depending on how you count.) The three major ones were:
  1. The VGA text console
  2. fbdev
  3. DRI/KMS
As I mentioned recently in my post about running an accurate 80 25 DOS-style console on modern Linux, the VGA text console mode is pretty much gone these days. It relied on hardware rendering of the text fonts, and that capability simply isn t present on systems that aren t PCs or even on PCs that are UEFI, which is most of them now. fbdev, or a framebuffer console under earlier names, has been in Linux since the late 1990s. It was the default for most distros until more recently. It supported DPMS powersave modes, and most of the instructions you will find online reference it. Nowadays, the DRI/KMS system is used for graphics. Unfortunately, it is targeted mainly at X11 and Wayland. It is also used for the text console, but things like DPMS-enabled timeouts were never implemented there. You can find some manual workarounds for instance, using ddcutil or similar for an external monitor, or adjusting the backlight files under /sys on a laptop. But these have a number of flaws making unwanted brightness adjustments, and not automatically waking up on keypress among them. My workaround I finally gave up and ran apt-get install xdm. Then in /etc/X11/xdm/Xsetup, I added one line:
xset dpms 0 0 120
Now the system boots into an xdm login screen, and shuts down the screen after 2 minutes of inactivity. On the rare occasion where I want a text console from it, I can switch to it and it won t have a timeout, but I can live with that. Thus, quite hopefully, concludes my series of way too much information about the Linux text console!

22 February 2026

Otto Kek l inen: Do AI models still keep getting better, or have they plateaued?

Featured image of post Do AI models still keep getting better, or have they plateaued?The AI hype is based on the assumption that the frontier AI labs are producing better and better foundational models at an accelerating pace. Is that really true, or are people just in sort of a mass psychosis because AI models have become so good at mimicking human behavior that we unconsciously attribute increasing intelligence to them? I decided to conduct a mini-benchmark of my own to find out if the latest and greatest AI models are actually really good or not.

The problem with benchmarks Every time any team releases a new LLM, they boast how well it performs on various industry benchmarks such as Humanity s Last Exam, SWE-Bench and Ai2 ARC or ARC-AGI. An overall leaderboard can be viewed at LLM-stats. This incentivizes teams to optimize for specific benchmarks, which might make them excel on specific tasks while general abilities degrade. Also, the older a benchmark dataset is, the more online material there is discussing the questions and best answers, which in turn increases the chances of newer models trained on more recent web content scoring better. Thus I prefer looking at real-time leaderboards such as the LM Arena leaderboard (or OpenCompass for Chinese models that might be missing from LM Arena). However, even though the LM Arena Elo score is rated by humans in real-time, the benchmark can still be played. For example, Meta reportedly used a special chat-optimized model instead of the actual Llama 4 model when getting scored on the LM Arena. Therefore I trust my own first-hand experience more than the benchmarks for gaining intuition. Intuition however is not a compelling argument in discussions on whether or not new flagship AI models have plateaued. Thus, I decided to devise my own mini-benchmark so that no model could have possibly seen it in its training data or be specifically optimized for it in any way.

My mini-benchmark I crafted 6 questions based on my own experience using various LLMs for several years and having developed some intuition about what kinds of questions LLMs typically struggle with. I conducted the benchmark using the OpenRouter.ai chat playroom with the following state-of-the-art models: OpenRouter.ai is great as it very easy to get responses from multiple models in parallel to a single question. Also it allows to turn off web search to force the models to answer purely based on their embedded knowledge. OpenRouter.ai Chat playroom Common for all the test questions is that they are fairly straightforward and have a clear answer, yet the answer isn t common knowledge or statistically the most obvious one, and instead requires a bit of reasoning to get correct. Some of these questions are also based on myself witnessing a flagship model failing miserably to answer it.

1. Which cities have hosted the Olympics more than just once? This question requires accounting for both summer and winter Olympics, and for Olympics hosted across multiple cities. The variance in responses comes from if the model understands that Beijing should be counted as it has hosted both summer and winter Olympics. Interestingly GPT was the only model to not mention Beijing at all. Some variance also comes from how models account for co-hosted Olympics. For example Cortina should be counted as having hosted the Olympics twice, in 1956 and 2026, but only Claude, Gemini and Kimi pointed this out. Stockholm s 1956 hosting of the equestrian games during the Melbourne Olympics is a special case, which GPT, Gemini and Kimi pointed out in a side note. Some models seem to have old training material, and for example Grok assumes the current year is 2024. All models that accounted for awarded future Olympics (e.g. Los Angeles 2028) marked them clearly as upcoming. Overall I would judge that only GPT and MinMax gave incomplete answers, while all other models replied as the best humans could reasonably have.

2. If EUR/USD continues to slide to 1.5 by mid-2026, what is the likely effect on BMW s stock price by end of 2026? This question requires mapping the currency exchange rate to historic value, dodging the misleading word slide , and reasoning on where the revenue of a company comes from and how a weaker US dollar affects it in multiple ways. I ve frequently witnessed flagship models get it wrong how interest rates and exchange rates work. Apparently the binary choice between up or down is somehow challenging to the internal statistical model in the LLMs on a topic where there are a lot of training material that talk about both things being likely to happen, and choosing between them requires specifically reasoning about the scenario at hand and disregarding general knowledge of the situation. However, this time all the models concluded correctly that a weak dollar would have a negative overall effect on the BMW stock price. Gemini, GLM, Qwen and Kimi also mention the potential hedging effect of BMW s X-series production in South Carolina for worldwide export.

3. What is the Unicode code point for the traffic cone emoji? This was the first question where the the flagship models clearly still struggle in 2026. The trap here is that there is no traffic cone emoji, so an advanced model should simply refuse to give any Unicode numbers at all. Most LLMs however have an urge to give some answer, leading to hallucinations. Also, as the answer has a graphical element to it, the LLM might not understand how the emoji looks in ways that would be obvious to a human, and thus many models claim the construction sign emoji is a traffic cone, which it is not. By far the worst response was from GPT, that simply hallucinates and stops there: OpenAIs GPT-5.2 completely wrong answer to traffic cone emoji question While Gemini and Grok were among the three models not falling into this trap, the response from Claude was exemplary good: Claude Opus 4.6 exemplary good answer to traffic cone emoji question

4. Which languages are the 10 most commonly spoken in the world? For each language, count from 1 to 5. This seemingly simple question can become quite complex when you account for the fact that other languages might also have their own character system, both for words and the numerals (e.g. Arabic /one vs /1), and that numbers might have variations depending on what the thing being counted is (e.g in French object gender un/une), or whether plain numbers and occurrences have their own words (e.g. Russian /odin vs /raz). I ve also witnessed older models frequently mix up on what parts of the reply should be using what language. For a human this question is trivial, assuming they know the language. For LLMs it is surprisingly challenging. Grok and MinMax only gave a list of the most popular languages and stopped there without showing any words in the languages. Qwen got a bit further by trying to list numbers in each language, but the lists were only the numbers 1 to 5: Qwen3.5 Plus count to five in top 10 languages I have seen Grok do something similar before, which in fact inspired me to include this question in my mini-benchmark. GPT got a bit further, but for Hindi, Arabic and Bengali it listed the numerals in local script, not the number words. Gemini, GLM and Kimi gave a complete and correct answer as a list, while the absolute best answer and presentation was by Claude, that gave the table below:
Claude Opus 4.6 10 Most Commonly Spoken Languages (by Total Speakers)
# Language 1 2 3 4 5
1 English One Two Three Four Five
2 Mandarin Chinese (y ) ( r) (s n) (s ) (w )
3 Hindi (ek) (do) (t n) (ch r) (p ch)
4 Spanish Uno Dos Tres Cuatro Cinco
5 French Un Deux Trois Quatre Cinq
6 Arabic (w id) (ithn n) (thal tha) (arba a) (khamsa)
7 Bengali (ek) (dui) (tin) (ch r) (p ch)
8 Portuguese Um Dois Tr s Quatro Cinco
9 Russian (odin) (dva) (tri) (chetyre) (pyat')
10 Japanese (ichi) (ni) (san) (shi) (go)
Note: Rankings are based on estimated total speakers (native + non-native). Rankings can vary depending on the source and methodology used.

5. Count the number of drone launch pads in the picture. Together with the question, I uploaded this picture: Drone pad picture attached to question above A human can easily count that there are 10 rows and 30+ columns in the grid, but because the picture resolution isn t good enough, the exact number of columns can t be counted, and the answer should be that there are at least 300 launch pads in the picture. GPT and Grok both guessed the count is zero. Instead of hallucinating some number they say zero, but it would have been better to not give any number at all, and just state that they are unable to perform the task. Gemini gave as its answer 101 , which is quite odd, but reading the reasoning section, it seems to have tried counting items in the image without reasoning much about what it is actually counting and that there is clearly a grid that can make the counting much easier. Both Qwen and Kimi state they can see four parallel structures, but are unable to count drone launch pads. The absolutely best answer was given by Claude, which counted 10-12 rows and 30-40+ columns, and concluded that there must be 300-500 drone launch pads. Very close to best human level - impressive! This question applied only to multi-modal models that can see images, so GLM and MinMax could not give any response.

6. Explain why I am getting the error below, and what is the best way to fix it? Together with the question above, I gave this code block:
$ SH_SCRIPTS="$(mktemp; grep -Irnw debian/ -e '^#!.*/sh'   sort -u   cut -d ':' -f 1   true)"
$ shellcheck -x --enable=all --shell=sh "$SH_SCRIPTS"
/tmp/tmp.xQOpI5Nljx
debian/tests/integration-tests: /tmp/tmp.xQOpI5Nljx
debian/tests/integration-tests: openBinaryFile: does not exist (No such file or directory)
Older models would easily be misled by the last error message thinking that a file went missing, and focus on suggesting changes to the complex-looking first line. In reality the error is simply caused by having the quotes around the $SH_SCRIPTS, resulting in the entire multi-line string being passed as a single argument to shellcheck. So instead of receiving two separate file paths, shellcheck tries to open one file literally named /tmp/tmp.xQOpI5Nljx\ndebian/tests/integration-tests. Incorrect argument expansion is fairly easy for an experienced human programmer to notice, but tricky for an LLM. Indeed, Grok, MinMax, and Qwen fell for this trap and focused on the mktemp, assuming it somehow fails to create a file. Interestingly GLM fails to produce an answer at all, as the reasoning step seems to be looping, thinking too much about the missing file, but not understanding why it would be missing when there is nothing wrong with how mktemp is executed. Claude, Gemini, and Kimi immediately spot the real root cause of passing the variable quoted and suggested correct fixes that involve either removing the quotes, or using Bash arrays or xargs in a way that makes the whole command also handle correctly filenames with spaces in them.

Conclusion
Model Sports Economics Emoji Languages Visual Shell Score
Claude Opus 4.6 6/6
GPT-5.2 ~ 2.5/6
Grok 4.1 3/6
Gemini 3.1 Pro 5/6
GLM 5 ? N/A 3/5
MinMax M2.5 N/A 1/5
Qwen3.5 Plus ~ 2.5/6
Kimi K2.5 4/6
Obviously, my mini-benchmark only had 6 questions, and I ran it only once. This was obviously not scientifically rigorous. However it was systematic enough to trump just a mere feeling. The main finding for me personally is that Claude Opus 4.6, the flagship model by Anthropic, seems to give great answers consistently. The answers are not only correct, but also well scoped giving enough information to cover everything that seems relevant, without blurping unnecessary filler. I used Claude extensively in 2023-2024 when it was the main model available at my day work, but for the past year I had been using other models that I felt were better at the time. Now Claude seems to be the best-of-the-best again, with Gemini and Kimi as close follow-ups. Comparing their pricing at OpenRouter.ai the Kimi K2.5 price of $0.6 / million tokens is almost 90% cheaper than the Claude Opus 4.6 s $5.0 / million tokens suggests that Kimi K2.5 offers the best price-per-performance ratio. Claude might be cheaper with a monthly subscription directly from Anthropic, potentially narrowing the price gap. Overall I do feel that Anthropic, Google and Moonshot.ai have been pushing the envelope with their latest models in a way that one can t really claim that AI models have plateaued. In fact, one could claim that at least Claude has now climbed over the hill of AI slop and consistently produces valuable results. If and when AI usage expands from here, we might actually not drown in AI slop as chances of accidentally crappy results decrease. This makes me positive about the future. I am also really happy to see that there wasn t just one model crushing everybody else, but that there are at least three models doing very well. As an open source enthusiast I am particularly glad to see that Moonshot.ai s Kimi K2.5 is published with an open license. Given the hardware, anyone can run it on their own. OpenRouter.ai currently lists 9 independent providers alongside Moonshot.ai itself, showcasing the potential of open-weight models in practice. If the pattern holds and flagship models continue improving at this pace we might look back at 2026 as the year AI stopped feeling like a call center associate and started to resemble a scientific researcher. While new models become available we need to keep testing, keep questioning, and keep our expectations grounded in actual performance rather than press releases. Thanks to OpenRouter.ai for providing a great service that makes testing various models incredibly easy!

19 February 2026

Peter Pentchev: Ringlet release: fnmatch-regex 0.3.0

Version 0.3.0 of the fnmatch-regex Rust crate is now available. The major new addition is the glob_to_regex_pattern function that only converts the glob pattern to a regular expression one without building a regular expression matcher. Two new features - regex and std - are also added, both enabled by default. For more information, see the changelog at the homepage.

Next.