The twenty-second release of the qlcal package
arrivied at CRAN yesterday, and
has been built for r2u
as well as on r-universe.
qlcal
delivers the calendaring parts of QuantLib. It is provided (for the R
package) as a set of included files, so the package is self-contained
and does not depend on an external QuantLib library (which can be
demanding to build). qlcal covers
over seventy country / market calendars and can compute holiday lists,
its complement (i.e. business day lists) and much more.
Examples are in the README at the repository, the package page,
and course at the CRAN package
page.
This release is based on the changes in the QuantLib 1.44 release also made
yesterday. It includes two new calendars for Malaysia and the
Philippines, updates to around a dozen calendars, and a few other
updates to internal files. Maxmilian Muecke spotted and
fixed a few small bugs just as he did for the RQuantLib release we made
yesterday;
this is equally appreciated! The full details from NEWS.Rd
follow.
Changes in version 0.1.4
(2026-10-13)
Add missing market calendars for Poland, Russia, Romania,
Indonesia (Maximilian Muecke in #21)
Add (semi-deprecated) alternate separators '::' for Israel and
New Zealand markets (Maximilian Muecke in #22)
Fix several typos across source, configuration and documentation
files (Maximilian Muecke in #23)
Synchronize with QuantLib 1.44, add two new calendars, update
several others, and update general utility files (Dirk in #24)
Courtesy of my CRANberries, there
is a diffstat report for this
release. See the project page
and package documentation for more details, and more examples.
Here's one that m2c usually gets right itself, but sometimes cannot:
if (var_v1_7 != 1) {
if (var_v1_7 < 2) {
if (var_v1_7 != 0) {
spF0 = 40;
} else {
vram_x = cmt46->vram_x;
vram_y = cmt46->vram_y;
spF0 = 40;
}
} else if (var_v1_7 != 2) {
if (var_v1_7 != 3) {
Repeated comparisons against the same variable, especially in a nested form
that could be binary search, should remind you of one thing: A switch.
Usually, the compiler implements a switch with a jump table, but not always;
if there are few conditions, or the range is too large for a reasonably-sized
table, it will do some ifs instead (or, enough ifs to get within the range
of a table). So what you want here is more like:
switch (var_v1_7) {
case 0:
vram_x = cmt46->vram_x;
vram_y = cmt46->vram_y;
spF0 = 40;
break;
case 1:
spF0 = 40;
break;
case 2:
...
}
Remember that sometimes, you may need to insert cases that are not explicitly
mentioned in the ifs (and/or a default case) to get everything to match back
properly.
In our reports, we try to 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.
As ever, if you are interested in contributing to the project, please visit our Contribute page on our website.
Reproducible Builds Summit 2026 held in Gothenburg, Sweden
The annual Reproducible Builds summit took place from 22 to 24 September 2026 in Gothenburg, Sweden. The event brought together developers, distribution maintainers, security researchers and free software advocates from across the ecosystem to share updates on current initiatives, address outstanding toolchain bottlenecks and strengthen cross-project collaboration.
Participants engaged in a series of working sessions and technical roundtables focused on expanding the practical scope of reproducibility. Discussions covered ongoing work within major Linux distributions, language-specific package ecosystems and the various parts of the binary verification infrastructure.
Strategic planning formed a pillar of the gathering, with participants discussing long-term roadmap goals for supply-chain security, software integrity and related topics. Attendees explored new methods for automated verification, discussing how to lower the adoption barriers for maintainers and streamline the integration of deterministic build pipelines into established continuous integration (CI) systems. The event was then followed by two days of unmoderated hacking sessions. These hacking sessions provided space for technical progress, allowing attendees to draft specification updates, prototype new tool features and resolve standing reproducibility bugs in-person.
Dettrace expanding architecture support for PGO & Lisp dumps
Bernhard M. Wiedemann announced major updates to dettrace, porting the tool across most architectures supported by Debian and openSUSE. The release addresses key stability issues, passing test suites across these targets and making it ready for production build pipelines.
These improvements enable deterministic builds when utilising Profile-Guided Optimisations (PGO), removing the need to sacrifice runtime performance for reproducibility. Additionally, dettrace can now be applied to (notoriously difficult-to-reproduce) Lisp and Scheme memory dumps, including environments such as CLISP, SBCL, Scheme 48, Racket and GNU Emacs.
Trusted Execution Environments (TEEs) have become a cornerstone of modern cloud computing, providing strong confidentiality and integrity guarantees for both code and data. A critical component of this trust model is remote attestation, which enables external entities to verify the authenticity and integrity of code executing within a TEE through cryptographic measurements. However, the effectiveness of remote attestation fundamentally depends on the verifierβs ability to trace the reported measurement back to the original source code - a property that can only be guaranteed through reproducible builds.Β [β¦]
Bernhardβs proposed modifications are independent of one another, allowing for any combination to be merged depending on community feedback. Bernhard noted that one proposal may see the most debate, and could still benefit from further refinement before reaching consensus. Community members are invited to review and provide feedback on the individual merge requests by 1 December 2026. Input can be submitted via reactions or comments on Salsa, directly by email to Bernhard or the mailing list.
The specification was first published in 2015 and updated in 2017.
Reproducible Builds will be present at SeaGL in Seattle, WA, on October 23rd & 24th. Come chat with us in person!
Founded in 2013, SeaGL is a free, grassroots technical summit dedicated to spreading awareness and knowledge about free source software, hardware and culture.
New Code of Conduct (CoC) for the Reproducible Builds project
Mattia Rizzolo announced the official launch of a Code of Conduct for the Reproducible Builds project. This follows work and community discussions originally initiated in 2024, incorporating feedback gathered over email and during project summits, as well as with input from the Software Freedom Conservancy.
The new Code of Conduct establishes guidelines emphasising mutual respect among project contributors. It also defines a formalised process for submitting and handling complaints regarding community misconduct, ensuring transparent governance across project spaces. Along with the core policy document, an accompanying Code of Conduct FAQ has been published to outline the rationale behind adopting the guidelines.
Supporter Spotlight: Jochen Sprickerhof
As a reminder, another supporter spotlight interview was published featuring long-time Debian and Reproducible Builds contributor Jochen Sprickerhof, highlighting his work on package verification and project infrastructure.
Jonathan Starr from the Institute of Open Science Practices (IOSP) posted with an announcement of its upcoming 2026 workshop series in Leiden in the Netherlands. This free-to-attend event aims to build a participant-owned data storage consortium, allowing community members to publish datasets, code and knowledge objects alongside algorithms designed to evaluate trust and value. The event features a dedicated Community Track for talks and discussions, a tool showcase and an opening session on resilient data storage.
Following up on IRC discussions regarding profile-guided optimization (PGO) on build servers, kpcyrd created a demonstration repository showing how PGO profiling can introduce non-determinism into output binaries. The demo highlights how runtime randomness (specifically, the randomised keys used in HashMaps to prevent denial-of-service attacks) causes varying bucket collision rates during test runs. These structural variances alter execution weights on underlying code branches, prompting the compiler to generate differing final assembly output for builds that would otherwise be perfectly reproducible if PGO was disabled. Rather than abandoning PGO performance gains, kpcyrd suggested treating generated profiling data as a documented build input or recorded environment artifact (similar to .buildinfo files), rather than regenerating profiles dynamically on the fly during build execution.
diffoscope is our in-depth and content-aware diff utility that can locate and diagnose reproducibility issues. This month, Chris Lamb made a number of changes, including preparing and uploading versions 330, 331 and 332 to Debian. In particular, Chris committed support for radare2 versions greater than 5.9.0Β [β¦] based on input from Guillaume GirolΒ [β¦], and committed support for .nar files via Guix.Β [β¦][β¦].
Chris later modified diffoscope to not Build-Depend on the Debian package of GNU Guix, as its status in the Debian bug tracking system would transitively render diffoscope as being overly buggy for the next stable releaseΒ [β¦]. Similarly, he adjusted the Build-Depends on the apksigcopier package as it has been removed from Debian testingΒ [β¦] and, finally, he updated a number of copyright yearsΒ [β¦][β¦].
reprotest is our tool for building the same source code twice in different environments and then checking the binaries produced by each build for any differences. This month, reprotest version 0.7.33 was uploaded to Debian by Holger Levsen with the following changes:
Sergei Tachenov fixed the fileordering test on all filesystems, including the likes of ZFS, which could return a different order across two builds due to build being done in a different directoryΒ [β¦].
Holger Levsen bumped the Standards-Version to 4.7.4, reflected that Priority: optional is the default nowΒ [β¦], fixed the old Free Software Foundation addressΒ [β¦] and bumped the debhelper βcompatβ level to 14 after confirming there are only desired changesΒ [β¦].
rebuilderd, our server designed monitor the official package repositories of Linux distributions and attempt to reproduce the observed results there; it powers, amongst other things, reproduce.debian.net. This month, Bernhard M. Wiedemann submitted a pull request to rebuilderd that refactors RPM repository metadata handling. The change extracts rpm-md parsing out of the Fedora-specific scheduler module and into a shared one.
Lastly, there were a number of improvements made to our website this month as well. For example:
Chris Lamb:
Added a note re. TZ=UTC vs. TZ=UTC0 to the Timezones page.Β [β¦][β¦][β¦]
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:
At the time of this writing, that leaves 130 submitted patches, with only 61 outstanding submitted patches that older than a year in the Debian bug tracking system. This number is only 26 if we exclude any low-priority build path issues.
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:
Linux draws a distinction between code running in kernel (kernel space) and applications running in userland (user space). This is enforced at the hardware level - in x86-speak1, kernel space code runs in ring 0 and user space code runs in ring 32. If youβre running in ring 3 and you attempt to touch memory thatβs only accessible in ring 0, the hardware will raise a fault. No matter how privileged your ring 3 code, you donβt get to touch ring 0.
Kind of. In theory. Traditionally this wasnβt well enforced. At the most basic level, since root can load kernel modules, you could just build a kernel module that performed any kernel modifications you wanted and then have root load it. Technically user space code wasnβt modifying kernel space code, but the difference was pretty semantic rather than useful. But it got worse - root could also map memory ranges belonging to PCI devices3, and if the device could perform DMA you could just ask the device to overwrite bits of the kernel4. Or root could modify special CPU registers (βModel Specific Registersβ, or MSRs) that alter CPU behaviour via the /dev/msr interface, and compromise the kernel boundary that way.
It turns out that there were a number of ways root was effectively equivalent to ring 0, and the boundary was more about reliability (ie, a process running as root that ends up misbehaving should still only be able to crash itself rather than taking down the kernel with it) than security. After all, if you were root you could just replace the on-disk kernel with a backdoored one and reboot. Going deeper, you could replace the bootloader with one that automatically injected backdoors into a legitimate kernel image. We didnβt have any way to prevent this sort of thing, so attempting to harden the root/kernel boundary wasnβt especially interesting.
In 2012 Microsoft started requiring vendors ship systems with UEFI Secure Boot, a firmware feature that allowed5 systems to refuse to boot anything without an appropriate signature. This not only enabled the creation of a system that drew a strong boundary between root and kernel, it arguably required one - whatβs the point of restricting what the firmware will stick in ring 0 if root can just throw more code in there afterwards? What ended up as the Lockdown Linux Security Module provides the tooling for this, blocking userspace interfaces that can be used to modify the kernel and enforcing that any modules have a trusted signature.
But that comes at something of a cost. Most of the features that Lockdown blocks are fairly niche, so the direct impact of having it enabled is small. Except that it also blocks hibernation6, and it turns out some people were using that. The obvious question is βwhat does hibernation have to do with keeping root out of kernel spaceβ, and the answer is a little convoluted and is tied into how Linux implements hibernation. Basically, Linux saves system state into the swap partition and modifies the header to indicate that thereβs a hibernation image there instead of swap. On the next boot, the kernel sees the header indicating that itβs a hibernation image, copies the contents of the swap partition back into RAM, and then jumps back into the old kernel code. What ensures that the hibernation image was actually written out by the kernel? Absolutely nothing, which means a motivated attacker with root access could turn off swap, write a hibernation image to the swap partition themselves, and then reboot. The kernel would happily resume into the attackerβs image, giving the attacker control over what gets copied back into kernel space.
This is annoying, because normally when we think about attacks on swap we mitigate it by requiring an encrypted swap partition. But in this case, our attacker is root, and so already has access to the plaintext version of the swap partition. Disk encryption doesnβt save us here. We need some way to verify that the hibernation image was written out by the kernel, not by root. And thankfully we have some tools for that.
Trusted Platform Modules (TPMs) are cryptographic coprocessors7 capable of doing things like generating encryption keys and then encrypting things with them. You can ask a TPM to encrypt something with a key thatβs tied to that specific TPM - the OS has no access to the decryption key, and nor does any other TPM. So we can have the kernel generate an encryption key, encrypt part of the hibernation image with it, and then have the TPM encrypt it. We store the encrypted copy of the key in the hibernation image as well. On resume, the kernel reads the encrypted copy of the key, passes it to the TPM, gets the decrypted copy back and is able to verify the hibernation image.
Thatβs great! Except root can do exactly the same thing. This tells us the hibernation image was generated on this machine, but doesnβt tell us that it was done by the kernel. We need some way to be able to differentiate between keys that were generated in kernel and ones that were generated in userland. TPMs have the concept of βlocalitiesβ (effectively privilege levels) that would be perfect for this. Userland is only able to access locality 0, so the kernel could simply use locality 1 to encrypt the key. Unfortunately, localities are typically only available on systems that implement a Dynamic Root of Trust Measurement like Intelβs TXT, and most consumer systems have no support for this. So, we need another approach.
TPMs have a set of Platform Configuration Registers (PCRs), intended for keeping a record of system state. The OS isnβt able to modify the PCRs directly. Instead, the OS provides a cryptographic hash of some material to the TPM. The TPM takes the existing PCR value, appends the new hash to that, and then stores the hash of the combination in the PCR - a process called βextensionβ. This means that the new value of the TPM depends not only on the value of the new data, it depends on the previous value of the PCR - and, in turn, that previous value depended on its previous value, and so on. The only way to get to a specific PCR value is to either (a) break the hash algorithm, or (b) perform exactly the same sequence of writes. On system reset the PCRs go back to a known value, and the entire process starts again.
Some PCRs are different. PCR 23, for example, can be reset back to its original value without resetting the system. We can make use of that. The first thing we need to do is to prevent userland from being able to reset or extend PCR 23 itself. All TPM accesses go through the kernel, so this is a simple matter of parsing the write before itβs sent to the TPM and returning an error if itβs a sensitive command that would touch PCR 23. We now know that any change in PCR 23βs state will be restricted to the kernel.
When we encrypt material with the TPM, we can ask it to record the PCR state. This is given back to us as metadata accompanying the encrypted secret. Along with the metadata is an additional signature created by the TPM, which can be used to prove that the metadata is both legitimate and associated with this specific encrypted data. In our case, that means we know what the value of PCR 23 was when we encrypted the key. That means that if we simply extend PCR 23 with a known value in-kernel before encrypting our key, we can look at the value of PCR 23 in the metadata. If it matches, the key was encrypted by the kernel - userland can create its own key, but it has no way to extend PCR 23 to the appropriate value first. We now know that the key was generated by the kernel.
Unfortunately it turns out that some people actually make use of PCR 23, and
restricting it to the kernel makes them sad. There are other resettable PCRs, but only in non-0 localities, so (a) we canβt reset them on most systems and (b) weβll just find someone who uses them anyway and run into the same problem. We need a different approach.
Thankfully TPMs have a small chunk of non-volatile RAM that can be used for storing things, and by βstoringβ I mean they can be used in a range of different ways. One of these is effectively as additional PCRs. These values can be configured such that they work the same way as PCRs, with the only way to modify them being to append a new hash to their existing value and update the new value to the hash of that merged value. We can allocate one of these for in-kernel use and reject any access from userspace, and also there are officially allocated ranges (so people shouldnβt be using random values) and the namespace is large enough anyway that w eshouldnβt collide with anyone, so we can carve one of those out and use it for this purpose.
Unfortunately, though, these NV indexes arenβt included in the creation data we get when we create a key, so we need another approach. TPMs have the concept of sessions, a series of commands executed in sequence. A flag allows these sessions to be βauditedβ, with the TPM keeping a running track of the hash of the commands executed and the results returned. At the end of the session you can ask for a signed copy of this hash, and if youβve kept a log of all the commands then you can verify that they match that signed hash and know that this log represents a real communication with the TPM. If we read the NV index and generate the key inside the same session, we can verify that the key weβre looking at was created while the NV index had a specific value, and if only the kernel can set that value then we know that the key was generated by the kernel.
Or, well, can we? Only new kernels would impose the restriction on modifying that NV index. An attacker could boot an old kernel, generate a key there in userland, sign a hibernation image with it, and then reboot into a new kernel. The new kernel would see a valid signature with a valid NV index associated with it and have no way to know it was generated from userland rather than the kernel. We need to be able to tell the difference between kernels that implement this feature and ones that donβt, and we need to do that without being able to go back in time to change old kernel behaviour. We can extend a PCR to indicate we implement the new functionality, but so can an attacker running an old kernel.
The normal approach to determining whether an event occurred in a specific phase of the boot process is to βcapβ the PCRs - extend them with a known value that indicates a transition between stages of the boot process. Any events that occur before the cap event must have occurred in the previous stage of boot, and since the final PCR value depends on the order of measurements and not just the contents of those measurements, if a PCR is capped before userland runs, userland canβt fake the same PCR value afterwards. If Linux capped the PCRs before userland started running, weβd be able to place a measurement there before the cap occurred and then prove that that extension occurred before userland had the opportunity to interfere. We could simply place a statement that the kernel supported the NV index restrictions there, and weβd be fine.
Unfortunately Linux doesnβt currently do this, and adding support for doing so doesnβt fix the problem - if an attacker boots a kernel that doesnβt cap a PCR, they can just cap it themselves from userland. So, weβre faced with the same problem: booting an older kernel allows the system to be placed in an identical state to the current kernel, and a fake hibernation image can be written out. Solving this requires a PCR that was being modified after kernel code was running, but before userland was started, even with existing kernels.
Thankfully, there is one! PCR 5 is defined as containing measurements related to boot management configuration and data. One of the measurements it contains is the result of the UEFI ExitBootServices() call. ExitBootServices() is called at the transition from the UEFI boot environment to the running OS, and the kernel contains code that executes before it. So, if we measure an assertion regarding whether or not we support restricted access to NV indexes into PCR 5 before we call ExitBootServices(), this will prevent userspace from spoofing us (because userspace will only be able to extend PCR 5 after the firmware extended PCR 5 in response to ExitBootServices() being called). Obviously this depends on the firmware actually performing the PCR 5 extension when ExitBootServices() is called, but if firmwareβs out of spec then I donβt think thereβs any real expectation of it being secure enough for any of this to buy you anything anyway.
Thatβs what this patchset does. Review feedback so far seems positive, although James Bottomley suggested a design improvement. My approach of using an NV index as a PCR and generating a signing key made sense as a consequence of my original approach involving PCR 23, which had been to use the key creation data and not use audit sessions at all. But the goal isnβt to use a key - itβs to prove that hibernation occurred in-kernel, and while signing is one way of doing that, there are others. His suggestion is to simply record the hash of the hibernation image into the NV index and then read that back as part of the audit session. On resume we verify the audit session log is legitimate, read the hibernation image, and compare the hash of the hibernation image against the hash in the log. If it matches, and if the PCR 5 value matches, we know the audit session happened in kernel and that the hibernation image hasnβt been modified.
It still seems like weβre missing something, though. If weβre not storing a key anywhere, how do we verify the audit session? To explain this weβre going to need to go into some details about how TPM keys work in the first place. TPMs can produce a special kind of key called an Attestation Key, or AK. Attestation keys are used to sign attestation statements from the TPM, such as PCR quotes or audit logs. We need a separate key type for this in order to prove that the signature was generated as part of an attestation - normal TPM signing keys are fundamentally designed to sign arbitrary material, and so can be used by userland to sign something that looks identical to an attestation record. By being able to distinguish the types of key the TPM uses, we can prove that a signature was generated using an AK, and so know that itβs a real TPM attestation (because nothing other than the TPM can sign stuff with an AK).
But even beyond this distinction, TPMs have different key types. Ordinary keys are generated using an RNG and will be unique. Primary keys are generated using a seed that is used as an input into a key derivation function. For a given TPM, using the same seed to generate the same type of primary key will always generate the same primary key8. We can have the kernel deterministically generate an AK during suspend in order to sign the audit log, and then generate the same key again on resume to validate it. This means we donβt need any other form of trusted storage for the AK - the TPM already provides that functionality.
This is all rather complicated but Iβve been thinking about this for some time now and I havenβt come up with a cleaner solution. The only real open issue is the degree to which machines actually perform the PCR 5 extension. If it turns out thereβs a bunch that donβt weβll need to come up with some alternative approach to identifying whether the kernel supports this functionality, but the design is such that that should be invisible to userspace so adding alternatives later shouldnβt be an issue. Filtering accesses to the NV index is also not super clean, and depends on there not being alternative mechanisms to get at the TPM by sending raw commands. Again, this is intended to be opaque to userland and can be improved if we come up with something better later on (such as a secure way to pass a secret to the kernel on boot that can be used as an auth value for acess to the NV index).
In preparation for DebConf 27, Stefano Rivera,
Nattie Mayer-Hutchings, and Jongmin Kim from the DebConf Committee visited
Asahikawa Japan. A contingent of the local team gathered to tour the venues
with us, discuss plans, and meet the local organizations assisting the event.
We held a press conference at the city hall that got
some coverage.
A few potential day-trip options were explored.
Photos from the site visit.
autopkgtest-virt-pconr, by Helmut Grohne
Following the creation of the pconr
module that provides a basis for
unschroot
and a more secure containment for mmdebstrap,
an experimental autopkgtest backend autopkgtest-virt-pconr
is now available. It fits a spot between autopkgtest-virt-unshare and more fully
fledged containers as it does not require much infrastructure like setting up
incus or lxc but still provides a containment running an initsystem. Due to the
use of erofs as base image format,
it boots and reverts quickly. This development also required fixing concurrency
issues in asyncvarlink and even shed
light on a matching bug in systemd
that was promptly fixed upstream.
Miscellaneous contributions
Carles reviewed and submitted six Catalan po-debconf translations using
po-debconf-manager.
Improved po-debconf-managerβs workflow for updating translations.
Carles reviewed, translated or updated webwml
Catalan translations for security and CD pages. He also fixed issues that
prevented building webwml, deleted orphaned translation pages, etc. and
experimented with a more global HTML tidying up experiment.
Carles refactored check-relations
adding capacity to ignore concrete from_package -> to_package specific relations
and more flexibility around ignores. Detects unused ignore rules as well.
Carles improved some Wiki pages (LibreOffice,
Framework amongst some
other ones)
Thorsten did an hplip upload to replace an expired certificate of HP. This
certificate is used to sign new upstream releases and modules that can be
downloaded later. Afterwards work on a new upstream version started.
Thorsten also struggles with autopkgtest problems of the new upstream version
of cups. Work is still ongoing.
Antonio worked on several topics around Debian CI maintenance, reliability and
performance.
Stefano updated the autopkgtests for cPython packages in Debian to support
setuptools 80.
Stefano uploaded pypy 8.
Stefano helped to get DebConf 26 reimbursement claims processed, and worked on
some of the resulting issues in debian-reimbursements.
Emilio managed various transitions.
Emilio worked on the rustc-web/trixie bootstrap for armel, using the rustc
1.95 version from trixie-backports. This will be needed by the upcoming clamav
security updates.
Helmut sent patches for 11 cross build failures.
Helmut introduced a new build profile to libcap-ng to keep architecture cross
bootstrap working.
Helmut uploaded debvm_0.5.5
introducing support for Hurd thanks to a lot of work by Johannes Schauer Marin
Rodrigues.
Colin upgraded to new security releases of groff and parted.
Colin upgraded 50 Python packages to new upstream versions, and fixed over 40
RC bugs in the Python team.
Santiago continued mentoring Aryan Karamtoth with the GSoC project about
introducing livepatching support for linux. This mentoring is done in
collaboration with Emmanuel Arias. In particular, Santiago explored different
requirements for providing binary modules for the same kernel packages used by
Debian, that require a matching version number.
Anupa joined Jean-Pierre Giraud to prepare the files for point release
announcement for Debian 13.7.
Review: Ties That Bond in Gretna Green, by Linzi Day
Series:
Midlife Recorder #3
Publisher:
Linzi Day
Copyright:
February 2023
ISBN:
9798376517017
Format:
Kindle
Pages:
616
Ties That Bond in Gretna Green is a self-published fantasy novel
and the third book in the Midlife Recorder series. As with the previous
book, it picks up almost immediately after Painting the Blues in Gretna Green. Day calls the genre
paranormal women's fantasy, and that sounds about right to me.
There is an intervening short story that you can get by
joining Day's mailing list (along
with an epilogue to the first book and to this book). I don't think it's
that important, but there are frequent references to it at the start of
this novel that become somewhat annoying if you haven't read it.
Valentine's Day is fast approaching. This the busiest day of the year for
the part of the Recorder's job that Niki already knew how to do: marrying
people. She performed marriage ceremonies in her previous job before her
inheritance, and she loved that work despite her awful boss. Performing
marriages in the Gateway sounds even more rewarding, but the sheer number
of couples and the political complications from the first two books create
logistical problems she has to solve first.
Also, the remaining member of her staff held over from her grandmother's
time has returned from vacation to discover that Niki has upended most of
what she thought she knew about the job. Time for Niki to apply what she
learned from the change management training her old boss forced her to
attend and then ignored! (If you just groaned, rest assured that Day
somehow makes this work, although she clearly has a higher opinion of
corporate training than I do.)
This plot was more to my taste than Painting the Blues, mostly
because Niki seemed on top of the problems and didn't postpone looking at
them for half the book. There is more of the management competence porn
that readers have come to expect of the series, a few more revelations
about Niki's magic, more mysterious manifestations and the first mention
of a prophecy, and a few political developments. As always, the pacing is
exceptional. These books grab me and pull me along, page after page, often
robbing me of sleep because I find them nearly impossible to put down.
Ties that Bond (and to some extent the last book) looked like it
was going to introduce a romantic subplot, and I was preparing to be
annoyed by it because I did not like the man in the subplot at all. I
won't spoil how it works out in this book, but I will say that Niki
surprised me and showed more sense than I thought the author was
telegraphing. This is not the first time that Day has surprised me with
what I thought was foreshadowing that did not turn out how I expected,
usually for the better. If she can keep that up, it's a nice effect (if a
bit unbalancing) and adds to my desire to read just one more chapter.
The way the world-building of these books is structured continues to
intrigue me. Day gets away with some problems that I think would be more
noticeable in other books thanks to the excellent pacing (no time to ponder
the world-building, we're on to the next crisis!) and the tight
third-person perspective. Niki is not a detail person, to say the least,
but she does keep returning to bits of world-building and poking at them a
bit harder, which gives Day repeated opportunities to shore up ideas that
felt shaky and dubious at first glance. Still, this is not a book that
prizes deep logical consistency or full exploration of the ramifications
of a bit of magic. You have to be willing to go along with the ride and
not think too hard.
For example, Niki can see bonds between couples and judge their strength.
This is apparently a long-standing power of the Recorder's line (she was
able to do that before she became the Recorder), and part of her
responsibility in these weddings is to check the bonds of the couples.
This leads to some well-handled scenes where she diagnoses a problem or
resolves a conflict because she can magically see the true relationship
structure.
Everyone in the book just takes this power in stride. This is wild to me!
This ability would completely upend half of society if you fully worked
out its ramifications and could find people other than Niki with the same
ability. Even if one assumes that a bond only forms over time and can't be
sensed in the early stages of a relationship (something the book never
establishes), can you imagine the implications of having an infallible
oracle that says at a glance whether a marriage will work (permanently,
the book strongly implies)? The implications for how to structure
marriage, for family planning, for psychology and psychiatry, for how to
structure dating, even for living arrangements and housing markets? This
is not a small world-building detail, and yet it's treated more like part
of Niki's characterization than like a foundation of society.
I think some of this all-vibes world-building may come from the general
new-age feel to how magic works. Day makes a grab-bag of random magic
canonical in this world: psychic lie detecting abilities, auras, chakras,
premonitions, and other bits of the sort of spiritualism that tends to be
heavily marketed to middle-class women. I have that niggling feeling that
one sometimes gets in SF that a lot of this is in the world-building
because the author believes it's partly real (see, for instance, psychic
powers and the Dean drive in 1960s science fiction). If one believes that
psychic powers and various bits of mysticism are true in our world, one
has already developed the rationalization mechanisms to explain away their
almost total lack of influence or impact on how the world is structured.
If one then introduces them into a true fantasy world, one may not notice
how many implications that should have for how society is structured,
since one is used to believing magic exists but has no significant
implications.
I could be entirely wrong about this analysis, and maybe it's a bit unfair
to Day. I should also be clear that this is not a complaint, just a point
of fascination to me when I think back over one of these books after I
finished it. There is a glut of fantasy out there that's constructed like
an engineering problem or that goes deep into politics or social theory,
usually written by people with strong opinions but not a lot of training
in real sociology. One of the joys of self-published books is that you
occasionally get something that assembles genre conventions in a way that
feels odd and delightfully strange. What if, in addition to dimensional
gates and overwhelming protean power invested in a true heir, all the
magic sold by the local Tarot and crystal store was also true? Sure, why
not! If the pacing is good and the story is grabby, it works for me.
It does seem worth attaching a warning for the folks who need scientific
reproducibility and explored implications in their world-building, though.
If you're the sort of reader who is bothered by political boundaries and
economic systems that match our world despite the existence of magic, this
series may cause you problems.
This continues to be a grabby, highly entertaining story of a woman who
finally gets to run her own small business the way that she always thought
the business should be run after years of working for an awful manager,
except that her business is total magical power over an interchange point
between dimensions, she's the chosen one, and the setting is a high
fantasy world with prophecies and gods. If that sounds appealing, don't
start here, but I highly recommend giving Midlife in Gretna Green a try. The series seems to be holding up.
Followed by a short story epilogue available by joining Day's mailing
list, an optional novella (maybe novelette) published in Spilling
the Tea in Gretna Green, and then Seeing Red in Gretna Green.
A new release 0.4.29 of RQuantLib
arrived on CRAN this evening,
had already been built for r2u as well as on r-universe and
has just been uploaded to Debian.
QuantLib is a rather
comprehensice free/open-source library for quantitative
finance. RQuantLib
connects (some parts of) it to the R environment and language, and has
been part of CRAN for
twenty-three years (!!) as it was one of the first packages I uploaded
to CRAN.
This release of RQuantLib
brings two new calendars (Malaysia, Philippines), updates to a number of
other calendars as well to other files, and two deprecations we are
reflecting in this release. It also benefits from a number of careful
and attentive pull requests by Maximilian Muecke cleaning up an
(astonishing !!) number of likely rather old little bugs. Full details
from the NEWS file follow as usual.
Changes in RQuantLib version 0.4.29 (2026-10-13)
Configure check for outdated QuantLib version updated,
Adjust two instantiations on macOS x86_64 with old
compilers
Correct Euribor2W and Euribor3W tenors setters (Maximilian Muecke
in #196)
Accect Euribor1W as alias for EuriborSW tenor (Maximilian Muecke
in #197)
Correct default handling of the payFixed parameter (Maximilian
Muecke in #198)
Assign missing S3 class to American option implied volatitly
return (Maximilian Muecke in #199)
Correct spelling in manual pages and R code (Maximilian Muecke in
#202,
#203)
Support additional Polish settlements (Maximilian Muecke in #204)
Check for curve parameters in FittedBondCurve (Dirk in #205
fixing #201)
Reflect two deprecations in QuantLib 1.44 and adjust code in two
spot (Dirk in #206)
This is more of a note to myself since I keep forgetting and "rediscovering" this technique every time I need it, but if it helps you, great!
The JavaScript way is to toggle displaying the menu based on a button click. For instance, here's an example in Svelte:
[This interactive/preview is only available on the website.]
<script lang="ts">
let showMenu = $state(false);
</script>
<button onclick={() => (showMenu = !showMenu)}>
{#if showMenu}
Hide menu
{:else}
Show menu
{/if}
</button>
<nav class:hidden={!showMenu}>
<ul>
<li>Home</li>
<li>Blog</li>
<li>Foo</li>
<li>Bar</li>
</ul>
</nav>
<style>
.hidden {
display: none;
}
</style>
But there is a hack to use a hidden checkbox and some CSS to achieve the same thing without any JavaScript. The example here turns the menu button into a label that toggles the checkbox, and uses the checkbox state to determine when to show the menu via the next-sibling combinator.
[This interactive/preview is only available on the website.]
MIPS has 16-bit loads and stores, but not 16-bit (signed) arithmetic,
so if it wants to do that, it needs to sign-extend to 32-bit first.
And it does that by first shifting up by 16 so that the 16-bit sign bit is
at the 32-bit sign bit, and then it can do an arithmetic shift afterwards.
So what you want is:
s16 temp_v0;
s.x = temp_v0 >> 1;
Sometimes, it can be pretty stubborn, and you'll need to go through
memory or similar before GCC really inserts the shift patterns you want.
Of course, you could just use the modern choice. Here, I've shortened what Schade uses into one line of CSS (because I can). As noted, this is a fairly new feature so not every browser supports this.
[This interactive/preview is only available on the website.]
.underline-me {
text-decoration: underline red solid 3px;
}
Workarounds
Since some browsers don't support text-decoration, here are the alternatives I could think of.
border-bottom
The workaround Schade describes is using border-bottom, though it has a gap between the text in the line.
[This interactive/preview is only available on the website.]
I am <span class="underline-me">underlining this yap</span> within the sentence.
.underline-me {
border-bottom: red solid 3px;
}
::after
Probably the better way is to create an ::after element, which gets very close, but displays above the text (covering the letters 'g' and 'y').
[This interactive/preview is only available on the website.]
I am <span class="underline-me">underlining this yap</span> within the sentence.
If you use a container (in this case, two nested <span>s instead of just one), then you can get very close with the underline displaying below the text.
[This interactive/preview is only available on the website.]
I am
<span class="underline-me-ctn">
<span class="underline-me">underlining this yap</span>
</span>
within the sentence.
And now if you add a stroke to the text via -webkit-text-stroke, you're pretty much there, though this may not be feasible in some situations (e.g. gradient or image background behind the text). Support for -webkit-text-stroke is not that much better than text-decoration though, so make of that what you will.
[This interactive/preview is only available on the website.]
I am
<span class="underline-me-ctn">
<span class="underline-me-outline">underlining this yap</span>
<span class="underline-me">underlining this yap</span>
</span>
within the sentence.
Today, for a slightly more complicated snippet, but still in the
same general area; say you have code like this:
x = (0x1000 - y) / 24;
z = var_s5 < 5;
if (x >= 0x81) {
x = 0x80;
z = var_s5 < 5;
}
var_s4 = 0;
if (z == 0) {
In particular, note the z = var_s5 < 5; repeated. Most likely, this is the
compiler sticking stuff into branch delay slots, or else re-checking the condition
if var_s5 changed in the branches (it does not here). You almost certainly
want to undo this into:
x = (0x1000 - y) / 24;
if (x >= 0x81) {
x = 0x80;
}
var_s4 = 0;
if ((var_s5 < 5) == 0) {
and the if, as we already know, should probably rather then be:
if (var_s5 >= 5) {
For a small bonus (since I'm not going to have a separate post about it),
you can choose to change the upper if to:
if (x > 0x80) {
x = 0x80;
}
which generates exactly the same code (on MIPS), but may in many cases be
easier to understand.
Daggerbound is a secondary-world fantasy and a sequel to
Swordheart, although it follows
different protagonists for most of the book. It's also a sort of sequel to
the Clocktaur War duology in that one of the
protagonists was a protagonist of those books. T. Kingfisher is the pen
name Ursula Vernon uses for her adult fiction.
Learned Edmond was once a devout acolyte of the Order of the Many-Armed
God and a true believer in its teachings, including the ones about the
evils of women. Then he went on an adventure with, among other people, a
forger named Mistress Slate, during which he learned a great deal of
things about the world that his order did not teach him. He has spent the
subsequent years trying to bring this important new information to his
order and encourage them to adjust their view of the world accordingly.
The order has not changed, and Edmond is very tired.
Someone needs to carry a number of books and a magical sword to the city
of Archon's Glory. Edmond has volunteered and intends to perform the task
exactly as expected, and then never return. He is unsure what he will do
then, but at least it will be different.
Edmond did not expect to be waylaid by bandits or to accidentally draw the
magical sword. He certainly did not expect the man who appeared in a flash
of light and violently saved him from the bandits.
It would have been very convenient for the Dervish if Edmond died of his
injuries after drawing the sword. That would leave the sword without a
wielder and therefore the Dervish without someone he had to protect. All
he would have to do was sit and watch while the fever and cold and lack of
water did the work, and then he would be free. Given his past experience
with wielders, no one could even blame him. Except maybe his own
conscience.
I did not realize I wanted to read a novel about a traumatized Mercedes
Lackey sword waking up in a T. Kingfisher universe, but apparently I did.
The Dervish is not a sword, precisely, at least not in the way that
Need has become by the time we meet her in
Lackey's novels. He's a man trapped in a sword, he doesn't like it, and he
would very much like it to stop. But at the start of the book, he has the
rest of the vibe of a Mercedes Lackey protagonist: vast past trauma, poor
emotional regulation skills, and a prickly cynicism that only runs skin
deep. Pairing him with an Edmund who is far more mature than in the
earlier duology but still rather innocent and delightfully neurodivergent
is a great touch.
The rest of the plot was fine but not that memorable. Like the Clocktaur
series, there is a strong feeling of episodic D&D adventure as the
characters stumble across disconnected problems and hostilities while
collecting party members. Like Ursula Vernon's occasional D&D recaps, this
party has a tendency to adopt everyone they come in contact with, which
includes a gnole (as always one of the best parts of the book) and a
sentient giant pill bug. The pill bug, who has a complex
appendage-operated mechanical talk box and a banner that says "I DO NOT
EAT HUMAN MEAT," is the most Ursula Vernon character that has ever existed
in fiction.
There is a romance because there is always a romance in the novels set in
this universe. I didn't mind this one. Like every Kingfisher romance, it
involves a lot of irritating refusal to communicate and endless angst
about how the other person couldn't possibly be in love with them, but the
characters have rather more justification than usual and at least I didn't
spend most of the book thinking one half of the romantic pairing was an
idiot. Apparently it helps if no paladins are involved.
The strongest parts of this book are the beginning, before the Lackey
vibes unfortunately drain out of the story, and the end, when all of the
best characters from Swordheart show up again. The Temple of the
White Rat continues to be the best feature of this fictional universe, and
I could read endless books about how they solve problems.
The Dervish gazed at her with admiration. "And here I thought you'd be
on the side of law and order..."
"No, young man," said Beartongue, despite being several centuries his
junior. "I am on the side of justice. If we're lucky, the two run in
parallel. If they don't, we bend the law to attain justice, not the
other way around."
The ending also featured some exploration of the edge cases of the rules
for the sentient swords that reminded me a bit of
Fred Saberhagen with better characterization.
Daggerbound was not as charming as Swordheart, mostly
because Halla appeared only at the end. I wish Kingfisher had leaned more
into the Mercedes Lackey vibes rather than taking her normal D&D-flavored
romance approach. But this was enjoyable, charming at times, had great
supporting characters like all Kingfisher novels, and was a satisfying
continuation of the series. I am looking forward to the third (and I
believe final) book, which is supposed to be out next year.
With 26.10 in Feature Freeze, wore my Release team hat to:
Review and approve a bunch of Feature Freeze Exceptions, including the Go 1.27 transition, Qt 6.11.2, rust-coreutils 0.12.0, devscripts 2.6.12, kdump-tools moving to dracut, and a few GCE packages.
Point linux-entra-sso to the SRU team, as it didnβt need an FFe at all, and milestone a couple of bugs for 26.10.
Continued to push for infrastructure improvements:
Made ubuntu-cdimage more resilient to Launchpad hiccups (MP).
Taught generate-freeze-block in ubuntu-archive-tools a couple of new options (--skip-update and --series) and better error handling (MP).
Reviewed quite a lot of MPs - britney hints, meta-release, ubuntu-release-tools, ubuntu-archive-tools, ubuntu-cdimage, livecd-rootfs, ubuntu-seeds, and so on.
(A quick note that I did quite a lot of the work during my work as Frontdesk that went on till 4th Oct, so 33.50 hours include the work I did till 4th October 2026.)
Released Security Updates
node-lodash: Prototype pollution in _.unset and _.omit (plus a bypass of the initial fix for it), and code injection via the imports option of _.template.
This is the bookworm follow-up to DLA 4663-1 that I carried over from July. The update had been sitting prepared and tested on debusine, so once the upstream discussion wrapped up, it went straight out. It also carries an extra patch to the lodash-cli dependency map - without it, the regenerated lodash.template module throws βassignWith is not definedβ at runtime, which node-gulp-utilβs autopkgtest caught.
libpng1.6: Use-after-free of zlib input in png_read_end() after incomplete zTXt, iTXt, or iCCP decompression, which could result in denial of service.
I picked this one up whilst on front desk; the upstream fix that went into trixie via DSA 6537-1 applied to 1.6.39 as-is.
Work in Progress
libpng1.6: The same CVE-2026-46675.
[ELTS]: Claimed in ela-needed; the same upstream fix applies to all three ELTS suites, so this should go out shortly.
Other Activities
[E/LTS] Did my front-desk week from 28-09 to 04-10.
Over the week I did roughly:
Triage of 524 unique CVEs across 192 source packages and all four suites. And 1 automated ELTS end-of-life sweep.
Additional data is now part of the front-desk job too, so 75 commits record it for 179 CVEs across 71 packages.
Newly queued 22 source packages - 13 in dla-needed (apache2, chromium, firefox-esr, libnet-idn-encode-perl, libpng1.6, node-shell-quote, nvidia-graphics-drivers, openssl, pcre2, php-mongodb, php8.2, social-auth-core, and wpa) and 16 in ela-needed (apache2, firefox-esr, gegl, lemonldap-ng, libnet-idn-encode-perl, libpng1.6, libwebsockets, nsd, nvidia-graphics-drivers, pcre2, php7.0, php7.3, php7.4, rar, unrar-nonfree, and wpa), 7 of them in both. Also widened 3 ELTS entries to bullseye - antiword, ocaml, and openvswitch.
Most of the interesting work was again in the <not-affected> verdicts: 217 of 263 (83%, across 57 packages) rest on showing that the vulnerable code or feature isnβt in the source we ship. Another 17 cover incomplete upstream fixes that we either never shipped or shipped with the follow-up already in (expat, libxml2, node-tar, and pyjwt). 15 come from bundled components we donβt ship - node-ajv again, which uses uri-js and not fast-uri, and nvidia-open, which doesnβt ship the NGX updater. And 13 are vendor- or platform-specific - opensshβs CVE-2026-55654 lives only in a Red Hat GSSAPI patch, and the netdata and PHP ones only affect Windows.
Did 4 rounds of newly supported bullseye packages (33 packages overall), plus libmail-mboxparser-perl for buster - 34 in all. The existing CVE data mostly held up; the real finds were discovery gaps. One interesting gotcha: sid renamed stunnel4 to stunnel, and two new CVEs were associated only with the new name, so they never showed up for trixie, bookworm, or any ELTS suite. I also sorted the 180 βTriage neededβ entries (57 packages) from elts-eol by where each was already handled, and triaged for ELTS the 100 that trixie or bookworm had already decided; the rest come next. And I imported 79 package-level lines for the older source names ELTS still ships (tomcat8, php7.0/7.3/7.4, netty-3.9, openssl1.0, python2.7/3.5/3.7, and pypy).
Caught some inconsistencies in released advisories: DLA 4801-1 (swift) was missing a CVE, ELA 1831-1 (redis) listed the wrong one, and DLA 4808-1 (network-manager-l2tp) listed CVE-2026-75883 - an ID that was renamed and then reassigned to an unrelated ppp issue. I fixed all three in the trackerβs advisory lists.
Made the Security team and the uploaders aware of regressions from our own updates:
libde265: the CVE-2026-49337 fix in DLA 4789-1/ELA 1828-1 (and DSA 6413-1) misses upstreamβs follow-up commit.
libxml2 CVE-2026-76781 and policykit-1 CVE-2026-85498 were introduced by our own back-ports; the policykit-1 one is harmless.
expat: the CVE-2025-59375 backport in DLA 4807-1 is compiled out, because XML_GE is never defined.
xen: DLA 4818-1βs XSA-491 fix makes XSA-510 reachable.
gegl: the CVE-2026-2049/2050 back-port in ELA 1650-1 rejects valid RLE .hdr files.
log4cxx: DLA 4322-1 broke UTF-8 in JSONLayout output. That one is functional, not security.
Helped correct a set of triages and let the Debian Security team know:
ruby-json CVE-2026-71847: the vulnerable code was never in a released Debian version.
openssh CVE-2026-55654: only affects a Red Hat-specific GSSAPI patch.
three libxml2 issues that donβt affect trixie either.
the stunnel4 rename above.
trixie <no-dsa> lines on jsch, node-qs, and ruby-loofah, where trixieβs version doesnβt have the vulnerable feature at all.
Independently found a security issue in rclone and reported it privately upstream through a GitHub security advisory. Upstream already had a report for the same issue, so they folded mine into the existing advisory and credited me as a co-reporter.
Also found some bugs in the additional-data tooling, e.g. cve-ad --package P --severity tbd wipes the CVE-level severity, and the schema doesnβt validate package-level entries at all.
[E/LTS] Monitored discussions on mailing lists, IRC, and all the documentation updates.
For over ten years, the only way I have had of knowing what our solar
panels are doing has been an impulse monitor on the generation meter,
counting the flashes of its LED. It has always been a bit flaky due
to battery changes or loose connections, and there has never been a
way to see solar generation alongside the energy used by the rest of
the house. This week I have fixed that, by building a small ESPHome
sensor which talks directly to the inverter. I am still reeling
slightly: with AI assistance, it took under a week from idea to
working implementation, and it is a project I would certainly never
have attempted otherwise. The result is
zeversolar-esphome.
The inverter is a Zeversolar Zeverlution 3680, which has an RS485 port
but no Wi-Fi or Ethernet module. It doesnβt speak Modbus; it uses
Eversolarβs own master/slave protocol, which nobody has officially
documented but which several people have reverse-engineered over the
years (credit to
eversolar-monitor,
Heliograph and
others).
Manufacturer support for Eversolar/Zeversolar inverters apparently
ended in December 2019 when the company went out of business.
The rating plate on my inverter.
A while ago I had looked into what ports were available on this
inverter with a view to connecting some sort of monitoring, but I had
abandoned it as βtoo hardβ. While working on some Home Assistant
configuration, Claude suggested the idea again, but similarly wrote it
off initially. I encouraged it to explore a bit further, and supplied
the model number of the inverter, at which point the picture got a lot
more promising. After a bit of back-and-forth about hardware, we
settled on an M5Stack Atom Lite on an Atomic RS485
Base. The
main attraction was that it already comes in a case, so I didnβt need
to design (or, more likely, bodge) an enclosure.
While I waited for the parts to arrive, Claude wrote the firmware: an
ESPHome external
component built
around a small, dependency-free C++ driver, with host-side unit tests
against byte captures that other people had published from other
models. I found it slightly surreal to have a tested protocol driver
before owning any hardware to run it on.
Cables
The parts came before the tools I needed to get into the inverter
safely (a DC clamp meter and a T25 screwdriver bit), so in the
meantime I made up an RS485 cable - this uses an RJ45 connection at
one end, and some custom wiring at the other. My first attempt was
with some CAT 6, but (as Claude had pointed out and I had ignored) its
solid-core conductors are much harder to twist together. So I gave up
and sacrificed an old CAT 5e patch lead instead, which went much
better.
The custom end of the cable, before being taped up.
The risky bit
On a Zeverlution, the RJ45 sockets are inside the wiring compartment,
which means taking the cover off to install the monitor. The manual
addresses this part to qualified electricians. So the step I was most
nervous about was not the software at all, but safely isolating the
inverter.
Inside the inverter, with the cover off.
I did this with appropriate caution, following the instructions from
the manual carefully, and also waiting until after dark just in case.
Now that I understand how the DC clips from the panels work, I can see
how to do this safely during daytime too β but hopefully this wonβt
be necessary.
Plugged in, and threaded through the cable gland.
Warning: if youβre not confident, I would always advise to get an
electrician to do this bit. Do what I say, not what I do.
Debugging over Wi-Fi
Once the device was installed, the nice thing about ESPHome is that I
never need to go back in the loft again to update it β it reflashes
over Wi-Fi. Iβve taken full advantage of this for debugging, and for
reading the wire protocol directly from the logs.
The finished device. Green means generating.
Separately, Iβve used Claude to integrate the new sensors into Home
Assistant. Solar generation finally sits in the Energy dashboard
next to the rest of the houseβs usage, which was the whole point.
Reflections
This was a fun, quick project, and it solved a problem that has eluded
me for a decade. I want to be honest about how much of that is down
to the AI: I would not have attempted any of this on my own. I could
not have told you the protocol existed, never mind implemented it. My
contribution was everything involving a screwdriver, and ordering some
hardware.
A brand new release 0.1.16 of the RcppSimdJson
package is now on CRAN, has
been built for r2u,
and is updated on r-universe.
RcppSimdJson
wraps the fantastic and genuinely impressive simdjson library by Daniel Lemire and collaborators. Via
very clever algorithmic engineering to obtain largely branch-free code,
coupled with modern C++ and newer compiler instructions, it results in
parsing gigabytes of JSON parsed per second which is quite
mindboggling. The best-case performance is βfaster than CPU speedβ as
use of parallel SIMD instructions and careful branch avoidance can lead
to less than one cpu cycle per byte parsed; see the video of the talk by Daniel Lemire
at QCon.
This version updates to the recent 5.0.2 upstream release with
improved performance. Given the strong focus on performance, this
release also alters some of the R and C++ interfaces to be more direct.
Rcpp generally aims for a balance between readable / maintainable code
and good performance; this moves the dial closer to strong performance
gains. As Daniel noted in a table in
the (main) PR for this release, this now consistently beats yyjsonr, and
improves considerably over the previous release of this package. I had
made some smaller interim upgrades earlier (without a CRAN releases),
and made a number of minor standard packaging updates.
The short NEWS entry for this release follows.
Changes in version 0.1.16
(2026-10-09)
simdjson was upgraded to versions 4.6.1
and 4.6.3 (Dirk in #100
and #102
fixing #99
and #101)
simdjson was upgraded to version 5.0.2
(Daniel in #104)
A number of small tweaks to the Rcpp
interface were made too (Daniel in #104)
Diagnostics for gcc and clang are not
suppressed per CRAN Policy (Dirk in #106
closing #105)
The continuous integration matrix was extended to
g++ version 15 and 16 (Dirk in #108
closing #107)
A few minor packaging nags were address (Dirk in #109)
A few days ago, a colleague reached out to me, asking if I could help draft a
resolution on digital autonomy and free software for the upcoming renewal of
our collective bargaining agreement. Although she's very politicised and
generally a great individual, she is not a techie and we never really talked
about these issues before.
This took me by surprise. I should have been rejoicing; no, I should have been
the one who brought up this topic in our union. Wasn't I the technical expert
on these issues? But alas, my first reaction when she came to me was to snicker
and tell myself: "That's cute, but nothing's ever gonna change, Big Tech is too
strong and our governments don't give a damn."
How sad and cynical is that? That's not the person I want to be.
Something in the Quebecois political landscape has shifted in the last few
years with regards to tech. More and more "regular" people seem to be talking
about the importance of digital autonomy, how government services shouldn't
rely on Big Tech and how we should be using FOSS on hardware we actually
control.
I first fell for Free Software for its political ideals: Knowledge Should Be
Free, Cooperation Is More Efficient Than Competition, Centralisation Should Be
Avoided When Possible, etc. I still believe in all of this, but somehow, after
years of fighting for change in the tech world, I have retreated into technical
work. I maintain Debian packages as a hobby and to scratch my own itch. I run
services for myself and a few friends. I organise Debian User Group meetings.
To be clear, I haven't generally given up. I'm very politically active in my
union and at work, I'm a good member of my housing cooperative and I'm still
fighting against the industrialisation of natural spaces in my
neighborhood. But when it comes time to defend freedom in the tech
world, lately, I've just been shrugging and accepting I should use Microsoft
Teams like every one else does. I want this to change and to do so, I need to
free some time.
Lintian
Two years ago, I added myself to Lintian's Uploaders list. Up until
then1, I enjoyed fixing little issues here and there and liked creating
new tags to help out the Python Team, where most of my Debian time was spent.
Lintian was badly in need of a new maintainer and as I felt it was an important
project, I decided to step in. 37 releases and about 160 closed bugs
later2, I'm done.
To be honest, I haven't had much fun maintaining Lintian lately. Bugs keep
piling on the BTS, so much so I have mostly been ignoring them for the past few
months. In fact, most of the actual work triaging, writing code and fixing bug
has been done by Nilesh Patra for a while now.
All this to say, if you care about Lintian, you should consider adding yourself
to the Uploaders list, as I'm out and Nilesh can't do all the work by himself.
He's a great person to work with and since I don't want to suddenly leave him
all alone, I'll be sticking around for a few more weeks in the hope someone
else will volunteer to help out. A smooth transition with a new co-maintainer
would really be nice!
If you're interested, don't hesitate to reach out to me on IRC or by email.
The diffoscope maintainers are pleased to announce the release of diffoscope
version 333. This version includes the following changes:
[ Johannes Schauer Marin Rodrigues ]
* Add support for showing differences in POSIX.1-2001 PAX headers within .tar
files.
[ Chris Lamb ]
* Re-order various parts of the tar comparator to make it easier to
progressively delve into its functionality.
* Reflect that the use of the "case-match" language feature requires at least
Python 3.10+. This can be worked around if it causes problems - let us know.
* Update various copyright years.
setuptools 84 is now in Debian and no longer contains pkg_resources. Removing uses of that module has been an ongoing project for a while now, but I did another batch of fixes thisΒ month:
A new python-coverage version triggered several build regressions due to a known problem in pytest-cov. I worked around these by setting COVERAGE_CORE=ctrace:
This may be what was intended, but if it doesn't match (usually due to
different register allocation; the rewrite itself is nearly always going
to be taken, since it saves a branch on one of the paths), this is often
a better rewrite:
if (a == 10) {
x = 4;
} else {
x = 3;
}
and in some cases, this may have different code generation (especially
as you can stick it into an argument list):
x = (a == 10) ? 4 : 3;
This is a bit more of a trial and error than the others, and of course,
it depends on the computed values not having side effects.
One side note that Greg made at 20:30 is about the natural tendency to anthromorphise the entity sending bug reports, I think this deserves much wider appreciation. For the case of patches to source code the human sending the patch will have dedicated some time and effort to writing it, if itβs their first patch then they will have probably checked it a lot and maybe sought advice from people they regard as skilled. If they have a history of sending patches for the project they will probably do fewer checks but the quality of their work would be higher. In every case thereβs some minimum level of quality that you can expect from a human who has gone to the effort of finding a bug and writing code to try and fix it.
This doesnβt mean that all code written by humans is good, I have written code thatβs objectively bad on many occasions and I have also written code thatβs good for my scenario but bad for others. For example the first patch I sent to the Linux kernel made all possible configuration settings of an ISA NE2000 network card be detected by the kernel (a clear benefit when using hardware I had available) but was rejected by Linus because it would break some ISA SCSI cards. So while being unsuitable for inclusion in the main kernel source tree the patch wasnβt bad for everyone and the change was clear and easy to check. I had of course checked the code many times before submitting it because I didnβt want to waste Linusβ time on rubbish code.
When you receive something produced by a human you will consider that the work was done by someone who spent some effort on it and did it for a reason. It may be misguided or even hostile in some rare cases but it is definitely worth some consideration. If you think of the output of LLMs in the same way you will give them much more consideration than they deserve. I think that the output of a LLM should not be given any more consideration than the first hit from a web search engine, it might be good but it might also be ridiculously wrong. I think in many ways LLM output should be treated with less trust than the first result from a web search because LLMs are designed to be convincing as an answer to your question unlike the cases where a web page has the wrong answer and itβs more obviously wrong.
This is going to become more of an issue as LLMs give responses that are more human-like. Early LLMs would refuse to advise on crimes. Currently ChatGPT will deflect for example when asking about advice for writing a crime novel it recommends to have the action of finding an employee to bribe happen outside the plot and concentrate on interactions within the group. One recent model I tried gave a human style conversation about how it felt uncomfortable about discussing such things in a similar manner to a law abiding person being coerced into helping with a criminal plan.
I expect that the attempts to appear human will increase and that this will become more of a problem. Possibly to the extent that we need to use something like the GPG web of trust to determine who is human.
Currently a scam group is sending out email to the administrators of web sites that link to a variety of dead domains. They claim to be the legitimate operators of the dead domains in question and request that the links change to mirrors of the web sites under en-html.org. Their end goal appears to game search engines by getting links from a lot of old web pages and later use that high ranking for selling SEO optimisation.
If you have made the mistake of linking to something under en-html.org then I recommend that you change the link to the original content on archive.org.
If you receive email from the en-html.org domain then ignore it, or configure your mail server to reject it.
I am seriously considering configuring the DNS servers I run to return no valid data for any requests under that domain.
Also I will reject all future requests to update links in old blog posts. The very small number of people who want to see what I linked to in old posts can use archive.org.
Related posts:
Advertising a Scam Below is a strange Google advert that appeared on my...
This is a copy of an email I sent at work. I'm not sure I
should be making noise about this here, feedback welcome.
This is your bi-yearly reminder that time is changing soon! October 25th
in Europe, November 1st in North America. Less people in Canada are
changing this year, with BC, Alberta, Manitoba and Northwest
Territories getting rid of DST.
What's happening?
Some places in the world implement what is called Daylight saving time
or DST:
Normally, you shouldn't have to do anything: computers automatically
change time following local rules, assuming they are correctly
configured, provided recent updates have been applied in the case of a
recent change in said rules (because yes, this happens, and happened
this year, and yes, you need to upgrade your software!).
Of course, appliances like your microwave oven will likely not change
time and will need to adjusted unless they are so-called "smart", in
which case they are part of the skynet botnet and should be destroyed.
If your clock is flashing "0:00" or "12:00", you have no action to take
to adapt to this change, lucky you.
If you haven't changed time in six months, congratulations, your clock
will be accurate again!
In any case, you should still consider DST because it might affect some
of your meeting schedules, particularly if you set up a new meeting
schedule in the last 6 months and forgot to consider this change.
If your location does not have DST
Properly scheduled meetings affecting multiple time zones are set in UTC
time, which does not change. So if your location does not observer
time changes, your (local!) meeting time will not change.
But be aware that some other folks attending your meeting might have
the DST bug and their meeting times will change.
Be kind to those poor souls which might be missing meetings by a full
hour because time flies backwards for them.
If you do observe DST
If you are affected by daylight savings, your local meeting times
will change for UTC meetings. Normally, your meeting times are
scheduled to take this into account and the new hours should be
reasonable.
But now is a good time to verify that. Take a look at your schedule for
the next couple of weeks and reschedule meetings before the daylight
saving come up to avoid too much disruption. You have only a couple of
weeks to do so right now.
When do times change, how, and and where?
As regular readers will remember, the rule of thumb is:
Spring forward, fall backwards.
That is, during the season of Spring, the clocks move forward, and
during the Fall (like right now), they move backwards. That is in the
northern hemisphere, but then the southern hemisphere is often saner and
doesn't switch anyways.
So time will move backwards which means an extra hour of sleep. Unless
you have children or bad sleep, in which case your body doesn't care
about what the clock says and will wake up one hour earlier than what it
should.
And of course, this doesn't happen everywhere at once, so let's see when
it happens where.
Europe
The dance starts in Europe.
The change happens on the last Sunday in October at 01:00 UTC (not local
time!), that is October 25th. If you are in the central European
timezone, also known as Amsterdam, Berlin, or Paris time depending on
your national affiliation, that essentially means that at 2:59 local the
clocks will fall back to 2:00 instead of going to 3:00.
Concretely, set your watch back one hour before going to bed, go to bed
at the normal time, and enjoy an extra hour of sleep or leisure.
If you have kids, you might want to start getting them to bed slightly
earlier every day for a week before the change so they take time getting
used to the change. If you have trouble sleeping in the morning, find
your inner child and do that to yourself as well.
USA / Canada
Then it's the US[1] and Canada[2] joining the dance, on the First Sunday in
November at 02:00 local (not UTC!), that is, I believe, November 1st
2026.
This means that, at 1:59, the clocks will flip to 1:00, instead of 2:00.
Concretely, do like the Europeans and tweak your clock before going to
bed.
That is a little less than four weeks from now.
[1] except Arizona (except the Navajo nation), US territories, and
Hawaii
[2] except Yukon, Saskatchewan, (newly) British Columbia, (newly)
Alberta, (newly) Northwest Territories, (newly) Manitoba, one island in
Nunavut (Southampton Island), one town in Ontario (Atikokan) and small
parts of Quebec (Le Golfe-du-Saint-Laurent)
Other places with DST
This time again, I must apologize to the people of Cuba, Lebanon,
Israel, Palestine, Egypt, Chile, Australia, and New Zealand, as you fine
folks all have your own DST rules that are omitted here for brevity. I
rely on this page from Wikipedia to be updated by time nerds accurately
for this message, and it should provide you with a rough idea of what's
coming:
In general, changes also happen in October, but either on different
times or different days, except in the south hemisphere, where they
might happen in September (oops, sorry NZ folks, I'm late!).
Places without DST
Everyone else, enjoy, you're on the right side of history, and we thank
you for the good example you give us.
Changes since last time
There's been lots of changes since last time:
British Columbia moved to permanent -07 on 2026-03-09, that is it
will not change to normal time in November
Alberta moved to permanent -06 on 2026-06-18, similar to BC above.
Canadaβs Northwest Territories moved to permanent -06 on 2026-08-21,
matching Alberta.
Manitoba moves to permanent -05 on 2026-10-31.
Morocco moves to permanent +00 on 2026-09-20.
Moldova has used EU transition times since 2022, but the tz database
only noticed in 2026
This is my interpretation of the changes announced on the tzdata mailing
list here:
It also seems like the US government might finally adopt a permanent
daylight saving change bill in 2026, as the "Sunshine protection act"
pass the house in July:
True to form, this was associated with absolutely ridiculous pressure
from Donald Trump against republicans (his own party!) objecting to the
change:
On July 14, 2026, the House passed a Sunshine Protection Act bill
backed by President Trump. Nevertheless, the bill was opposed in the
Senate by Republicans, including Senator Cotton. In response, on
October 3, 2026, Trump shared a post on Truth Social urging Cotton to
approve the bill, where he revealed Cotton's personal cellphone number
and called on people to call him.
Given that the last time the US did a major change to the daylight
savings policy (in 2005), Canada followed suit to stay in sync, it's
quite possible Trump's mad dash might actually finish getting rid of DST
in North America:
This was my hundred-forty-seventh month that I did some work for the Debian LTS initiative, started by Raphael Hertzog at Freexian.
During my allocated time I uploaded or worked on:
[DLA 4804-1] libsmpp34 security update to fix one CVE in Bookworm related to an out of bound read.
[#1149107] trixie-pu of libsmpp34 has been created and wait for review by the release team.
[DLA 4805-1] mkvtoolnix security update to fix one CVE in Bookworm related to a heap buffer overflow.
[DLA 4806-1] pgextwlist security update to fix one CVE in Bookworm related to substituting extension schemas or owners matching [β$β\].
[ELA-1837-1] mkvtoolnix gimp security update to fix one CVE in Bullseye to a heap buffer overflow.
[osmo-iuh] upload to fix a CVE related to a reachable assertion in Sid.
Besides doing these uploads, the month was filled with unscheduled meetings, discussions and explanations, all basically around the role of FD.
During my week of FD duties, I had to become familiar with new tools. As there are several things that FD has to do over and over again, I tried to automate this a bit. My experiments went well and I think this can be used to make FD duties less repetitive.
I also continued my work on cups and hplip and I am confident that I can do uploads in October.
Last but not least I spend some time on security-master to assist others (especially the kernel team) with uploads to Bookworm and Bullseye.
In case you need to get a complete clone of the security-tracker, using the option βdeepen n might be of help. Unfortunately in case you choosed n too high and some kind of error appears, something gets into a mess and you need to start almost from the beginning (some objects are still present and are used again). If you want to run a script, a value of 10 might be a good choice. You donβt have to deepen back to the beginning. At some point in time you can just βunshallow and get the whole rest. Afterwards doing a git gc is highly recommended.
Debian Printing
This month I uploaded a new upstream version or a bugfix version of:
β¦ hplip to unstable, to fix an expired certificate and some bugs.
A new and exciting version of our still-new package myman reached CRAN this morning, and has been
built for r2u and on
r-universe. The
matching Python package
has also been updated. The package offers nineteen hundred eighty four
βMy man β¦β posts by Kevin Kruse
made on bsky during the summer of 2026.
Each wave picked at one particular public persona. This package wrapse
these up in the style of packages like fortunes or gaussfacts.
A sample usage illustration shows how to extract by pattern, and
shows posts from the two most recent waves:
>library(myman)>myman("maitre")My man looks like he's inquiring with the maitre'd about the house curly fries.-- about Howard Lutnick on 2026-08-28My man looks like a maitre'd who deeply doubts you have a reservation. -- about Scott Bessent on 2026-08-31My man looks like he's asked the maitre'd to remove a party of four he finds visuallyunpleasant. -- about Scott Bessent on 2026-08-31My man looks like the maitre'd at a very exclusive restaurant called The Berghof.-- about JD Vance on 2026-09-13My man looks like he's asking the maitre'd where they source their corn dogs.-- about Palmer Luckey on 2026-10-02My man looks like Data had to borrow a sports jacket from the maitre'd. -- about Elon Musk on 2026-10-05>
One can also subset by βtargetβ, or sample randomly (which is the
default).
As noted during the initial
announcement last week, wave eight did not make it into the initial
CRAN release as it happened
while the package was under review. Waves nine and ten occured more or
less while I was out of town last weekendβso this release now brings
three new waves to the CRAN package! We also added two new helper
functions to extract the underlying data frame object, and tabulate the
targetted men.
To align the version number with the count of post βwavesβ, we
switched to version 0.10.0 for this release and the corresponding Python package release so
that both implementations now have the same version number.
The NEWS entry for this release follows.
Changes in version 0.10.0
(2026-10-08)
New waves nine (100 posts) and ten (190 posts) made last week;
total is now 1984 posts
New helper functions posts() and men()
retrieving data.frame of posts and tabulation of targets
Internal post gathering and aggregation functions have been
updated and generalized
Versioning scheme now goes with post waves (also for Python
sibbling)
I've always known that the browser uses the Domain Name Resolution (DNS) system to convert domain names like aezign.com.au into raw IP addresses, but have never actually understood how it works, outside of making sure my network config uses 1.1.1.1 or 8.8.8.8 instead of the crappy one the ISP provides. In this article, I set out to understand what nameservers do, and how they help anything that uses the internet find the right records.
DNS in a nutshell
A well-used analogy is a phonebook; when you have someone's name and need to find their phone number, you look through the phonebook. Overall, DNS does the same thing: when you enter an address like example.com, your browser will contact a DNS server (the "phonebook") to determine its IP addresses. On Linux (or MacOS) systems, you can use dig on the command line to perform a DNS lookup and see what this looks like:
$ dig example.com
; <<>> DiG 9.20.29-1-Debian <<>> example.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 42782
;; flags: qr rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;example.com. IN A
;; ANSWER SECTION:
example.com. 221 IN A 104.20.23.154
example.com. 221 IN A 172.66.147.243
;; Query time: 20 msec
;; SERVER: 1.1.1.1#53(1.1.1.1) (UDP)
;; WHEN: Sun Sep 27 21:07:50 AWST 2026
;; MSG SIZE rcvd: 72
Here, you can see the DNS server being used is 1.1.1.1 (Cloudflare's DNS server), and that it found the IPs 104.20.23.154 and 172.66.147.243 associated with the domain name example.com. Your browser will then contact these IP addresses directly to load the website you are trying to open.
Configuring DNS servers
You can configure DNS servers through your operating system's network settings, and for some applications such as Firefox, with the application itself. If you don't manually set a DNS server, your computer will use the ones provided by your modem. If those are not configured, the default defined by the modem manufacturer or your ISP will be used.
The most well-used and reliable DNS servers you can use are Cloudflare's (1.1.1.1 and 1.0.0.1) and Google's (8.8.8.8 and 8.8.4.4). Having more than one DNS server means that if the first one in the list doesn't work or doesn't find the matching IPs for a domain name, it will move to the next one in the list, a backup DNS server.
DNS servers must be configured as IP addresses. If your computer tried to use a DNS server at a domain name, then where would your computer find the IP address for that domain name? It wouldn't be able to, as this is the exact gap that DNS servers fulfill.
Records
DNS is not used only to find IPs for a domain, but a whole host of other things. Collectively they are referred to as "records" (or "DNS records"). Each record is associated to a domain (e.g. example.com) or a subdomain (e.g. bar.example.com, foo.bar.example.com) Here are some of the most common and important types:
A - Map to an IPv4 address (e.g. 104.20.23.154).
This is the fundamental function of DNS: to map from a domain name to a server's IP address. Multiple A records leads to load balancing, where browsers will randomly select one of the returned IP addresses such that traffic is evenly split between them.
AAAA - Map to an IPv6 address (e.g. 2606:4700:10::6814:179a).
This serves the same purpose as A records but for IPv6. Multiple AAAA records results in the same load balancing behaviour. All modern websites should have both an A record for legacy compatibility, and an AAAA record to future-proof for the gradual shift towards IPv6 addresses.
CNAME - Alias to another domain name (e.g. example.com).
DNS servers will recursively lookup records for the aliased domain name until they reach an IP address (in an A or AAAA record).
MX - Specify a Mail Exchange server (e.g. mail.example.com).
This record tells email SMTP servers where to direct emails to. For instance, the MX record for aezign.com.au is mail.aezign.au; this directs SMTP servers like Gmail and Outlook to send emails to the mailserver running at mail.aezign.au. This also allows specifying a priority field, so that you can define multiple MX records (such as a main and a backup) and consumers will try to connect to the listed mailservers from lowest priority to highest.
TXT - Store arbitrary text data
This is most commonly used for site verification (for instance, to link your website to Google Search Console) and for email security measures like DKIM, DMARC, and SPF.
NS - Specify the nameservers for a domain (e.g. zeus.ns.cloudflare.com).
These authoritative nameservers are responsible for providing all the other record types when needed.
dig lets you query each of these. For instance, to list the MX records against aezign.com.au:
You can see that gmail.com has several mailservers configured as redundancies due to the Gmail's sheer scale. gmail-smtp-in.l.google.com has the highest priority of 5, so SMTP servers will attempt to contact that mailserver first, followed by alt1.gmail-smtp-in.l.google.com and so on.
Nameservers
Now there needs to be somewhere for DNS records against a domain name to be defined, right? This is where nameservers come in.
Nameservers are DNS servers that answer queries about the domains they are authoritative for i.e. the domains they are in charge of. For example, the authoritative nameservers for example.com are hera.ns.cloudflare.com and elliott.ns.cloudflare.com, which means Cloudflare's nameservers are the source of truth when looking up DNS records for example.com. You can check this with dig:
Nameservers are configured with your domain name's registrar. The nameserver you set dictates where your remaining DNS records will live. For example.com, this means you would configure all your records on Cloudflare's dashboard, since they are responsible for the domain's DNS records.
Recursive resolution
The DNS servers that our computers use follow a recursive resolution process behind-the-scenes to find the authoritative nameservers for a domain, followed by the records you are querying.
Finding the root nameservers
Due to the recursive nature of the resolution process, there needs to be a starting point: the root (.) nameservers. The resolver uses a fixed list of root nameservers called root hints issued by IANA (named.root), to locate and contact the root nameservers. Resolvers will have this provided to them initially, and can dynamically update it themselves by querying one of the root nameservers for the root NS records to obtain a fresh list. For instance, we can query 198.41.0.4 (a.root-servers.net) to obtain a new list of root nameservers:
$ dig @198.41.0.4 NS .
; <<>> DiG 9.20.29-1-Debian <<>> @198.41.0.4 NS .
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 43677
;; flags: qr aa rd; QUERY: 1, ANSWER: 13, AUTHORITY: 0, ADDITIONAL: 27
;; WARNING: recursion requested but not available
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;. IN NS
;; ANSWER SECTION:
. 518400 IN NS l.root-servers.net.
. 518400 IN NS j.root-servers.net.
. 518400 IN NS f.root-servers.net.
. 518400 IN NS h.root-servers.net.
. 518400 IN NS d.root-servers.net.
. 518400 IN NS b.root-servers.net.
. 518400 IN NS k.root-servers.net.
. 518400 IN NS i.root-servers.net.
. 518400 IN NS m.root-servers.net.
. 518400 IN NS e.root-servers.net.
. 518400 IN NS g.root-servers.net.
. 518400 IN NS c.root-servers.net.
. 518400 IN NS a.root-servers.net.
;; ADDITIONAL SECTION:
l.root-servers.net. 518400 IN A 199.7.83.42
l.root-servers.net. 518400 IN AAAA 2001:500:9f::42
j.root-servers.net. 518400 IN A 192.58.128.30
j.root-servers.net. 518400 IN AAAA 2001:503:c27::2:30
f.root-servers.net. 518400 IN A 192.5.5.241
f.root-servers.net. 518400 IN AAAA 2001:500:2f::f
h.root-servers.net. 518400 IN A 198.97.190.53
h.root-servers.net. 518400 IN AAAA 2001:500:1::53
d.root-servers.net. 518400 IN A 199.7.91.13
d.root-servers.net. 518400 IN AAAA 2001:500:2d::d
b.root-servers.net. 518400 IN A 170.247.170.2
b.root-servers.net. 518400 IN AAAA 2801:1b8:10::b
k.root-servers.net. 518400 IN A 193.0.14.129
k.root-servers.net. 518400 IN AAAA 2001:7fd::1
i.root-servers.net. 518400 IN A 192.36.148.17
i.root-servers.net. 518400 IN AAAA 2001:7fe::53
m.root-servers.net. 518400 IN A 202.12.27.33
m.root-servers.net. 518400 IN AAAA 2001:dc3::35
e.root-servers.net. 518400 IN A 192.203.230.10
e.root-servers.net. 518400 IN AAAA 2001:500:a8::e
g.root-servers.net. 518400 IN A 192.112.36.4
g.root-servers.net. 518400 IN AAAA 2001:500:12::d0d
c.root-servers.net. 518400 IN A 192.33.4.12
c.root-servers.net. 518400 IN AAAA 2001:500:2::c
a.root-servers.net. 518400 IN A 198.41.0.4
a.root-servers.net. 518400 IN AAAA 2001:503:ba3e::2:30
;; Query time: 476 msec
;; SERVER: 198.41.0.4#53(198.41.0.4) (UDP)
;; WHEN: Mon Sep 28 00:01:08 AWST 2026
;; MSG SIZE rcvd: 811
Querying the root nameservers
For example.com, the first step is to query the root (.) nameservers to find the nameservers for the .com top-level domain (TLD). Using a.root-servers.net again:
$ dig @198.41.0.4 NS com
; <<>> DiG 9.20.29-1-Debian <<>> @198.41.0.4 NS com
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 22528
;; flags: qr rd; QUERY: 1, ANSWER: 0, AUTHORITY: 13, ADDITIONAL: 27
;; WARNING: recursion requested but not available
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;com. IN NS
;; AUTHORITY SECTION:
com. 172800 IN NS l.gtld-servers.net.
com. 172800 IN NS j.gtld-servers.net.
com. 172800 IN NS h.gtld-servers.net.
com. 172800 IN NS d.gtld-servers.net.
com. 172800 IN NS b.gtld-servers.net.
com. 172800 IN NS f.gtld-servers.net.
com. 172800 IN NS k.gtld-servers.net.
com. 172800 IN NS m.gtld-servers.net.
com. 172800 IN NS i.gtld-servers.net.
com. 172800 IN NS g.gtld-servers.net.
com. 172800 IN NS a.gtld-servers.net.
com. 172800 IN NS c.gtld-servers.net.
com. 172800 IN NS e.gtld-servers.net.
;; ADDITIONAL SECTION:
l.gtld-servers.net. 172800 IN A 192.41.162.30
l.gtld-servers.net. 172800 IN AAAA 2001:500:d937::30
j.gtld-servers.net. 172800 IN A 192.48.79.30
j.gtld-servers.net. 172800 IN AAAA 2001:502:7094::30
h.gtld-servers.net. 172800 IN A 192.54.112.30
h.gtld-servers.net. 172800 IN AAAA 2001:502:8cc::30
d.gtld-servers.net. 172800 IN A 192.31.80.30
d.gtld-servers.net. 172800 IN AAAA 2001:500:856e::30
b.gtld-servers.net. 172800 IN A 192.33.14.30
b.gtld-servers.net. 172800 IN AAAA 2001:503:231d::2:30
f.gtld-servers.net. 172800 IN A 192.35.51.30
f.gtld-servers.net. 172800 IN AAAA 2001:503:d414::30
k.gtld-servers.net. 172800 IN A 192.52.178.30
k.gtld-servers.net. 172800 IN AAAA 2001:503:d2d::30
m.gtld-servers.net. 172800 IN A 192.55.83.30
m.gtld-servers.net. 172800 IN AAAA 2001:501:b1f9::30
i.gtld-servers.net. 172800 IN A 192.43.172.30
i.gtld-servers.net. 172800 IN AAAA 2001:503:39c1::30
g.gtld-servers.net. 172800 IN A 192.42.93.30
g.gtld-servers.net. 172800 IN AAAA 2001:503:eea3::30
a.gtld-servers.net. 172800 IN A 192.5.6.30
a.gtld-servers.net. 172800 IN AAAA 2001:503:a83e::2:30
c.gtld-servers.net. 172800 IN A 192.26.92.30
c.gtld-servers.net. 172800 IN AAAA 2001:503:83eb::30
e.gtld-servers.net. 172800 IN A 192.12.94.30
e.gtld-servers.net. 172800 IN AAAA 2001:502:1ca1::30
;; Query time: 772 msec
;; SERVER: 198.41.0.4#53(198.41.0.4) (UDP)
;; WHEN: Mon Sep 28 00:09:25 AWST 2026
;; MSG SIZE rcvd: 828
Querying the TLD nameservers
Now the resolver has .com nameservers and their IPs. We can use one of these, such as 192.5.6.30 (a.gtld-servers.net) to find the nameservers for example.com:
$ dig @192.5.6.30 NS example.com
; <<>> DiG 9.20.29-1-Debian <<>> @192.5.6.30 NS example.com
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 37419
;; flags: qr rd; QUERY: 1, ANSWER: 0, AUTHORITY: 2, ADDITIONAL: 13
;; WARNING: recursion requested but not available
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;example.com. IN NS
;; AUTHORITY SECTION:
example.com. 172800 IN NS hera.ns.cloudflare.com.
example.com. 172800 IN NS elliott.ns.cloudflare.com.
;; ADDITIONAL SECTION:
hera.ns.cloudflare.com. 172800 IN A 108.162.192.162
hera.ns.cloudflare.com. 172800 IN A 172.64.32.162
hera.ns.cloudflare.com. 172800 IN A 173.245.58.162
hera.ns.cloudflare.com. 172800 IN AAAA 2606:4700:50::adf5:3aa2
hera.ns.cloudflare.com. 172800 IN AAAA 2803:f800:50::6ca2:c0a2
hera.ns.cloudflare.com. 172800 IN AAAA 2a06:98c1:50::ac40:20a2
elliott.ns.cloudflare.com. 172800 IN A 108.162.195.228
elliott.ns.cloudflare.com. 172800 IN A 162.159.44.228
elliott.ns.cloudflare.com. 172800 IN A 172.64.35.228
elliott.ns.cloudflare.com. 172800 IN AAAA 2606:4700:58::a29f:2ce4
elliott.ns.cloudflare.com. 172800 IN AAAA 2803:f800:50::6ca2:c3e4
elliott.ns.cloudflare.com. 172800 IN AAAA 2a06:98c1:50::ac40:23e4
;; Query time: 80 msec
;; SERVER: 192.5.6.30#53(192.5.6.30) (UDP)
;; WHEN: Mon Sep 28 00:12:19 AWST 2026
;; MSG SIZE rcvd: 359
Finding the domain's nameserver
The response tells the resolver that the nameservers for example.com are hera.ns.cloudflare.com and elliott.ns.cloudflare.com. Now since these Cloudflare nameservers are also .com domains and Cloudflare is so commonly used, the .com nameserver provides optional sibling glue records as defined in RFC 9471 to prevent further roundtrips and optimize the process.
If we ignore the sibling glue records provided, the resolver would have to ask the .com nameservers for the nameservers for cloudflare.com:
$ dig @192.5.6.30 NS cloudflare.com
; <<>> DiG 9.20.29-1-Debian <<>> @192.5.6.30 NS cloudflare.com
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 40307
;; flags: qr rd; QUERY: 1, ANSWER: 0, AUTHORITY: 5, ADDITIONAL: 21
;; WARNING: recursion requested but not available
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;cloudflare.com. IN NS
;; AUTHORITY SECTION:
cloudflare.com. 172800 IN NS ns3.cloudflare.com.
cloudflare.com. 172800 IN NS ns5.cloudflare.com.
cloudflare.com. 172800 IN NS ns4.cloudflare.com.
cloudflare.com. 172800 IN NS ns6.cloudflare.com.
cloudflare.com. 172800 IN NS ns7.cloudflare.com.
;; ADDITIONAL SECTION:
ns3.cloudflare.com. 172800 IN A 162.159.0.33
ns3.cloudflare.com. 172800 IN A 162.159.7.226
ns3.cloudflare.com. 172800 IN AAAA 2400:cb00:2049:1::a29f:21
ns3.cloudflare.com. 172800 IN AAAA 2400:cb00:2049:1::a29f:7e2
ns5.cloudflare.com. 172800 IN A 162.159.2.9
ns5.cloudflare.com. 172800 IN A 162.159.9.55
ns5.cloudflare.com. 172800 IN AAAA 2400:cb00:2049:1::a29f:209
ns5.cloudflare.com. 172800 IN AAAA 2400:cb00:2049:1::a29f:937
ns4.cloudflare.com. 172800 IN A 162.159.1.33
ns4.cloudflare.com. 172800 IN A 162.159.8.55
ns4.cloudflare.com. 172800 IN AAAA 2400:cb00:2049:1::a29f:121
ns4.cloudflare.com. 172800 IN AAAA 2400:cb00:2049:1::a29f:837
ns6.cloudflare.com. 172800 IN A 162.159.3.11
ns6.cloudflare.com. 172800 IN A 162.159.5.6
ns6.cloudflare.com. 172800 IN AAAA 2400:cb00:2049:1::a29f:30b
ns6.cloudflare.com. 172800 IN AAAA 2400:cb00:2049:1::a29f:506
ns7.cloudflare.com. 172800 IN A 162.159.4.8
ns7.cloudflare.com. 172800 IN A 162.159.6.6
ns7.cloudflare.com. 172800 IN AAAA 2400:cb00:2049:1::a29f:408
ns7.cloudflare.com. 172800 IN AAAA 2400:cb00:2049:1::a29f:606
;; Query time: 163 msec
;; SERVER: 192.5.6.30#53(192.5.6.30) (UDP)
;; WHEN: Mon Sep 28 00:26:53 AWST 2026
;; MSG SIZE rcvd: 573
Here, the .com nameserver provides in-domain glue records for cloudflare.com's nameservers. Without these, there would be a circular dependency, since the nameserver named is a subdomain of the domain being queried for. This is because nameservers are always defined by name, which means that in order to actually contact the nameserver, your computer needs to obtain its IP address with another DNS lookup.
Next, we'd use one of these nameservers, say ns3.cloudflare.com, to obtain the IP for hera.ns.cloudflare.com:
$ dig @162.159.0.33 hera.ns.cloudflare.com
; <<>> DiG 9.20.29-1-Debian <<>> @162.159.0.33 hera.ns.cloudflare.com
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 16801
;; flags: qr aa rd; QUERY: 1, ANSWER: 3, AUTHORITY: 0, ADDITIONAL: 1
;; WARNING: recursion requested but not available
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;hera.ns.cloudflare.com. IN A
;; ANSWER SECTION:
hera.ns.cloudflare.com. 86353 IN A 108.162.192.162
hera.ns.cloudflare.com. 86353 IN A 173.245.58.162
hera.ns.cloudflare.com. 86353 IN A 172.64.32.162
;; Query time: 123 msec
;; SERVER: 162.159.0.33#53(162.159.0.33) (UDP)
;; WHEN: Mon Sep 28 01:11:37 AWST 2026
;; MSG SIZE rcvd: 99
Querying the domain's authoritative nameserver
Now we've reached the authoritative nameserver for example.com! The last thing to do is to fetch the A record for example.com to obtain its server's IP address:
$ dig @108.162.192.162 example.com
; <<>> DiG 9.20.29-1-Debian <<>> @108.162.192.162 example.com
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 43082
;; flags: qr aa rd; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1
;; WARNING: recursion requested but not available
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;example.com. IN A
;; ANSWER SECTION:
example.com. 300 IN A 104.20.23.154
example.com. 300 IN A 172.66.147.243
;; Query time: 123 msec
;; SERVER: 108.162.192.162#53(108.162.192.162) (UDP)
;; WHEN: Mon Sep 28 01:13:47 AWST 2026
;; MSG SIZE rcvd: 72
At last, we've determined that the IP addresses of the servers behind example.com are 104.20.23.154 and 172.66.147.243. This matches our earlier lookup directly against your computer's configured DNS server.
The DNS servers your computer uses encapsulate this recursive resolution process behind the scenes, so that your computer only needs to make one round trip, and the servers can use their much faster data center connections and caching to optimize the process and make the query as fast as possible.
Checking DNS records
To check a website's records, the first thing to do is to ensure the domain name is pointed at the correct nameserver. For instance, aezign.com.au is supposed to use Cloudflare, since that is where all the DNS records have been set up. We can verify this using dig, (or alternatively with online tools like dnschecker.org):
If this is not set correctly, the DNS records you configure will not work, as resolvers will be fetching records from somewhere else. In most cases, you can update this in your domain registrar's settings. This is especially common for new domain names, which default to the registrar's nameservers; unless you plan to use their built-in DNS management, you'll need to point them to your chosen provider.
Now we can check that the DNS records configured match those your DNS server returns. For aezign.com.au, these are the MX and TXT records configured on Cloudflare.
The MX record and some TXT records configured for aezign.com.au on Cloudflare's dashboard.
We can check these using dig, and using the +short option to show only the content of the records.
As we can see, this matches, which means that the nameservers for aezign.com.au are correct and the DNS records are what we intend them to be.
Note that if you've recently changed the nameserver or DNS records for your domain, it can take anywhere between 10 seconds and 24 hours for the changes to take effect and propagate across the different DNS servers globally. Tools like dnschecker.org can check your records on different DNS servers worldwide to see if they're reflecting your changes. The reason for this is caching: DNS servers like 1.1.1.1 will cache results so it doesn't have to do the full resolution process every time, and uses the Time-To-Live (TTL) you configure in your DNS records as a guideline for caching duration.
Today, we're looking at a rewrite that rarely differs for matching,
but can mean quite a lot for understanding the code. Say that you
have a signed variable and the compiler has suddenly decided to make
an unsigned comparison:
if ((u32) (x - 4) < 2) {
What's happening is that it uses a controlled overflow to do two
signed comparisons using one unsigned comparison. How does this work?
Since the comparison is unsigned, this is trivially equivalent
to the first expression:
if ((u32) (x - 4) >= 0 && (u32) (x - 4) < 2) {
and since negative numbers compare larger than 6 in unsigned comparisons,
nothing prevents us from adding 4 on each side of the inequality signs:
Japanese input method editors (IMEs) such as Mozc are essential for typing
Japanese (yes, skk, anthy and more, but it's out-of-scope in this article). Testing them is a surprisingly fiddly job: an IME behaves differently
depending on the desktop environment (GNOME, KDE, Xfce, Budgie), the input
framework (ibus, fcitx5, uim), and the Wayland or X11 session. Setting up a
fresh VM for every combination from scratch is slow and repetitive, and
breaking your host's environment while experimenting is no fun.
virt-japanese-desktop is a small set of toy scripts that solves exactly this
problem. It builds ready-to-use virtual machines - each with a desktop
environment and a Japanese IME already installed and configured - so you can
start typing Japanese within a minute of booting, and throw the whole thing
away without any impact on your host.
What it gives you
A single make command produces a qcow2 image that combines one of four
desktops with one of three IMEs:
ibus-mozc
fcitx5-mozc
uim-mozc
GNOME
β
β
β
KDE
β
β
β
Xfce
β
β
β
Budgie
β
β
β
The one gap - uim on Budgie - is a real technical limitation, not an
oversight: Budgie runs the labwc compositor, which implements
zwp_input_method_v2, while uim-wayland only speaks the older
zwp_input_method_v1 (KWin/Weston). On Budgie you use ibus-mozc or fcitx5-mozc
instead.
All images come with the Japanese locale, fonts, and the Asia/Tokyo time
zone pre-configured, plus the SSH key of your choice. Input switching is wired
up out of the box: Ctrl + Space or Shift + Space for your IMEs to enable it.
The layered-image trick
The core idea is that the images are stacked qcow2 overlay layers, one on
top of another:
debian-sid-nocloud-amd64-daily.qcow2 ... base image you download
ββ unstable-japanese-template.qcow2 ... locale, fonts, user, SSH
ββ unstable-<DE>-template.qcow2 ... a desktop environment
ββ unstable-<DE>-<IME>.qcow2 ... desktop + IME, configured
ββ unstable-<DE>-<IME>.workspace.qcow2 ... the image you test in
This makes both building and resetting fast. The first build downloads and
customizes the base image and takes a while, but every later step only adds a
thin overlay. When a test breaks the VM, you do not rebuild anything - you
simply delete the top workspace layer and recreate it. That is all it takes
to return to a pristine state:
rm /tmp/unstable-gnome-ibus-mozc.workspace.qcow2
make gnome-ibus-mozc
The images reference their backing files by relative path, so you can move
an entire stack anywhere you like as long as the images stay together.
A platform for experimenting with bleeding-edge IMEs
The base system is Debian unstable (sid), and the experimental
repository is already added. That makes the project a convenient platform for
testing not just the IME packages in sid but also the ones still being
developed in experimental - exactly what the project was built for.
The keyboard layout of the VM follows the layout of your host (read from
/etc/default/keyboard, with a fallback chain to localectl and finally
us).
Quick start
# 1. Install the tools (Debian/Ubuntu example)
sudo apt install qemu-utils libguestfs-tools virt-install curl
# 2. Save your SSH public key
curl --location https://github.com/USERNAME.keys --output pubkey.pub
# 3. Download the base Debian sid image
make download
# 4. Build a desktop + IME (first build takes a while)
make gnome-ibus-mozc
# 5. Start it as a VM (needs the libvirt daemon, qemu:///system)
./scripts/make-virsh-image.sh virt-gnome-ibus-mozc /tmp/unstable-gnome-ibus-mozc.workspace.qcow2
Log in as debian (password debian) and press Ctrl + Space (Shift + Space) to start
typing Japanese. The VM is registered in libvirt, so you can manage it with
virsh or virt-manager - handy for opening the SPICE console or restarting
the machine after a test.
Status
The project is still in the proof-of-concept phase, and it is developed
mainly to test Mozc and other IMEs across desktops and input frameworks.
If you regularly test Japanese IMEs - give it a try.