Search Results: "ag"

23 April 2026

Dirk Eddelbuettel: dtts 0.1.4 on CRAN: Maintenance

Leonardo and I are happy to announce another maintenance release 0.1.4 of our dtts package which has been on CRAN for four years now. dtts builds upon our nanotime package as well as the beloved data.table to bring high-performance and high-resolution indexing at the nanosecond level to data frames. dtts aims to offers the time-series indexing versatility of xts (and zoo) to the immense power of data.table while supporting highest nanosecond resolution. This release, not unlike yesterday s release of nanotime, is driven by recent changes in the bit64 package which underlies it. Michael, who now maintains it, had sent in two PRs to prepare for these changes. I updated continuous integration, and switched to Authors@R, and that pretty much is the release. The short list of changes follows.

Changes in version 0.1.4 (2026-04-23)
  • Continuous integration has received some routine updates
  • Adapt align() column names with changes in 'data.table' (Michael Chirico in #20)
  • Narrow imports to functions used for packages 'bit64', 'data.table' and 'nanotime' (Michael Chirico in #21)

Courtesy of my CRANberries, there is also a [diffstat repor]tbsdiffstat for this release. Questions, comments, issue tickets can be brought to the GitHub repo.

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

Sergio Talens-Oliag: Developing a Git Worktree Helper with Copilot

Over the past few weeks I ve been developing and using a personal command-line tool called gwt (Git Worktree) to manage Git repositories using worktrees. This article explains what the tool does, how it evolved, and how I used GitHub Copilot CLI to develop it (in fact the idea of building the script was also to test the tool).

The Problem: Managing Multiple BranchesI was working on a project with multiple active branches, including orphans; the regular branches are for fixes or features, while the orphans are used to keep copies of remote documents or store processed versions of those documents. The project also uses a special orphan branch that contains the scripts and the CI/CD configuration to store and process the external documents (it is on a separate branch to avoid mixing its operation with the main project code). The plan is trigger a pipeline against the special branch from remote projects to create or update the doc branch for it in our git repository, retrieving artifacts from the remote projects to get the files and put them on an orphan branch (initially I added new commits after each update, but I changed the system to use force pushes and keep only one commit, as the history is not really needed). The original documents have to be changed, so, after ingesting them, we run a script that modifies them and adds or updates another branch with the processed version; the contents of that branch are used by the main branch build process (there we use git fetch and git archive to retrieve its contents). When working on the scripts to manage the orphan branches I discovered the worktree feature of git, a functionality that allows me to keep multiple branches checked out in parallel using a single .git folder, removing the need to use git switch and git stash when changing between branches (until now I ve been a heavy user of those commands). Reading about it I found that a lot of people use worktrees with the help of a wrapper script to simplify the management. After looking at one or two posts and the related scripts I decided to create my own using a specific directory structure to simplify things. That s how I started to work on the gwt script; as I also wanted to test copilot I decided to build it using its help (I have a pro license at work and wanted to play with the cli version instead of integrated into an editor, as I didn t want to learn a lot of new keyboard shortcuts).

The gwt Philosophy: Opinionated and Transparentgwt enforces a simple, filesystem-visible model:
  • Exactly one bare repository named bare.git (treated as an implementation detail)
  • One worktree directory per branch where the directory name matches the branch name
  • Single responsibility: gwt doesn t try to be a general git wrapper; it only handles operations that map cleanly to this layout
The repository structure looks like this:
my-repo/
+-- bare.git/           # the Git repository (internal)
+-- main/               # worktree for branch "main"
+-- feature/api/        # worktree for branch "feature/api"
+-- fix/docs/           # worktree for branch "fix/docs"
+-- orphan-history/     # worktree for the "orphan-history" branch
The tool follows five core design principles:
  1. Explicit over clever: Git commands are not hidden or reinterpreted
  2. Transparent execution: Every operation is printed before it happens
  3. Safe, preview-first operations: Destructive commands default to preview, confirmation, then apply
  4. Shell-agnostic core: The script never changes the caller s working directory (shell wrappers handle that)
  5. Opinionated but minimal: Only commands that fit the layout model are included

Core CommandsThe script provides these essential commands:
  • gwt init <url> Clone a repository and set up the gwt layout
  • gwt convert <dir> Convert an existing Git checkout to the gwt layout
  • gwt add [--orphan] <branch> [<base>] Create a new worktree (optionally orphaned)
  • gwt remove <branch> Remove a worktree and unregister it (asks the user to remove the local branch too, useful when removing already merged branches)
  • gwt rename <old> <new> Rename a branch AND its worktree directory
  • gwt list List all worktrees
  • gwt default [<branch>] Get or set the default branch
  • gwt current Print the current worktree or branch name
Except init and convert all of the commands work inside a directory structure that follows the gwt layout, which looks for the bare.git folder to find the root folder of the structure. As I don t want to hide which commands are really used by the wrapper, all git and filesystem operations pass through a single run shell function that prints each command before executing it. This gives complete visibility into what the tool is doing. Also, destructive operations (remove, rename) default to preview mode:
$ gwt remove feature-old --dry-run
+ git -C bare.git branch -d feature-old
+ git -C bare.git worktree remove feature-old/
Apply these changes? [y/N]:
The user sees exactly what will happen, can verify it s correct, and only then confirm execution.

Incremental Development with CopilotThe gwt script has grown from 597 lines in its original version (git-wt) to 1,111 lines when writing the first draft of this post. This growth happened through incremental, test-driven development, with each feature being refined based on real usage patterns. What follows is a little history of the script evolution written with the help of git log.

Initial versionFirst I wrote a design document and asked copilot to create the initial version of the git-wt script with the original core commands. I started to use the tool with a remote repostory (I made copies of the branches in some cases to avoid missing work) and fixed bugs (trivial ones with neovim, larger ones asking copilot to fix the issues for me, so I had less typing to do).

First command updateOne of the first commands I had to enhance was rename:
  • as I normally use branches with / on their name and my tool checks out the worktrees using the branch name as the path inside the gwt root folder (i.e. a fix/rename branch creates the fix directory and checks the branch inside the fix/rename folder) the rename command had to clean up the empty parent directories
  • when renaming a worktree we move the folders and fix the references using the worktree repair command to make things work locally, but the rename also affects the remote branch reference, to avoid surprises the command unsets the remote branch reference so it can be pushed again using the new name (of course, the user is responsible of managing the old remote branch, as the gwt can t guess what it should do with it).

Integration with the shellAs I use zsh with the Powerlevel10k theme I asked copilot to help me add visual elements to the prompt when working with gwt folders, something that I would have never tried without help, as it would have required a lot of digging on my part on how to do it, as I never looked into it. The initial version of the code was on an independent file that I sourced from my .zshrc file and it prints on the right part of the prompt when we are inside a gwt folder (note that if the folder is a worktree we see the existing git integration text right before it, so we have the previous behavior and we see that it is a gwt friendly repo) and if we are on the root folder or the bare.git folder we see gwt or bare (I added the text because there are no git promts on those folders). I also asked copilot to create zsh autocompletion functions (I only use zsh, so I didn t add autocompletion for other shells). The good thing here is that I wouldn t have done that manually, as it would have required some reading to get it right, but the output of copilot worked and I can update things using it or manually if I need to. One thing I was missing from the script was the possibility of changing the working directory easily, so I wrote a gwt wrapper function for zsh that intercepts commands that require shell cooperation (changing the working directory) and delegates everything else to the core script. Currently the function supports the following enhanced commands:
  • cd [<branch>]: change into a worktree or the default one if missing
  • convert <dir>: convert a checkout, then cd into the initial worktree
  • add [--orphan] <branch> [<base>]: create a worktree, then cd into it on success
  • rename <old> <new>: rename a worktree, then cd into it if we were inside it
Note that the cd command will not work on other shells or if the user does not load my wrapper, but the rest will still work without the working directory changes.

Renaming the commandAs I felt that git-wt was a long name I renamed the tool to gwt, I could have done it by hand, but using copilot I didn t have to review all files by myself and it did it right (note that I have it configured to always ask me before doing changes, as it sometimes tries to do something I don t want and I like to check its changes as I have the files in git repos, I manually add the files when I like the status and if the cli output is not clear I allow it to apply it and check the effects with git diff so I can validate or revert what was done).

The convert commandAfter playing with one repo I added the convert subcommand for migrating existing checkouts, it seemed a simple task at first, but it took multiple iterations to get it right, as I found multiple issues while testing (in fact I did copies of the existing checkouts to be able to re-test each update, as some of the iterations broke them). The version of the function when this post was first edited had the following comment explaining what it does:
# ---------------------------------------------------------------------------
# convert - convert an existing checkout into the gwt layout
# ---------------------------------------------------------------------------
#
# Must be run from the parent directory of <dir>.
#
# Steps:
#   1. Read branch from the checkout's HEAD
#   2. Rename <dir> to <dir>.wt.tmp (sibling, same filesystem)
#   3. Create <dir>/ as the new gwt root
#   4. Move <dir>.wt.tmp/.git to <dir>/bare.git; set core.bare = true
#   5. Fix fetch refspec (bare clone default maps refs directly, no remotes/)
#   6. Add a --no-checkout worktree so git wires up the metadata and
#      creates <dir>/<branch>/.git (the only file in that dir)
#   7. Move that .git file into the real working tree (<dir>.wt.tmp)
#   8. Remove the now-empty placeholder directory
#   9. Move the real working tree into place as <dir>/<branch>
#  10. Reset the index to HEAD so git status is clean
#      (--no-checkout leaves the index empty)
#  11. Create <dir>/.git -> bare.git symlink so plain git commands work
#      from the root without --git-dir
#
# The .git file ends up at the same absolute path git recorded in step 5,
# so no worktree repair is needed. Working tree files are never modified.
The .git link was added when I noticed that I could run commands that don t need the checked out files on the root of the gwt structure, which is handy sometimes (i.e. a git fetch or a git log, that shows the log of the branch marked as default). After playing with commands that used the bare.git folder I updated the init and convert commands to keep the origin refs, ensuring that the remote tracking works correctly.

Improving the add commandWhile playing with the tool on more repos I noticed that I also had to enhance the add command to better handle worktree creation, depending on my needs. Right now the tool supports the following use cases:
  • if the branch exists locally or on origin, it just checks it out.
  • if the branch does not exist, we create it using the given base branch or, if no base is given, the current worktree (if we are in the root folder or bare.git the command fails).
  • as I needed it for my project, I added a --orphan option to be able to create orphan branches directly.

Moving to a single fileEventually I decided to make the tool self contained; I removed the design document (I moved the content to comments on the top of the script and details to comments on each function definition) and added a pair of commands to print the code to source for the p10k and zsh integration (autocompletion & functions), leaving everything in a single file. Now my .zshrc file adds the following to source both things:
# After loading the p10k configuration
if type gwt >/dev/null 2>&1; then
  source <(gwt p10k)
fi
[...]
# After loading autocompletion
if type gwt >/dev/null 2>&1; then
  source <(gwt zsh)
fi

VersioningAs I modified the script I found interesting to use CalVer-based versioning (the version variable has the format YYYY.mm.dd-r#) so I added a subcommand to show its value or bump it using the current date and computing the right revision number.

About the use of copilotAlthough I ve never been a fan of AI tools I have to admit that the copilot CLI has been very useful for building the tool:
  • Rapid prototyping: Each commit represented a small feature or fix that I could implement, test immediately in my actual workflow, and iterate on based on the result
  • Edge case handling: Rather than trying to anticipate every scenario upfront, I could ask Copilot how to handle edge cases as they appeared in real usage
  • Script refinement: Questions like "how do I clean up empty directories after a rename" or "how do I detect if I m inside a specific worktree" were quickly answered with working code
  • Shell integration: The Zsh wrapper and completion system grew from simple prototypes to sophisticated features, with each iteration informed by how I actually used the tool
For example, the convert command started as a simple rename operation, but evolved to also create a .git symlink and intelligently handle various migration scenarios all because I used it repeatedly and refined the implementation each time.

Self-Contained and Opinionatedgwt is deliberately opinionated:
  • Zsh & Powerlevel10k Integration: The tool includes built-in Zsh shell integration, accessed via source <(gwt zsh) and supports adding a prompt segment when using p10k, as described earlier.
  • Directory Structure: The bare.git directory name is non-negotiable. This is how gwt discovers the repository root from any subdirectory, and how the tool knows whether a directory is a gwt repository. The simplicity of this marker means the discovery mechanism is foolproof and requires no configuration.
  • No Configuration Files: gwt deliberately has no configuration. There are no .gwtrc files or config directories. This makes it portable; the tool works the same way everywhere, and repositories can be shared across systems without synchronizing configuration.

From Script to SystemWhat started as a small helper script for managing worktrees has become a complete system:
  1. Core script (gwt): 1,111 lines of pure shell, no external dependencies
  2. Shell integration: Zsh functions and completions
  3. Prompt integration: Powerlevel10k segment
  4. Documentation: Built-in help and design philosophy documentation
The script is self-contained, everything needed for the tool to work is in a single file. This makes it trivial to update (just replace the script) or audit (no hidden dependencies).

Development with AI supportDeveloping gwt with copilot taught me some things:
  • Incremental refinement works well for small tools: Each iteration informed the next, resulting in a tool that handles real use cases elegantly
  • Transparency is a feature: Making operations visible builds confidence and is easier to debug
  • Opinionated tools can be powerful: By constraining the problem space (one bare repo, one worktree per branch), the solution becomes simpler and more robust
  • Shell integration matters: The same core commands are easier to use when they can automatically change directories and provide completions
  • Real-world testing is essential: I wouldn t have discovered the need for automatic directory cleanup or context-aware cd behavior without actually using the tool daily

What was next?The tool is stable and handles my daily workflow well, so my guess is that I would keep using it and fixing issues if or when I found them, but I do not plan to include additional features unless I find a use case that justifies it (i.e. I never added support for some of the worktree subcommands, as it is easier to use the git versions if I ever needed them).

What really happenedWhile editing this post I discovered that I needed to add another command to it and fixed a bug (see below). With those changes and the inclusion of a license and copyright notice (just in case I distribute it at some point) now the script is 1,217 lines long instead of the 1,111 it had when I started to write this entry.

Submodule SupportWhen I converted this blog repository to the gwt format and tried to preview the post using docker compose, it failed because the worktree I was on didn t have the Git submodule initialized. My blog theme is included on the repository as a submodule, and when I used gwt to check out different branches in worktrees, the submodule was not initialized in the new worktrees. This led me to add new internal function and a gwt submodule command to handle submodule initialization; the internal function is called from convert and add (when converting a repo or adding a worktree) and the public command is useful to update the submodules on existing branches.

Path Handling with Branch Names Containing SlashesThe second discovery was a bug in how the tool handled branch names containing slashes (e.g., feature/new-api, docs/user-guide), the worktree directories are created with the branch name as the path, so a branch like feature/new-api would create two nested folders (feature and new-api inside it). However, there was a mismatch in how the zsh wrapper function resolved worktree paths (initially it used shell parameter expansion, i.e. rel="$ cwd#"$REPO_ROOT"/ "), versus how the core script calculated them, causing the cd command to fail or navigate to the wrong location when branch names contained slashes. The fix involved ensuring consistent path resolution throughout the script and wrapper (now it uses a function that processes the git worktree list output), so that gwt cd feature/new-api correctly navigates to the worktree directory regardless of path depth.

Conclusiongwt is a tool that solves a real problem: managing multiple Git branches simultaneously without context-switching overhead. I m sure I m going to keep using it for my projects, as it simplifies some workflows, although I ll also use switch and stash in some cases, but I like the use of multiple worktrees in parallel. In fact I converted this blog repository checkout to the gwt format to work on a separate branch as it felt the right approach even if I m the only one using the repo now, and it helped me improve the tool, as explained before. Also, it was a good example of how to use AI tools like copilot to develop a simple tool and keep it evolving while using it. In any case, although I find the copilot useful and has saved me time, I don t trust it to work without supervision, it worked well, but got stuck some times and didn t do the things as I wanted in multiple occasions. I also have an additional problem now I ve been reading about it, but I don t really know which models to use or how the premium requests are computed (I ve only been playing with it since last month and I ran out of requests the last day of the month on purpose, just to see what happened it stops working ;). On my work machine I ve been using a specific user account with a GitHub Copilot Business subscription and I only used the Anthropic Claude Sonnet 4.6 model and with my personal account I configured the Anthropic Claude Haiku 4.5 model, but I ve only used that to create the initial draft of this post (I ended up rewriting most of it manually anyway) and to review the final version (I m not a native speaker and it was useful for finding typos and improving the style in some parts). I guess I ll try other models with copilot in the future and check other command line tools like aider or claude-code, but probably only using free accounts unless I get a payed account at work, as I have with GitHub Copilot. To be fair, what I will love to be able to do is to use local models (aider can do it), but the machines I have are not powerful enough. I tried to run a simple test and it felt really slow, but when I have the time or the need I ll try again, just in case.

22 April 2026

Dirk Eddelbuettel: nanotime 0.3.14 on CRAN: Upstream Maintenance

Another minor update 0.3.14 for our nanotime package is now on CRAN, and has compiled for r2u (and will have to wait to be uploaded to Debian until dependency bit64 has been updated there). nanotime relies on the RcppCCTZ package (as well as the RcppDate package for additional C++ operations) and offers efficient high(er) resolution time parsing and formatting up to nanosecond resolution, using the bit64 package for the actual integer64 arithmetic. Initially implemented using the S3 system, it has benefitted greatly from a rigorous refactoring by Leonardo who not only rejigged nanotime internals in S4 but also added new S4 types for periods, intervals and durations. This release has been driven almost entirely by Michael, who took over as bit64 maintainer and has been making changes there that have an effect on us downstream . He reached out with a number of PRs which (following occassional refinement and smoothing) have all been integrated. There are no user-facing changes, or behavioural changes or enhancements, in this release. The NEWS snippet below has the fuller details.

Changes in version 0.3.14 (2026-04-22)
  • Tests were refactored to use NA_integer64_ (Michael Chirico in #149 and Dirk in #156)
  • nanoduration was updated for changes in nanotime 4.8.0 (Michael Chirico in #152 fixing #151)
  • Use of as.integer64(keep.names=TRUE) has been refactored (Michael Chirico in #154 fixing #153)
  • In tests, nanotime is attached after bit64; this still needs a better fix (Michael Chirico in #155)
  • The package now has a hard dependency on the just released bit64 version 4.8.0 (or later)

Thanks to my CRANberries, there is a diffstat report for this release. More details and examples are at the nanotime page; code, issue tickets etc at the GitHub repository and all documentation is provided at the nanotime documentation site.

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

Vincent Bernat: CSS & vertical rhythm for text, images, and tables

Vertical rhythm aligns lines to a consistent spacing cadence down the page. It creates a predictable flow for the eye to follow. Thanks to the rlh CSS unit, vertical rhythm is now easier to implement for text.1 But illustrations and tables can disrupt the layout. The amateur typographer in me wants to follow Bringhurst s wisdom:
Headings, subheads, block quotations, footnotes, illustrations, captions and other intrusions into the text create syncopations and variations against the base rhythm of regularly leaded lines. These variations can and should add life to the page, but the main text should also return after each variation precisely on beat and in phase. Robert Bringhurst, The Elements of Typographic Style

Text Three factors govern vertical rhythm: font size, line height and margin or padding. Let s set our baseline with an 18-pixel font and a 1.5 line height:
html  
  font-size: 112.5%;
  line-height: 1.5;
 
h1, h2, h3, h4  
  font-size: 100%;
 
html, body,
h1, h2, h3, h4,
p, blockquote,
dl, dt, dd, ol, ul, li  
  margin: 0;
  padding: 0;
 
CSS Values and Units Module Level 4 defines the rlh unit, equal to the computed line height of the root element. All browsers support it since 2023.2 Use it to insert vertical spaces or to fix the line height when altering font size:3
h1, h2, h3, h4  
  margin-top: 2rlh;
  margin-bottom: 1rlh;
 
h1  
  font-size: 2.5rem;
  line-height: 2rlh;
 
h2  
  font-size: 1.5rem;
  line-height: 1rlh;
 
h3  
  font-size: 1.25rem;
  line-height: 1rlh;
 
p, blockquote, pre  
  margin-top: 1rlh;
 
aside  
  font-size: 0.875rem;
  line-height: 1rlh;
 
We can check the result by overlaying a grid4 on the content:
Screenshot of my website with a grid as an overlay and each line of text fitting on the grid
Using CSS rlh unit to set vertical space works well for text. You can display the grid using Ctrl+Shift+G.
If a child element uses a font with taller intrinsic metrics, it may stretch the line s box beyond the configured line height.5 A workaround is to reduce the line height to 1. The glyphs overflow but don t push the line taller.
code, kbd  
  line-height: 1;
 

Responsive images Responsive images are difficult to align on the grid because we don t know their height. CSS Rhythmic Sizing Module Level 1 introduces the block-step property to adjust the height of an element to a multiple of a step unit. But most browsers don t support it yet. With JavaScript, we can add padding around the image so it does not disturb the vertical rhythm:
const targets = document.querySelectorAll(".lf-media-outer");
const adjust = (el, height) =>  
  const rlh = parseFloat(getComputedStyle(document.documentElement).lineHeight);
  const padding = Math.ceil(height / rlh) * rlh - height;
  el.style.padding =  $ padding / 2 px 0 ;
 ;
targets.forEach((el) => adjust(el, el.clientHeight));
Screenshot of my website with a grid as an overlay and an image not breaking the vertical rhythm. Additional padding is visible before and after the image. The height of the image with padding is 216.
The image is snapped to the grid thanks to the additional padding computed with JavaScript. 216 is divisible by 27, our line height in this example.
As the image is responsive, its height can change. We need to wrap a resize observer around the adjust() function:
const ro = new ResizeObserver((entries) =>  
  for (const entry of entries)  
    const height = entry.contentBoxSize[0].blockSize;
    adjust(entry.target, height);
   
 );
for (const target of targets)  
  ro.observe(target);
 

Tables Table cells could set 1rlh as their height but they would feel constricted. Using 2rlh wastes too much space. Instead, we use incremental leading: we align one in every five lines.
table  
  border-spacing: 2px 0;
  border-collapse: separate;
  th  
    padding: 0.4rlh 1em;
   
  td  
    padding: 0.2rlh 0.5em;
   
 
To align the elements after the table, we need to add some padding. We can either reuse the JavaScript code from images or use a few lines of CSS that count the regular rows and compute the missing vertical padding:
table:has(tbody tr:nth-child(5n):last-child)     padding-bottom: 0.2rlh;  
table:has(tbody tr:nth-child(5n+1):last-child)   padding-bottom: 0.8rlh;  
table:has(tbody tr:nth-child(5n+2):last-child)   padding-bottom: 0.4rlh;  
table:has(tbody tr:nth-child(5n+3):last-child)   padding-bottom: 0  
table:has(tbody tr:nth-child(5n+4):last-child)   padding-bottom: 0.6rlh;  
A header cell has twice the padding of a regular cell. With two regular rows, the total padding is 2 2 0.2+2 0.4=1.6. We need to add 0.4rlh to reach 2rlh of extra vertical padding across the table.
Screenshot of my website with a grid as an overlay and a table following the vertical rhythm. Additional padding is visible after the table. The height of the table with padding is 405.
One line out of five is aligned to the grid. Additional padding is added after the table to not break the vertical rhythm. 405 is divisible by 27, our line height in this example.

None of this is necessary. But once you start looking, you can t unsee it. Until browsers implement CSS Rhythmic Sizing, a bit of CSS wizardry and a touch of JavaScript is enough to pull it off. The main text now returns after each intrusion precisely on beat and in phase.

  1. See Vertical rhythm using CSS lh and rlh units by Pawe Grzybek.
  2. For broader compatibility, you can replace 2rlh with calc(var(--line-height) * 2rem) and set the --line-height custom property in the :root pseudo-class. I wrote a simple PostCSS plugin for this purpose.
  3. It would have been nicer to compute the line height with calc(round(up, calc(2.4rem / 1rlh), 0) * 1rlh). Unfortunately, typed arithmetic is not supported by Firefox yet. Moreover, browsers support round() only since 2024. Instead, I coded a PostCSS plugin for this as well.
  4. The following CSS code defines a grid tracking the line height:
    body  
      position: relative;
     
    body::after  
      content: "";
      position: absolute;
      inset: 0;
      z-index: 9999;
      background: linear-gradient(180deg, #c8e1ff99 1px, transparent 1px);
      background-size: 20px 1rlh;
      pointer-events: none;
     
    
  5. See Deep dive CSS: font metrics, line-height and vertical-align by Vincent De Oliveira.

21 April 2026

Dirk Eddelbuettel: RcppArmadillo 15.2.6-1 on CRAN: Several Updates

armadillo image Armadillo is a powerful and expressive C++ template library for linear algebra and scientific computing. It aims towards a good balance between speed and ease of use, has a syntax deliberately close to Matlab, and is useful for algorithm development directly in C++, or quick conversion of research code into production environments. RcppArmadillo integrates this library with the R environment and language and is widely used by (currently) 1263 other packages on CRAN, downloaded 45.7 million times (per the partial logs from the cloud mirrors of CRAN), and the CSDA paper (preprint / vignette) by Conrad and myself has been cited 683 times according to Google Scholar. This versions updates to the 15.2.5 and 15.2.6 upstream Armadillo releases from, respectively, two and five days ago. The package has already been updated for Debian, and built for r2u. When we ran the reverse-dependency check for 15.2.5 at the end of last week, one package failed. I got in touch with the authors, filed an issue, poked some more, isolated the one line that caused an example to fail and right then 15.2.6 came out fixing just that. It was after all an upstream issue. We used to ran these checks before Conrad made a release, he now skips this and hence needed a quick follow-up release. It can happen. The other big change is that this R package release phases out the dual support for both C++14 or newer (as in current Armadillo) along with a C++11 fallback for more slowly updating packages. I am happy to say that after over eight months of this managed transition (during which CRAN expulsed some laggard packages that were not moving in from C++11) we are now at all packages using C++14 or newer which is nice. And I will take this as an opportunity to stress that one can in fact manage a disruptive API change this way as we just demonstrated. Sadly, R Core does not seem to have gotten that message and rollout of this package was also still a little delayed because of the commotion created by the last minute API changes preceding the R 4.6.0 release later this week. Smaller changes in the package are a switch in pdf vignette production to the Rcpp::asis() driver, and a higher-precision computation in rmultinom() (matching a change made in R-devel during last week in its use of Kahan summation). All detailed changes since the last CRAN release follow.

Changes in RcppArmadillo version 15.2.6-1 (2026-04-20)
  • Upgraded to Armadillo release 15.2.6 (Medium Roast Deluxe)
    • Ensure internally computed tolerances are not NaN
  • The rmultinom deploys 'Kahan summation' as R-devel does now.

Changes in RcppArmadillo version 15.2.5-1 [github-only] (2026-04-18)
  • Upgraded to Armadillo release 15.2.5 (Medium Roast Deluxe)
    • Fix for handling NaN elements in .is_zero()
    • Fix for handling NaN in tolerance and conformance checks
    • Faster handling of diagonal views and submatrices with one row>

  • Sunset the C++11 fallback of including Armadillo 14.6.3 (#504 closing #503)
  • The vignettes have refreshed bibliographies, and are now built using the Rcpp::asis vignette builder (#506)
  • One rmultinom test is skipped under R-devel which has switched to a higher precisions calc
Courtesy of my CRANberries, there is a diffstat report relative to previous release. More detailed information is on the RcppArmadillo page. Questions, comments etc should go to the rcpp-devel mailing list off the Rcpp R-Forge page.

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

Russell Coker: More About Ebook Readers in Debian

FBReader After my previous blog post about eBook readers in Debian [1] a reader recommended FBReader. I tried it and it s now my favourite reader. It works nicely on laptop and phone and takes significantly less RAM than Calibre or Arianna (especially important for phones). While the problems with my FLX1s not displaying text with Calibre or Arianna might be the fault of something on the FLX1s side those problems just don t happen with FBReader. FBReader has apparently now got a proprietary version as the upstream, but we still have FOSS code to use in Debian. It would be nice if someone updated it to store the reading location using WebDAV and/or a local file that can be copied with the NextCloud client or similar. Currently there is code to store reading location in the Google cloud which I don t want to use. It s not THAT difficult to see what chapter you are at with one device and just skip to that part on another, but it is an annoyance. One thing I really like about FBReader is that you can run it with a epub file on the command line and it just opens it and when it s been closed you can just open it again to the same spot in the same file. I don t want a library to view a book list, I just want to go back to what I was last reading in a hurry. Calibre might be better for some uses, for example I can imagine someone in the publishing industry with a collection of thousands of epub files finding that Calibre works better for them. But for the typical person who just wants to read one book and keep reading it until they finish it FBReader seems clearly better. The GUI is a little unusual, but it s not at all confusing and it works really well on mobile. Okular I tried Okular (the KDE viewer for PDF files etc) which displays epub files if you have the okular-extra-backends installed, but it appears to not display books with the background color set to black. I would appreciate it if someone who has read some public domain or CC licences epub files can recommend ones with a black background that I could use for testing as I can t file a Debian bug report without sample data to reproduce the bug. I decided not to use it for actual book reading as FBReader is far better for my use taking less RAM and being well optimised for mobile use. Folite Foliate supports specifying a book on the command-line which is nice. But it takes more memory than FBReader which is probably mostly due to using webkit to display things. The output was in 2 columns on my laptop in small text which is probably configurable but I didn t proceed with it. I determined that it doesn t compare with FBReader for my use. It s written in JavaScript which may be a positive feature for some people. Koodo I had a brief test of Koodo which isn t in Debian. Here is the Koodo Reader Github [2]. I installed the .deb that they created, it installs files to /opt/Koodo Reader/ (yes that s a space in the directory name) and appears to have Chromium as part of the runtime. I didn t go past that even though it appears to have a decent feature set. It is licensed under version 3 of the AGPL so is suitable for Debian packaging if someone wants to do it. Thorium I saw the Thorium reader on Github [3] which looks promising, it s under the BSD 3 clause license so is suitable for Debian packaging. The EDR Lab seems like a good project for advancing electronic document use [4] and it would be good to have their stuff in Debian. For the moment I m happy using FBReader.

Ravi Dwivedi: LibreOffice Conference Budapest 2025

In September 2025, I attended the LibreOffice Conference in Budapest, Hungary, on the 4th and the 5th, and a community meeting on the 3rd. Thanks to The Document Foundation (TDF) for sponsoring my travel and accommodation costs. The conference venue was Faculty of Informatics, E tv s Lor nd University (ELTE). The conference was planned to be held from the 4th to the 6th, but the program for the 6th of September had to be canceled due to the venue being unavailable because of a marathon in Budapest. So, all the talks got squeezed into just two days, making the schedule a bit hectic. The TDF had booked my room at the Corvin Hotel. It was a double bedroom with a window. The breakfast was included in the hotel booking. The hotel was walking distance from the conference venue. One could also take a tram from the hotel to reach the venue.
A double bed A shot of my room. Photo by Ravi Dwivedi, released under CC-BY-SA 4.0.
Tram A tram in Budapest. Photo by Ravi Dwivedi, released under CC-BY-SA 4.0.

3rd of September On the 3rd of September, we had a community meeting at the above-mentioned venue. I walked with my friend Dione to the venue. Upon reaching there, I noticed that the university had no boundaries and gates. This reminded me of the previous year s conference venue in Luxembourg, which also had no boundaries or gates. In contrast, Indian universities and institutes typically have walls and gates serving as boundaries to separate them from the rest of the city. Many of these institutes also have security guards at the entrance, who may ask attendees to present proof of admission before allowing them inside. I was surprised to find that institutes in Europe, like the one where the conference was held, did not have such boundaries. The building where the conference was held was red, which happened to be the same color as the building for the previous year s conference venue. I remember joking with Dione that the criteria for the conference venue might have been the color of the building.
A red building The red building in the picture served as the conference venue. Photo by Ravi Dwivedi, released under CC-BY-SA 4.0.
During the community meeting, we shared ideas on how to spread the word about LibreOffice. The meeting lasted for a couple of hours. After the community meeting, we went to the hotel for dinner sponsored by the TDF.
Cake slices These Esterh zy cake bites were really yummy. Photo by Ravi Dwivedi, released under CC-BY-SA 4.0.
Raspberry Currant cake slices Raspberry Currant cake slices. Photo by Ravi Dwivedi, released under CC-BY-SA 4.0.

4th of September On the first day of the conference, attendees were given swag bags containing a pad, sticky notes, a pen, a conference T-shirt, and a bottle.
A blue colored T-shirt on a bed along with a pen, a bottle, a diary and a sticky note Conference swag. Photo by Ravi Dwivedi, released under CC-BY-SA 4.0.
The talks started early in the morning with Eliane Domingos, Chairperson of TDF s Board of Directors, giving the inauguration talk. As always, I found Italo Vignoli s talk on the importance of document freedom interesting. During the snack break, I noticed that there were three types of milk available for coffee: cow s milk, lactose-free milk, and almond milk. Almond milk is rare in India, but I have managed to get it, but I have never seen lactose-free milk in India. Since I run fundraisers in my projects, such as Prav, I could relate to Lothar K. Becker s talk. He discussed the issue that certain implementations in LibreOffice require a budget that is too large for any single interested entity to fund independently. Furthermore, The Document Foundation (TDF) cannot legally receive funds from government entities. Therefore, there is no organization or entity to pool resources from all the interested entities to finance the implementation.
Lothar giving his presentation Lothar giving his presentation. Photo by Ravi Dwivedi, released under CC-BY-SA 4.0.
Another talk was by the Austrian Armed Forces on their migration to LibreOffice. I wanted to know why they migrated, and I found out that they did it for their digital sovereignty, and not for saving on the license costs. Another point presented in the talk was that LibreOffice is available on all the operating systems, while the Microsoft Office suite is not that widely available. The migration was systematic and was performed over a few years. They started working on it in 2021, and the migration was finished recently. In addition, it also required training their staff in using LibreOffice.
Presentation on migration to LibreOffice by Austrian Armed Forces Presentation on migration to LibreOffice by Austrian Armed Forces. Photo by Ravi Dwivedi, released under CC-BY-SA 4.0.
The lunch was inside the university canteen. We were provided lunch coupons by the TDF. I got a vegan coupon with 4000 Ft written on it, which meant I could take lunch for up to 4000 Hungarian forints.
My lunch ticket My lunch ticket for the conference. Photo by Ravi Dwivedi, released under CC-BY-SA 4.0.
The lunch I had on the first day The lunch I had on the first day. Photo by Ravi Dwivedi, released under CC-BY-SA 4.0.
During the evening, it was my turn for the presentation. I was done with preparing my slides ten days before my talk. I also got my slides reviewed by friends. My talk was finished in 20 minutes, while I was given a 30-minute slot. This helped us catch up on the schedule. Furthermore, I made my talk interactive by asking questions and making sure that the audience was not asleep. During my talk, my friend Dione took my pictures with my camera. My talk was on how free software projects could give users a say in freedom to modify the software. I illustrated this using the Prav project that I am a part of. After the talks were over, we were treated to a conference dinner at Trofea Grill. It had a great selection of desserts, which helped me sample some Hungarian desserts. The sponge cake was especially good.
Desserts at Tofea Grill Desserts at Tofea Grill. Photo by Ravi Dwivedi, released under CC-BY-SA 4.0.

5th of September The next day the 5th of September I went with Dione to the venue early in the morning, as her talk was the first one of the day. Her talk was titled Managing Tasks with Nextcloud Deck. Later that day, I also attended a talk on Collabora. At lunch, I found the egg white salad quite tasty.
Dione giving her presentation Dione giving her presentation. Photo by Ravi Dwivedi, released under CC-BY-SA 4.0.
Egg white salad Egg white salad. Photo by Ravi Dwivedi, released under CC-BY-SA 4.0.
After the lunch break, we had the conference group photo. I had a Nikon camera, which we used to take the group photo. I requested a university student to take our group photo and also taught her how to operate the camera.
People looking at the camera and smiling Group photo
By the evening, the conference ended, after which we went to a pub, which was again sponsored by TDF. I had beer, but that one really tasted bad, so I couldn t finish it. The only vegetarian option was goat cheeseburger, which my friend Manish and I opted for. The burger tasted awful. Apparently, I don t like goat cheese. The next day I went sightseeing with Dione in Budapest. Stay tuned for our adventures! Credits: Thanks to Dione and Richard for proofreading.

20 April 2026

Bits from Debian: Debian Project Leader election 2026 is over, Sruthi Chandran elected!

The voting period and tally of votes for the Debian Project Leader election has just concluded, and the winner is Sruthi Chandran. Congratulations! 347 out of 1,039 Developers voted using the Condorcet method. More information about the results of the voting is available on the Debian Project Leader Elections 2026 page. Many thanks to Sruthi Chandran for her campaign, to our Developers for their votes, and to Andreas Tille for his service as DPL over the past two years! The new term for the project leader will start on April 21, 2026 and expire on April 20, 2027.

Sune Vuorela: Kookbook 0.3.0 released

I recently released version 0.3.0 of my recipe manager application Kookbook find it in git in KDE Invent or as released tarballs in https://download.kde.org/stable/kookbook/ Changes since last time is more or less Minor bugfixes and a Qt6 port nothing as such noteworthy unless you aim to get rid of Qt5 on your system. so what is kookbook?
It is a simple recipe viewer that works with semi-structured markdown. More details can be seen in the quite old 0.1.0 announcement At some point I should do a 10 recipe example collection, but my personal collection is in danish, so I m not sure it is going to be useful. Unless someone will donate me some handfuls of pre-formatted recipes, I will happily announce it.

Russ Allbery: Review: Surface Detail

Review: Surface Detail, by Iain M. Banks
Publisher: Orbit
Copyright: October 2010
Printing: May 2011
ISBN: 0-316-12341-2
Format: Trade paperback
Pages: 627
Surface Detail is the ninth novel in Banks's Culture science fiction (literary space opera?) series. As with most of the Culture novels, it can be read in any order, although this isn't the best starting point. There is an Easter egg reference to Use of Weapons that would be easier to notice if you have read that book recently, but which is not that important to the story. Lededje Y'breq is an Indented Intagliate from the Sichultian Enablement. Her body is patterned from her skin down to her bones, covered with elaborate markings similar to tattoos that extend to her internal organs. As an intagliate, she is someone's property. In her case, she is the property of Joller Veppers, the richest man in the Enablement and her father's former business partner. Intagliates are a tradition of great cultural pride in the Enablement. They are a living representation of the seriousness with which debts and honor are taken, up to and including one's not-yet-born children becoming the property of one's debtor. Such children are decorated as living works of art of the highest skill and technical sophistication; after all, the Enablement are not barbarians. As the story opens, Lededje is attempting, not for the first time, to escape. This attempt is successful in an unexpected way. Prin and Chay are Pavulean researchers and academics who, as this story opens, are in Hell. They are not dead; they have infiltrated the Hell that Pavuleans are shown to scare them into proper behavior in order to prove that it is not an illusion and their society does indeed torture people in an afterlife, in more awful ways than people dare imagine. They have reached the portal through which temporary visitors exit, hoping to escape with firm evidence of the existence and horrors of the Pavulean afterlife. They will not be entirely successful. Yime Nsokyi is a Culture agent for Quietus, the part of Contact that concerns itself with the dead. Many advanced societies throughout the galaxy have invented and reinvented the ability to digitize a mind and then run it in a virtual environment. Once a society can capture the minds of every person in that society from that point forward, it faces the question of whether to do so and, if it does, what to do with those minds. More specifically, it faces the moral question of whether to punish the minds of people who were horrible in life. It faces the question of whether to create Hell. Vatueil is a soldier in a contestation, a limited and carefully monitored virtual war. The purpose of that war game is to, once and for all, resolve the question of whether civilizations should be allowed to create Hells. Some civilizations consider them integral to their religion or self-conception. Others consider them morally abhorrent, and that conflict was in danger of spilling over into war in the Real. Hence the War in Heaven: Both sides committed to fight in a virtual space under specific and structured rules, and the winner decides the fate of the galaxy's Hells. Vatueil is fighting for the anti-Hell side. The anti-Hell side is losing. There are very few authors who were better at big-idea science fiction than Iain M. Banks. I've been reading a few books about AI ships and remembered that I had two unread Culture novels that I was saving. It felt like a good time to lose myself in something sprawling. Surface Detail does sprawl. Even by Banks's standards, there was an impressive amount of infodumping in this book. Banks always has huge and lovingly described set pieces, and this book is no exception, but there are also paragraphs and pages of background and cultural musings and galactic politics. We are introduced to not one but three new Contact divisions; as well as the already-mentioned Quietus, there is Numina, which concerns itself with the races that have sublimed (transcended), and Restoria, which deals with hegemonizing swarms (grey goo nanotech, paperclip maximizers, and their equivalents). Infodumping is both a feature and a bane of big-idea science fiction, and it helps to be in the right mood. It also helps if the info being dumped is interesting, and this is where Banks shines. This is a huge, sprawling book, but it deals with some huge, sprawling questions and it has interesting and non-reductive thoughts about them. The problems posed by the plot come with history, failed solutions, multi-sided political disputes, strategies and tactics of varying morality and efficacy, and an effort to wrestle with the irreducible complexity of trying to resolve political and ethical disagreements in a universe full of profound disagreements and moral systems that one cannot simply steamroll. It also helps that the characters are interesting, even when they're not likable. Surface Detail has one fully hissable villain (Veppers) as a viewpoint character, but even Veppers is interesting in a "let me check the publication date to see if Banks was aware of Peter Thiel" sort of way. The Culture ships, of which there are several in this story, tend towards a gently sarcastic kindness that I find utterly charming. Lededje provides the compelling motive force of someone who has no involvement in the broader philosophical questions and instead intends to resolve one specific problem through lethal violence. Vatueil and Yime were a bit bland in personality, more exposition generators than characters I warmed to, but their roles and therefore the surrounding exposition were fascinating enough that I still enjoyed their sections. I'm sure this is not an original observation, but I was struck reading this book in the first half of 2026 that the Culture functions as an implementation of what the United States likes to think it is but has never been. It has a strong sense of shared ethics and moral principles, it tries to export them to the rest of the galaxy through example, persuasion, and careful meddling, but it tries to follow some combination of pragmatic and moral rules while doing so, partly to avoid a backlash and partly to avoid becoming its own sort of hegemonizing swarm. That is a powerfully attractive vision of how to be an advanced civilization, and the fact that every hegemon that has claimed that mantle has behaved appallingly just makes it more intriguing as a fictional concept. In this book, like in many Culture books, the Culture is painfully aware of the failure modes of meddling, and the story slowly reveals the effort the Culture put into staying just on a defensible side of their own moral lines. This is, in a sense, a Prime Directive story, but with a level of hard-nosed pragmatism and political sophistication that the endless Star Trek Prime Directive episodes never reach. Surface Detail does tend to sprawl, and I'm not sure Banks pulled together all the pieces of the plot. For example, if there was a point to the subplot involving the Unfallen Bulbitian, it was lost on me. (There is always a possibility with Banks that I wasn't paying close enough attention.) But the descriptions are so elaborate and the sense of politics and history are so deep that I was never bored, even when following a plot thread that meandered off into apparent irrelevance. The main plot line comes to a satisfying conclusion that may be even more biting social commentary today than it was in 2010. A large part of the plot does involve Hell, so a warning for those who haven't read much Banks: He adores elaborate descriptions of body horror and physical torture. The sections involving Prin and Chay are rather grim and horrific, probably a bit worse than Dante's Inferno. I have a low tolerance for horror and I was able to read past and around the worst bits, but be warned that Banks indulges his love for the painfully grotesque quite a bit. This was great, and exactly what I was hoping for when I picked it up. It's not the strongest Culture novel (for me, that's either The Player of Games or Excession), but it's one of the better ones. Highly recommended, although if you're new to the Culture, I would start with one of the earlier books that provide a more gradual introduction to the Culture and Special Circumstances. Followed, in the somewhat disconnected Culture series sense, by The Hydrogen Sonata. Content warnings: Rape (largely off-screen), graphic violence, lots of Bosch-style grotesque torture, and a lot of Veppers being a thoroughly awful human being as a viewpoint character. Rating: 8 out of 10

19 April 2026

Russ Allbery: Review: Collision Course

Review: Collision Course, by Michelle Diener
Series: Class 5 #6
Publisher: Eclipse
Copyright: November 2024
ISBN: 1-7637844-0-1
Format: Kindle
Pages: 289
Collision Course is the sixth novel in the Class 5 science fiction series and the first that doesn't use the Dark X naming convention. There are lots of spoilers in this story for the earlier books, but you don't have to remember all the details of previous events. Like the novella, Dark Ambitions, this novel returns to Rose, Sazo, and Dav instead of introducing another Earth woman and Class 5 ship. In Dark Class, Ellie discovered an interesting artifact of a previously-unknown space-faring civilization. Rose, Sazo, and Dav are on their way to make first contact when, during a routine shuttle flight between the Class 5 and Dav's Grih military ship, Rose is abducted. The aliens they came to contact have an aggressive, leverage-based negotiating strategy. They're also in the middle of a complicated war with more sides than are readily apparent. What I liked most about Dark Horse, the first book of this series and our introduction to Rose, was the revealed ethical system and a tense plot that hinged primarily on establishing mutual trust when there were excellent reasons for the characters to not trust each other. As the series has continued, I think the plots have become more complicated but the ethical dilemmas and revealing moments of culture shock have become less common. That is certainly true of Collision Course; this is science fiction as thriller, with a complex factional conflict, a lot of events, more plot reversals than the earlier books, but also less ethics and philosophy. I'm not sure if this is a complaint. I kind of miss the ethics and philosophy, but Diener also hasn't had much new to say for the past few books. The plot of Collision Course is quite satisfyingly twisty for a popcorn-style science fiction series. I was kept guessing about the merits of some of the factions quite late into the book, although admittedly I was in the mood for light entertainment and was not trying too hard to figure out where the book was going. I did read nearly the entire book in one sitting and stayed up until 2am to finish it, which is a solid indication that something Diener was doing worked. I do have quibbles, though. One is that the ending is a bit unsatisfying. Like Sazo, I was getting quite annoyed at the people capturing (and recapturing) Rose and would have enjoyed somewhat more decisive consequences. Also, and here I have to be vague to avoid spoilers, I was expecting a bit more of a redemption arc for one of the players in the multi-sided conflict. The ending I did get was believable but rather sad, and I wish Diener had either chosen a different outcome (this is light happily-ever-after science fiction, after all) or wrestled more directly with the implications. There were a bit too many "wait, one more thing" ending reversals and not quite enough emotional payoff for me. The other quibble is that Collision Course was a bit too damsel in distress for this series. Rose is pregnant, which Diener uses throughout the book as a way to raise the stakes of the plot and also make Rose more annoyed but also less capable than she was in her earlier novel. Both Sazo and Dav are in full heroic rescue mode, and while Diener still ensures Rose is primarily responsible for her own fate, there is some "military men attempt to protect the vulnerable woman" here. One of the things I like about this series is that it does not use that plot, so while the balance between Rose rescuing herself and other people rescuing her is still tilted towards Rose, I would have liked this book more if Rose were in firmer control of events. I will mostly ignore the fact that a human and a Grih sexually reproducing makes little to no biological sense, since Star Trek did similar things routinely and it's an established genre trope. But I admit that it still annoys me a bit that the alien hunk is essentially human except that he's obsessed with Rose's singing and has pointy ears. Diener cares about Rose's pregnancy a lot more than I did, which added to my mild grumpiness at how often it came up. Overall, this was fine. I prefer a bit more of a protagonist discovering how powerful she is by making ingenious use of the ethical dilemmas her captors have trapped themselves in, and a bit less of Rose untangling a complicated political situation by getting abducted by every player serially, but it still kept the pages turning. Any book that is sufficiently engrossing for me to read straight through is working at some level. Collision Course was highly readable, undemanding, and distracting, which is what I was looking for when I read it. I would put it about middle of pack in the series. If Rose's pregnancy is more interesting to you than it was to me, that might push it a bit higher. If you have gotten this far in the series, you will probably enjoy this, although it does feel like Diener is running out of new things to say about this universe. That's unfortunate given the number of threads about AI sentience and rights that could still be followed, but I think tracing them properly would require more philosophical meat than Diener intends for these books. Which is why the next book I grabbed was a Culture novel. Currently this is the final book in the Class 5 series, but there is no inherent reason why Diener couldn't write more of them. Rating: 7 out of 10

18 April 2026

Matthias Klumpp: Hello old new Projects directory!

If you have recently installed a very up-to-date Linux distribution with a desktop environment, or upgraded your system on a rolling-release distribution, you might have noticed that your home directory has a new folder: Projects

Why? With the recent 0.20 release of xdg-user-dirs we enabled the Projects directory by default. Support for this has already existed since 2007, but was never formally enabled. This closes a more than 11 year old bug report that asked for this feature. The purpose of the Projects directory is to give applications a default location to place project files that do not cleanly belong into one of the existing categories (Documents, Music, Pictures, Videos). Examples of this are software engineering projects, scientific projects, 3D printing projects, CAD design or even things like video editing projects, where project files would end up in the Projects directory, with output video being more at home in Videos . By enabling this by default, and subsequently in the coming months adding support to GLib, Flatpak, desktops and applications that want to make use of it, we hope to give applications that do operate in a project-centric manner with mixed media a better default storage location. As of now, those tools either default to the home directory, or will clutter the Documents folder, both of which is not ideal. It also gives users a default organization structure, hopefully leading to less clutter overall and better storage layouts.

This sucks, I don t like it!
As usual, you are in control and can modify your system s behavior. If you do not like the Projects folder, simply delete it! The xdg-user-dirs utility will not try to create it again, and instead adjust the default location for this directory to your home directory. If you want more control, you can influence exactly what goes where by editing your ~/.config/user-dirs.dirs configuration file. If you are a system administrator or distribution vendor and want to set default locations for the default XDG directories, you can edit the /etc/xdg/user-dirs.defaults file to set global defaults that affect all users on the system (users can still adjust the settings however they like though).

What else is new? Besides this change, the 0.20 release of xdg-user-dirs brings full support for the Meson build system (dropping Automake), translation updates, and some robustness improvements to its code. We also fixed the arbitrary code execution from unsanitized input bug that the Arch Linux Wiki mentions here for the xdg-user-dirs utility, by replacing the shell script with a C binary. Thanks to everyone who contributed to this release!

Valhalla's Things: Pizza!

Posted on April 18, 2026
Tags: madeof:atoms, craft:cooking
This post contains a bit of consumerism and is full of references to commercial products, none of which caused me to receive any money nor non-monetary compensation. This post has also been written after eating in one meal the amount of bread-like stuff that we usually have in more than 24 hours. I ve been baking bread since a long time ago. I don t know exactly when, but probably it was the early 2000s or so, and remained a regular-ish thing until 2020, when it became an extremely regular thing, as in I believe I bake bread on average every other day. In the before times, I ve had a chance to bake pizza in a wood fired oven a few times: a friend had one and would offer the house, my partner would mind the fire, and I would get there with the dough and prepare the pizza. Now that we have moved to a new house, we don t have a good and convenient place for a proper wood fired oven in masonry, but we can use one of the portable ones, and having dealt with more urgent expenses, I decided that just before the potential collapse of the global economy was a good time as any to buy the oven I had been looking at since we found this house. I decided to get an Ooni Karu 2, having heard good things about the brand, and since it looked like a good balance between size and portability. I also didn t consider their gas fired ovens (nor did I buy the gas burner) because I m trying to get rid of gas, not add stuff that uses it, and I didn t get an electric one because I m not at all unhappy with the bakery-style pizza we make in our regular oven, and I have to admit we also wanted to play with fire1. We also needed an outdoor table suitable to use the oven on and store it. Here I looked for inspiration at the Ooni tables (and for cheaper alternatives in the same style), but my mother who shares the outdoor area with us wasn t happy with the idea of steel2. And then I was browsing the modern viking shores, and found that there was a new piece in the N MMAR series my mother likes (and of which we already have some reclining chairs): a kitchen unit in wood with a steel top. At first I expected to just skip the back panel, since it would be in the way when using the oven, but then I realized that it could probably be assembled upside down, down from the top between the table legs, and we decided to try that option. This week everything had arrived, and we could try it. Yesterday evening, after dinner (around 21, I think) I prepared the dough with the flour I usually use for bakery-style pizza: Farina di Grano Tenero Tipo 0 PANE (320 - 340 W); since I wanted to make things easier for myself I only used 55% hydration, so the recipe was:
  • 1 kg flour
  • 550 g water
  • 2 g dry yeast
  • 12 g salt
The next time I think I ll try with one of my other staples: Molino Bogetto etichetta blu (260/280 W) Then this morning we assembled the N MMAR , then I divided the dough in eight balls, put them in a covered but not sealed container 3, well floured with rice flour and then we fired the oven (as in: my partner did, I looked for a short while and then set the table and stuff), using charcoal, because we already had some, and could conveniently get more at the supermarket. When the oven had reached temperatures in the orange range4 I stretched the smallest ball out, working on my wooden peel, sprayed it with water5, sprinkled it with coarse salt and put it in the oven. After 30 seconds I turned it around with the new metal peel, then again after 30 seconds, and then I lost count of how many times I repeated this6, but it was probably 2 or 3 minutes until it looked good. A flatbread on a regular plate: it's only a bit more than half the plate in diameter, puffed up near the borders and thin in the middle, and only lightly browned in places, not burnt. It's sitting on the lower shelf of a wooden table. And it was good. The kind of pizza that is quite soft, especially near the borders. We ate it with fresh mozzarella and tomatoes, and then made another one the same way, to finish the mozzarella. Another flatbread on the same plate, this time it's about 4 cm smaller than the plate on all sides, and it's covered with brownish-red chopped up vegetables. This was supposed to be our lunch, but we decided to try one with some leftover cooked radicchio, and that also worked quite nicely. And finally, we decided we needed to try a more classical pizza, with tomato sauce and cured meat, of which we forgot to take pictures. Up to here we had eaten about half of the dough, and we were getting full: I had prepared significantly more than what I expected to eat, to be able to accidentally burn some, but also with the idea to bake something else to be eaten later. So I made two more focaccias with just water and salt, and then I tried to cook some bread with what I expected to be residual heat. Another flatbread with coarse salt and two bread rolls, one of which is completely carbonized on one side. The other one has been cut, and while it has a carbonized spot, it is also well cooked in the middle, and perfectly edible. Except that the oven was getting a bit too cold, so my partner added some charcoal, and when I put the last two unflattened balls right at the back of the oven where it was still warmer, that side carbonized. After 5 minutes I moved them to the middle of the oven, and turned them, and then after another turn and 5 more minutes they were ready. And other than the burnt crust, they were pretty edible. So, the thoughts after our first experience. Everybody around the table (my SO, my mother and me) was quite happy with the results, and they are different enough from the ones I could get with the regular oven. As I should have expected, it s much faster than a masonry oven, both in getting to temperature and in cooling down: my plan for residual heat bread cooking will have to be adjusted with experience. We were able to get it hot enough, but not as hot as it s supposed to be able to get: we suspect that using just charcoal may have influenced it, and next week we ll try to get some wood, and try with a mix. As for the recipe, dividing the dough in eight parts worked quite well: maybe the pizzas are a bit on the smaller side, but since they come one at a time it s more convenient to cut and share them, and maybe make a couple more at the end. Of course, I ll want to try different recipes, for different styles of pizzas (including some almost-trademark-violating ones) and for other types of flatbread. I expect it won t be hard to find volunteers to help us with the experiments. :D

  1. any insinuation that there may have been considerations of having a way to have freshly baked bread in case of a prolonged blackout may or may not be based on reality. But it wasn t the only or even the main reason.
  2. come on! it s made of STEEL. how can it be not good? :D
  3. IKEA 365+ 3.1 glass, the one that is 32 cm 21 cm 9 cm; it was just big enough for the amount of dough, and then I covered it with a lid that is missing the seal.
  4. why did they put a thermometer on it, and not add labels with the actual temperature? WHY???
  5. if you don t have dietary restrictions a bit of olive oil would taste even better.
  6. numbers above 2 are all basically the same, right?

Yifei Zhan: CommBank hardware MFA token

A while ago, CommBank started asking for MFA confirmation on its mobile app for every NetBank login on a browser. Previously, there was an option to use SMS for MFA, which isn t as secure as I would like, but it was at least usable. Since I m switching away from Android to Mobian and won t be able to use the CommBank app for much longer, I applied for a physical NetCode token. The hardware is made by Digipass and looks disposable. It is a small, battery powered gadget with a screen and a button. When pressed, it shows a temporary NetCode for authentication. Such a NetCode is required both for NetBank logins and approving online transactions. The letter that came with it has the wrong link for activation, the correct link is under NetBank -> Settings -> NetCode (under the Security section) To apply for a physical token, call the NetBank team, mention you can t use the app and need a physical NetCode token, and make sure they actually submit your request for a token. It took me 2 calls to get them to ship me a token. The hardware is free of charge but can only be applied for via phone call; unfortunately staff members at my local branch are unable to do anything in relation to NetBank. I was told privately by a CommBank employee that they are deprecating the hardware token in favor of the mobile app, I hope that won t happen anytime soon, or that they add support for passkeys before they do. The last time I checked, the CommBank app was LineageOS-friendly, but I don t want to configure WayDroid just to do online banking. PayID, the thing that allows you to receive payment via a phone number or email address, is not compatible with the hardware token, and existing PayID will be silently deactivated if you use hardware token. This looks to be an artificial restriction; I don t see why it has to be this way. Regular CommBank mobile app sessions will also be de-activated once the hardware token is activated (I was told so but my sessions weren t deactivated until I wiped my Android phone), and you won t be able to sign into mobile app again until you manually disable the NetCode token. Online banking has been getting progressively more invasive and anti-user over the last decade, from demanding remote attestation to requiring real time location data, each time locking certain features when those demands are not satisfied; all based on the flawed assumptions that everyone owns a phone running a certain flavor of iOS or Android, and has it ready all the time. I m not sure what can be done to reverse this trend, but on the personal level I will use NetBank less and go back to cash.

17 April 2026

Russell Coker: Home Battery

Prices On the 19th of March I got a home battery system installed. The government has a rebate scheme so it had a list price of about $22k for a 40kWh setup and cost me about $12k. It seems that 40KWh is the minimum usable size for the amount of electricity I use, I have 84 cores running BOINC when they have nothing better to do which is 585W of TDP according to Intel. While the CPUs are certainly using less than the maximum TDP (both due to design safety limits and the fact that I have disabled hyper-threading on all systems due to it providing minimal benefits and potential security issues) given some power usage by cooling fans and some inefficiency in PSUs I think that assuming that 585W is accounted for 24*7 by CPUs is reasonable. So my home draws between 800W and 1KW when no-one is home and with an electric car and all electric cooking a reasonable amount of electricity can be used. My bills prior to the battery installation were around $200/month which was based on charging my car only during sunny times as my electricity provider (Amber Electric) has variable rates based on wholesale prices. Also the feed in rates if my solar panels produce too much electricity in sunny times often go negative so if I don t use enough electricity. I haven t had the electric car long enough to find out what the bills might be in winter without a home battery. Before getting the battery my daily bills according to the Amber app were usually between $5 and $10. After getting it the daily bills have almost always been below $5. The only day where it s been over $5 since the battery installation was when electricity was cheap and I fully charged the home battery and my car which used 50KWh in one day and cost $7.87 which is 16 cents per KWh. 16 cents isn t the cheapest price (sometimes it gets as low as 10 cents) but is fairly cheap, sometimes even in the cheap parts of the day it doesn t get that low (the cheapest price on the day I started writing this was 20 cents). So it looks like this may save me $100 per month, if so there will be a 10% annual return on investment on the $12K I spent. This makes it a good investment, better than repaying a mortgage (which is generally under 6%) and almost as good as the long term results of index tracker funds. However if it cost $22K (the full price without subsidy) then it would still be ok but wouldn t be a great investment. The government subsidised batteries because the huge amount of power generated by rooftop solar systems was greater than the grid could use during the day in summer and batteries are needed to use that power when it s dark. Android App The battery system is from Fox ESS and the FoxCloud 2.0 Android app is a bit lacking in functionality. It has a timer for mode setting with options Self-use (not clearly explained), Feed-in Priority (not explained but testing shows feeding everything in to the grid), Back Up , Forced Charge , and Forced Discharge . Currently I have Forced Charge setup for most sunny 5 hours of the day for a maximum charge power of 5KW. I did that because about 25KW/day is what I need to cover everything and while the system can do almost 10KW that would charge the battery fully in a few hours and then electricity would be exported to the grid which would at best pay me almost nothing and at worst bill me for supplying electricity when they don t want it. There doesn t seem to be a never put locally generated power into the grid unless the battery is full option. The force charge mode allows stopping at a certain percentage, but when that is reached there is no fallback to another option. It would be nice if the people who designed the configuration could take as a baseline assumption that the macro programming in office suites and functions in spreadsheets are things that regular people are capable of using when designing the configuration options. I don t think we need a Turing complete programming language in the app to control batteries (although I would use it if there was one), but I think we need clauses like if battery is X% full then end this section . There is no option to say force charge until 100% or force charge for the next X minutes as a one-off thing. If I came home in the afternoon with my car below 50% battery and a plan to do a lot of driving the next day then I d want to force charge it immediately to allow charging the car overnight. But I can t do that without entering a schedule . For Unix people imagine having to do everything via a cron job and no option to run something directly from the command-line. It s a little annoying that they appear to have spent more development time on animations for the app than some of what should be core functionality. Management Amber has an option to allow my battery to be managed by them based on wholesale pries but I haven t done that as the feed-in prices are very low. So I just charge my battery when electricity is cheap and use it for the rest of the day. There is usually a factor of 2 or more price difference between the middle of the day and night time so that saves money. It also means I don t have to go out of my way to try and charge my car in the middle of the day. There is some energy lost in charging and discharging the batteries but it s not a lot. I configured the system to force charge for the 5 sunniest hours every day for 5KW as that s enough to keep it charged overnight and 5KW is greater than the amount of solar electricity produced on my house since I ve been monitoring it so that forces it to all be used for the battery. In summer I might have to change that to 6KW for the sunniest 2 or 3 hours and then 4KW or 5KW surrounding that which will be a pain to manage. Instead of charging the car every day during sunny times I charge it once or twice a week, I have a 3.3KW charger and the car has a 40KWh battery so usually it takes me less than 10 hours to fully charge it and I get at least 5 hours of good sunlight in the process. There are people hacking on these devices which is interesting to get direct control from computers [1], and apparently not banned from the official community for doing so. I m not enthusiastic enough to do this, I ve got plenty of other free software things to work on. But it s good that others are doing so.

16 April 2026

Sahil Dhiman: What is Life (to you)?

It started with a thought: to understand people s perspectives on life and its meaning. So I texted folks, What is life (to you)? . Each of the following list items (-) is a response from a different individual, mostly verbatim.
- A lot
- Everyone has a few universal basic qualities, and some special qualities. To me life is pursuit of exploring world based on those qualities and maturing those qualities as one goes on about exploring world/life with those qualities.
Discovering and enhancing experiences as one goes through them.
- life is endless suffering
- my answer might change daily, but this is what I ve noticed and feel recently. Life is a spectrum with two distinct ends: what we control and what we don t. At birth, the spectrum is largely tilted toward control, but throughout our lives, it gradually shifts toward the other side. Ultimately, as we approach death, we lose all control over any aspect of our existence, reaching the other end of the spectrum.
tho this isn t universal, privilege plays a huge part in what you control tho i believe it holds true for the majority
but yeah man, meaning and purpose are dynamic, it s in their nature to change i can give you a different answer this evening itself xD
- Funeral Monologue from Synecdoche, New York. https://www.youtube.com/watch?v=Z9PzSNy3xj0
- Zindagi ek nadiya hai, Aur mujhe tairna nahi aata
(translation - Life is a river, and I don t know how to swim)
On a more serious note, Life is what you make it out for yourself. The only established truth is that it will end. We can never know if there is something after or if there was something before. So try to live a life that you feel aspired by? But this question was beautifully answered by that book which you had about that dying professor
(Me - He was talking about Tuesday s with Morrie)
- My answer is 42
- One, it s living on your own terms, you define everything for yourself, success, normal, whatever. You get to curate your version of it no matter the societal norms.
It s an accumulation of experiences - friends, parents, work, activities, doing shit loads. Sab try karo- travel, zumba, art, music, workout, sports, dil kara ye karna hai karlo. (translation - If your heart wants to do it, just do it.)
Then I think relationships - all that you ve nurtured, people forget maintaining people because of work. It takes efforts to keep people in your life, everyone that comes has a place in yours, how well thats stays is upto you. You also get to curate your people, who stays who don t. Family toh hai hi (translation - family is there) but everyone else that comes along can make it pretty good.
So I don t want to be 50 and be like chalo ab kuch apne liye karte hai (translation - Come on, now let s do something for ourselves) Do whatever shit you want today. Not everything costs money, and if it does get thrifty
But do keep healthy while doing all of that
- Being alive so that my daughter can grow up and i can help raising her kids as well. Raising kids without mother is tough :P
- Definitively, I feel like Life is a by product of proteins and energy working together. But in a more personal sense, Life is a dumb joke played onto us. It s a rat race. But rats exists because of life and then it becomes a chicken-egg problem
Honestly, I don t give good answers to life questions. I m generally the one asking
Life can be like a box of chocolates, you don t know what you re gonna get untill you experience the chocolate(assuming the chocolates are heterogenous and contains a mix of everything)
Camus once said, Life is a revolt , and one of his students added more spice to it like Life is a revolt against the meaninglessness of existence"
I kinda feel like Life is the pursuit of every person s search for meaning
- Imprisonment waiting for execution
I have one more thought while we are on the topic , game with pre defined starting position and predefined destination , path to reach is a maze
- A phase where you can have a really good time or really bad one, usually the mix of both. A phase where you are prisoner to responsibility and materialistic wants.
It s a hell for you, where you try to create heaven for others. Being born was never your choice, but ending is always in your hands but you are a prisoner. You fear that leaving this world behind will destroy the heavens you created for others and they will be back to hell. But eventually everyone moves on watching the hourglass of their life.
Once you are left with no desires or no one to create heavens for, you look arround yourself. You see everyone chasing something, everyone scared of their limitted life time sliping away yet you want it to end sooner.
Doesn t matter if it was all good till now, or all bad. The other half is waiting for redemption. If it was all good, it s best time to die don t wait for the bad to start. If was all bad, it s still the best time to die what if it was the good one and more worst is waiting for you. We desire to be remembered, yet we want to free from this loop of suffering.
Someone once said, life is a suffering, chose your sufferings.
- Life to me is to live without regrets and live with freedom.
Life is always unpredictable and this unpredictability makes it more interesting and worth it.
- As of now, for the state of mind that I am in , I think for me life is about subtle struggle, subtle inconveniences and yet moving forward cause that s all I know.
I am not sure if any of this has any meaning, but sometimes I feel I was born of a purpose and that the universe has my back.
For me it s about raising my consciousness, understanding people to their depths, gaining moderate material success and helping people to some extend.
I have tried to seek a grander meaning but I have failed.
Life for me is what I make out it.
In my times of great success i rarely think about life for I am busy enjoying it, whatever you may call that state of mind.
- For me its the little things that you enjoy with YOUR people
- Life to me is about living and loving, and doing it in a way that sustains. It s the people who shape you, the work you get absorbed in, the quiet moments in between. There s also the wanting, the drive to figure out what s worth going after and how to get there, but that s just one part of it, not the point of it. And none of it happens in a vacuum. I m aware of the privileges that let me live this way, and I try to hold on to that gratitude. In the end, life has both a material and a non-material side, and a lot of what we do is chasing material things in an attempt to satisfy something non-material within us
- Mere liye (translation - for me) life is staying at my home and studying random economics papers. That s when I enjoy myself the most.
- Very complicated
Some days I wish this life never ended and some time I feel it would be better if it stopped at that moment.
It all depends on the events that happen in the so called life .
So life to me is a string of events that happen anyway and you get to make some decisions which can turn it in any direction and then you wonder how did that happen.
- not forgetting to breathe, learn, eat, game, take a good shit, love, sleep.
- To be honest it changed with time! At 19 it was about freedom, wasn t sure what freedom meant but i wanted that! To be free from everything, maybe because parents still controlled a part of my life. Then came 22-24 where i was working, trying to figure out what i want, the meaning changed from freedom to living for myself. To earn more, to be greedy about myself and pursue whatever would help me gain more steps in my career.
Came my mba life, switched my life from doing for myself to trying everything out to have no regrets. Life meaning was just about living with no regrets, invested, gambled, did everything to earn that tag of yeah, have tried that . Now it has all switched to, it was all just a fake facade. Life turned to having a meaningful life rather than finding meaning in what i am doing. Living for people around me, chhoti chhoti cheezo m khushi (translation - happiness in small things(?)) isn t really a topic of conversation but more of happy thing for me. So it changed, and m quite happy to be honest. Life did show me a lot of failures, but was privileged enough to face those failures. Gained a lot of learnings if not money
Hopeful for more learnings and change meaning of life with time
- A task.
- You have different answers at different times You learn different meanings at different times When you are studying, basically it is about job, finding a partner then it becomes, house, car other things based on your income in between, there can be passion too
Free Software was a passion, electoral politics too, but both kind of faded and I want cooperative and user driven development now (prav - something that motivates me every day) and these days learning Chinese and watching Cdrama takes a huge part of my leisure time it is heavily subjective and also influences by previous experiences people around you, how much influence they have on you
it also depends on if they had to struggle in their life or not, for some life did not give much troubles and trouble itself can be relative people who never had to struggle may find even smallest challenges as troubles like if you own a car, your worry is finding a parking slot
- I am too young to think about lyfe
- A ticket to see the show on earth, I guess
I guess life is different depending on the mood. It is a very broad question.
(Me - What is it in this present mood?)
Learning stuff (like I am learning a new language) and being happy but also to regulate emotions in a world where being optimistic is getting harder each day.
Life is also having a unique set of glasses you wear. Both in terms of looking from your eyeballs and your psychological perspective. Both are unique and cannot be replicated.
It is interesting what people on their deathbed think of life. If I know I am dying, my perspective would change a whole lot.
Life is finishing reading books while we are alive
Life is sleeping after a good XMPP chat
- Dukh dard peeda (translation - surrow pain suffering)
- uhh to word it? life is just like a journey from A to somewhere and its all about what paths you take and what line you get on to me, just a series of short adventures that all connect to a larger sequence until you can t have any more adventures-
(Me - eee, THE END. drop dead, like a coin)
yeaaaah- I am not really for spirituality of an afterlife, to me life just ends at some point, after which point there fails to remain a discernable you, and some X time after which, you will be last remembered, try to make that last time a good one I guess?
(Me - no soul?)
uhhh not in the way most people think of it i guess?
theres just a lot of yous, theres the physical you, there is the idea of you, there is the expectation of you, and one of the undefinable you I would label as the soul maybe? like the part thats not physically you, but also certainly you
(Me - can t say I understood part, but I get you in this sense)
mhm- well its about just questioning who you are more so questioning what life is-, I have sadly spent way too much time trying to figure that out
- Making the best of the time you have
- living a full range of experiences and embracing the good ones, seeing all that the world has to offer. In the end we were always just stardust. Might as well enjoy it when we are stardust with a consciousness of our own.
- For some reason or the Universe s /dev/random I was born here as a biological being, and from my experience I understood living is hard and the best way to live is by embracing it. Loving everyone and everything around you. Be happy and joyful until you naturally say good bye to this world.
- Life is being fucked by everything and you just have to figure out and try to stick to the things worth being fucked for
Note: Following was transcribed from a audio message.
- There are five conditions to become a life to survive in the environment. I think there s five conditions by the biological definitions and reproduction is one of the factor virus is not considered a life form because it cannot reproduce on its own but technically it s kind of a life because it reproduces using the DNA ability this is the biological definition. Do you want a philosophical definition?
My definition is kind of the same except that you get life experiences along with it as a human. Extra benefits is that you are not an NPC. All other organisms are NPCs. But humans can interpret the world and change it to their liking. That is life in the case of a human. But then many humans are mostly NPCs. But they still can change the life. Okay, fuck this. Where is this even going?
A human is an exception in the case of life, because human is not an NPC. Human can interrupt the world, human can change it to its liking, which is why we are such a successful organism on this planet. That is life to me. That s a human. But all of this is kind of meaningless, because the biological impurity of a human being still exists, so you still have the urges to reproduce, which kind of makes it like just another organism. But then, humans are yet to evolve to overcome that biological imperative.
I m grateful for all the replies, outlooks, and subsequent conversations I got to have after this question with everyone. After all, it was a deeply personal question. It does fit in nicely with my definition of life:
Life is all about experiences and all the transient relationships one gets to have with folks we meet on the way.

15 April 2026

Paul Tagliamonte: designing arf, an sdr iq encoding format

Interested in future updates? Follow me on mastodon at @paul@soylent.green. Posts about hz.tools will be tagged #hztools.

Want to jump right to the draft? I'll be maintaining ARF going forward at /draft-tagliamonte-arf-00.txt.
It s true processing data from software defined radios can be a bit complex which tends to keep all but the most grizzled experts and bravest souls from playing with it. While I wouldn t describe myself as either, I will say that I ve stuck with it for longer than most would have expected of me. One of the biggest takeaways I have from my adventures with software defined radio is that there s a lot of cool crossover opportunity between RF and nearly every other field of engineering. Fairly early on, I decided on a very light metadata scheme to track SDR captures, called rfcap. rfcap has withstood my test of time, and I can go back to even my earliest captures and still make sense of what they are IQ format, capture frequencies, sample rates, etc. A huge part of this was the simplicity of the scheme (fixed-lengh header, byte-aligned to supported capture formats), which made it roughly as easy to work with as a raw file of IQ samples. However, rfcap has a number of downsides. It s only a single, fixed-length header. If the frequency of operation changed during the capture, that change is not represented in the capture information. It s not possible to easily represent mulit-channel coherent IQ streams, and additional metadata is condemned to adjacent text files.

ARF (Archive of RF) A few years ago, I needed to finally solve some of these shortcomings and tried to see if a new format would stick. I sat down and wrote out my design goals before I started figuring out what it looked like. First, whatever I come up with must be capable of being streamed and processed while being streamed. This includes streaming across the network or merely written to disk as it s being created. No post-processing required. This is mostly an artifact of how I ve built all my tools and how I intereact with my SDRs. I use them extensively over the network (both locally, as well as remotely by friends across my wider lan). This decision sometimes even prompts me to do some crazy things from time to time. I need actual, real support for multiple IQ channels from my multi-channel SDRs (Ettus, Kerberos/Kracken SDR, etc) for playing with things like beamforming. My new format must be capable of storing multiple streams in a single capture file, rather than a pile of files in a directory (and hope they re aligned). Finally, metadata must be capable of being stored in-band. The initial set of metadata I needed to formalize in-stream were Frequency Changes and Discontinuities. Since then, ARF has grown a few more. After getting all that down, I opted to start at what I thought the simplest container would look like, TLV (tag-length-value) encoded packets. This is a fairly well trodden path, and used by a bunch of existing protocols we all know and love. Each ARF file (or stream) was a set of encoded packets (sometimes called data units in other specs). This means that unknown packet types may be skipped (since the length is included) and additional data can be added after the existing fields without breaking existing decoders.
tag
length
value
Heads up! Once this is posted, I'm not super likely to update this page. Once this goes out, the latest stable copy of the ARF spec is maintained at draft-tagliamonte-arf-00.txt. This page may quickly become out of date, so if you're actually interested in implementing this, I've put a lot of effort into making the draft comprehensive, and I plan to maintain it as I edit the format.
Unlike a traditional TLV structure, I opted to add flags to the top-level packet. This gives me a bit of wiggle room down the line, and gives me a feature that I like from ASN.1 a critical bit. The critical bit indicates that the packet must be understood fully by implementers, which allows future backward incompatible changes by marking a new packet type as critical. This would only really be done if something meaningfully changed the interpretation of the backwards compatible data to follow.
Flag Description
0x01Critical (tag must be understood)
Within each Packet is a tag field. This tag indicates how the contents of the value field should be interpreted.
Tag ID Description
0x01Header
0x02Stream Header
0x03Samples
0x04Frequency Change
0x05Timing
0x06Discontinuity
0x07Location
0xFEVendor Extension
In order to help with checking the basic parsing and encoding of this format, the following is an example packet which should parse without error.
 00, // tag (0; no subpacket is 0 yet)
 00, // flags (0; no flags)
 00, 00 // length (0; no data)
 // data would go here, but there is none
Additionally, throughout the rest of the subpackets, there are a few unique and shared datatypes. I document them all more clearly in the draft, but to quickly run through them here too:

UUID This field represents a globally unique idenfifer, as defined by RFC 9562, as 16 raw bytes.

Frequency Data encoded in a Frequency field is stored as microhz (1 Hz is stored as 1000000, 2 Hz is stored as 2000000) as an unsigned 64 bit integer. This has a minimum value of 0 Hz, and a maximum value of 18446744073709551615 uHz, or just above 18.4 THz. This is a bit of a tradeoff, but it s a set of issues that I would gladly contend with rather than deal with the related issues with storing frequency data as a floating point value downstream. Not a huge factor, but as an aside, this is also how my current generation SDR processing code (sparky) stores Frequency data internally, which makes conversion between the two natural.

IQ samples ARF supports IQ samples in a number of different formats. Part of the idea here is I want it to be easy for capturing programs to encode ARF for a specific radio without mandating a single iq format representation. For IQ types with a scalar value which takes more than a single byte, this is always paired with a Byte Order field, to indicate if the IQ scalar values are little or big endian.
ID Name Description
0x01f32interleaved 32 bit floating point scalar values
0x02i8 interleaved 8 bit signed integer scalar values
0x03i16interleaved 16 bit signed integer scalar values
0x04u8 interleaved 8 bit unsigned integer scalar values
0x05f64interleaved 64 bit floating point scalar values
0x06f16interleaved 16 bit floating point scalar values

Stream Header Immediately after the arf Header, some number of Stream Headers follow. There must be exactly the same number of Stream Header packets as are indicated by the num streams field of the Header. This has the nice effect of enabling clients to read all the stream headers without requiring buffering of unread packets from the stream.
id
flags
fmt
bo
rate
freq
guid
site
In order to help with checking the basic parsing and encoding of this format, the following is an example stream header subpacket (when encoded or decoded this will be found inside an ARF packet as described above) which should parse without error, with known values.
00, 01, // id (1)
00, 00, 00, 00, 00, 00, 00, 00, // flags
01, // format (float32)
01, // byte order (Little Endian)
00, 00, 01, d1, a9, 4a, 20, 00, // rate (2 MHz)
00, 00, 5a, f3, 10, 7a, 40, 00, // frequency (100 MHz)

// guid (7b98019d-694e-417a-8f18-167e2052be4d)
7b, 98, 01, 9d, 69, 4e, 41, 7a,
8f, 18, 16, 7e, 20, 52, be, 4d,

// site_id (98c98dc7-c3c6-47fe-bc05-05fb37b2e0db)
98, c9, 8d, c7, c3, c6, 47, fe,
bc, 05, 05, fb, 37, b2, e0, db,

Samples Block of IQ samples in the format indicated by this stream s format and byte_order field sent in the related Stream Header.
id
iq samples
In order to help with checking the basic parsing and encoding of this format, the following is an samples subpacket (when encoded or decoded this will be found inside an ARF packet as described above). The IQ values here are notional (and are either 2 8 bit samples, or 1 16 bit sample, depending on what the related Stream Header was).
01, // id
ab, cd, ab, cd, // iq samples

Frequency Change The center frequency of the IQ stream has changed since the Stream Header or last Frequency Change has been sent. This is useful to capture IQ streams that are jumping around in frequency during the duration of the capture, rather than starting and stopping them.
id
frequency
In order to help with checking the basic parsing and encoding of this format, the following is a frequency change subpacket (when encoded or decoded this will be found inside an ARF packet as described above).
01, // id
00, 00, b5, e6, 20, f4, 80, 00 // frequency (200 MHz)

Discontinuity Since the last Samples packet for this stream, samples have been dropped or not encoded to this stream. This can be used for a stream that has dropped samples for some reason, a large gap (radio was needed for something else), or communicating iq snippits .
id
In order to help with checking the basic parsing and encoding of this format, the following is a discontinuity subpacket (when encoded or decoded this will be found inside an ARF packet as described above).
01, // id

Location Up-to-date location as of this moment of the IQ stream, usually from a GPS. This allows for in-band geospatial information to be marked in the IQ stream. This can be used for all sorts of things (detected IQ packet snippits aligned with a time and location or a survey of rf noise in an area)
flags
sys
lat
long
el
accuracy
The sys field indicates the Geodetic system to be used for the provided latitude, longitude and elevation fields. The full list of supported geodetic systems is currently just WGS84, but in case something meaningfully changes in the future, it d be nice to migrate forward. Unfortunately, being a bit of a coward here, the accuracy field is a bit of a cop-out. I d really rather it be what we see out of kinematic state estimation tools like a kalman filter, or at minimum, some sort of ellipsoid. This is neither of those - it s a perfect sphere of error where we pick the largest error in any direction and use that. Truthfully, I can t be bothered to model this accurately, and I don t want to contort myself into half-assing something I know I will half-ass just because I know better.
System Description
0x01 WGS84 - World Geodetic System 1984
In order to help with checking the basic parsing and encoding of this format, the following is a location subpacket (when encoded or decoded this will be found inside an ARF packet as described above).
00, 00, 00, 00, 00, 00, 00, 00, // flags
01, // system (wgs84)
3f, f3, be, 76, c8, b4, 39, 58, // latitude (1.234)
40, 02, c2, 8f, 5c, 28, f5, c3, // longitude (2.345)
40, 59, 00, 00, 00, 00, 00, 00, // elevation (100)
40, 24, 00, 00, 00, 00, 00, 00 // accuracy (10)

Vendor Extension In addition to the fields I put in the spec, I expect that I may need custom packet types I can t think of now. There s all sorts of useful data that could be encoded into the stream, so I d rather there be an officially sanctioned mechanism that allows future work on the spec without constraining myself. Just an example, I ve used a custom subpacket to create test vectors, the data is encoded into a Vendor Extension, followed by the IQ for the modulated packet. If the demodulated data and in-band original data don t match, we ve regressed. You could imagine in-band speech-to-text, antenna rotator azimuth information, or demodulated digital sideband data (like FM HDR data) too. Or even things I can t even think of!
id
data
In order to help with checking the basic parsing and encoding of this format, the following is a vendor extension subpacket (when encoded or decoded this will be found inside an ARF packet as described above).
// extension id (b24305f6-ff73-4b7a-ae99-7a6b37a5d5cd)
b2, 43, 05, f6, ff, 73, 4b, 7a,
ae, 99, 7a, 6b, 37, a5, d5, cd,

// data (0x01, 0x02, 0x03, 0x04, 0x05)
01, 02, 03, 04, 05

Tradeoffs The biggest tradeoff that I m not entirely happy with is limiting the length of a packet to u16 65535 bytes. Given the u8 sample header, this limits us to 8191 32 bit sample pairs at a time. I wound up believing that the overhead in terms of additional packet framing is worth it because always encoding 4 byte lengths felt like overkill, and a dynamic length scheme ballooned codepaths in the decoder that I was trying to keep as easy to change as possible as I worked with the format.

Dirk Eddelbuettel: qlcal 0.1.1 on CRAN: Calendar Updates

The nineteenth release of the qlcal package arrivied at CRAN just now, and has already been built for r2u. This version synchronises with QuantLib 1.42 released this week. 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 sixty 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 releases updates to the 2025 holidays for China, Singapore, and Taiwan. The full details from NEWS.Rd follow.

Changes in version 0.1.1 (2026-04-15)
  • Synchronized with QuantLib 1.42 released two days ago
  • Calendar updates for China, Singapore, Taiwan

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.

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

Emmanuel Kasper: Minix 3 on Beagle Board Black (ARM)

Connected via serial console. Does not have a package manager, web or ssh server, but can play tetris in the terminal (bsdgames in Debian have the same tetris version packaged).
asciicast

Freexian Collaborators: Debian Contributions: Debusine projects in GSoC, Debian CI updates, Salsa CI maintenance and more! (by Anupa Ann Joseph)

Debian Contributions: 2026-03 Contributing to Debian is part of Freexian s mission. This article covers the latest achievements of Freexian and their collaborators. All of this is made possible by organizations subscribing to our Long Term Support contracts and consulting services.

Debusine projects in Google s Summer of Code While Freexian initiated Debusine, and is investing a lot of resources in the project, we manage it as a true free software project that can and should have a broader community. We always had documentation for new contributors and we aim to be reactive with them when they interact via the issue tracker or via merge requests. We decided to put those intentions under stress tests by proposing five projects for Google s Summer of Code as part of Debian s participation in that program. Given that at least 11 candidates managed to get their merge request accepted in the last 30 days (interacting with the development team is part of the pre-requisites to apply to Google Summer of Code projects these days), the contributing experience must not be too bad. If you want to try it out, we maintain a list of quick fixes that are accessible to newcomers. And as always, we welcome your feedback!

Debian CI: incus backend and upgrade to Bootstrap 5, by Antonio Terceiro debci 3.14 was released on March 4th, with a followup 3.14.1 release with regression fixes a few days afterwards. Those releases were followed by new development and maintenance work that will provide extra capabilities and stability to the platform. This month saw the initial version of an incus backend land in Debian CI. The transition into the new backend will be done carefully so as to not disrupt testing migration. Each package will be running jobs with both the current lxc backend and with incus. Packages that have the same result on both backends will be migrated over, and packages that exhibit different results will be investigated further, resulting in bug reports and/or other communication with the maintainers. On the frontend side, the code has been ported to Bootstrap 5 over from the now ancient Bootstrap 3. This need has been originally reported back in 2024 based on the lack of security support for Bootstrap 3. Beyond improving maintainability, this upgrade also enables support for dark mode in debci, which is still work in progress. Both updates mentioned in this section will be available in a following debci release.

Salsa CI maintenance by Santiago Ruano Rinc n et al. Santiago reviewed some Salsa CI issues and reviewed associated merge requests. For example, he investigated a regression (#545), introduced by the move to sbuild, on the use of extra repositories configured as .source files; and reviewed the MR (!712) that fixes it. Also, there were conflicts with changes made in debci 3.14 and debci 3.14.1 (those updates are mentioned above), and different people have contributed to fix the subsequent issues, in a long-term way. This includes Rapha l who proposed MR !707 and who also suggested Antonio to merge the Salsa CI patches to avoid similar errors in the future. This happened shortly after. Those fixes finally required the unrelated MR !709, which will prevent similar problems when building images. To identify bugs related to the autopkgtest support in the backport suites as early as possible, Santiago proposed MR !708. Finally, Santiago, in collaboration with Emmanuel Arias also had exchanges with GSoC candidates for the Salsa CI project, including the contributions they have made as merge requests. It is important to note that there are several very good candidates interested in participating. Thanks a lot to them for their work so far!

Miscellaneous contributions
  • Rapha l reported a zim bug affecting Debian Unstable users, which was already fixed in git apparently. He could thus cherry-pick the fix and update the package in Debian Unstable.
  • Carles created a new page on the InstallingDebianOn in Debian Wiki.
  • Carles submitted translation errors in the debian-installer Weblate.
  • Carles, using po-debconf-manager, improved Catalan translations: reviewed and submitted 3 packages. Also improved error handling when forking or submitting an MR if the fork already existed.
  • Carles kept improving check-relations: code base related general improvements (added strict typing, enabled pre-commit). Also added DebPorts support, virtual packages support and added commands for reporting missing relations and importing bugs from bugs.debian.org.
  • Antonio handled miscellaneous Salsa support requests.
  • Antonio improved the management of MiniDebConf websites by keeping all non-secret settings in git and fixed exporting these sites as static HTML.
  • Stefano uploaded routine updates to hatchling, python-mitogen, python-virtualenv, python-discovery, dh-python, pypy3, python-pipx, and git-filter-repo.
  • Faidon uploaded routine updates to crun, libmaxminddb, librdkafka, lowdown, platformdirs, python-discovery, sphinx-argparse-cli, tox, tox-uv.
  • Stefano and Santiago continued to help with DebConf 26 preparations.
  • Stefano reviewed some contributions to debian-reimbursements and handled admin for reimbursements.debian.net.
  • Stefano attended the Debian Technical Committee meeting.
  • Helmut sent 8 patches for cross build failures.
  • Building on the work of postmarketOS, Helmut managed to cross build systemd for musl in rebootstrap and sent several patches in the process.
  • Helmut reviewed several MRs of Johannes Schauer Marin Rodrigues expanding support for DPKG_ROOT to support installing hurd.
  • Helmut incorporated a final round of feedback for the Multi-Arch documentation in Debian policy, which finally made it into unstable together with documentation of Build-Profiles.
  • In order to fix python-memray, Helmut NMUed libunwind generally disabling C++ exception support as being an incompatible duplication of the gcc implementation. Unfortunately, that ended up breaking suricata on riscv64. After another NMU, python-memray finally migrated.
  • Thorsten uploaded new upstream versions of epson-inkjet-printer-escpr and sane-airscan. He also fixed a packaging bug in printer-driver-oki. As of systemd 260.1-1 the configuration of lpadmin has been added to the sysusers.d configuration. All printing packages can now simply depend on the systemd-sysusers package and don t have to take care of its creation in maintainer scripts anymore.
  • In collaboration with Emmanuel Arias, Santiago had exchanges with GSoC candidates and reviewed the proposals of the Linux livepatching GSoC 2026 project.
  • Colin helped to fix CVE-2026-3497 in openssh and CVE-2026-28356 in multipart.
  • Colin upgraded tango and pytango to new upstream releases and packaged pybind11-stubgen (needed for pytango), thanks to a Freexian customer. Tests of reproducible builds revealed that pybind11-stubgen didn t generate imports in a stable order; this is now fixed upstream.
  • Lucas fixed CVE-2025-67733 and CVE-2026-21863 affecting src:valkey in unstable and testing. Also reviewed the same fixes targeting stable proposed by Peter Wienemann.
  • Faidon worked with upstream and build-dep Debian maintainers on resolving blockers in order to bring pyHanko into Debian, starting with the adoption of python-pyhanko-certvalidator. pyHanko is a suite for signing and stamping PDF files, and one of the few libraries that can be leveraged to sign PDFs with eIDAS Qualified Electronic Signatures.
  • Anupa co-organized MiniDebConf Kanpur and attended the event with many others from all across India. She handled the accommodation arrangements along with the registration team members, worked on the budget and expenses. She was also a speaker at the event.
  • Lucas helped with content review/schedule for the MiniDebConf Campinas. Thanks Freexian for being a Gold sponsor!
  • Lucas organized and took part in a one-day in-person sprint to work on Ruby 3.4 transition. It was held in a coworking space in Brasilia - Brazil on April 6th. There were 5 DDs and they fixed multiple packages FTBFSing against Ruby 3.4 (coming to unstable soon hopefully). Lucas has been postponing a blog post about this sprint since then :-)

Next.

Previous.