Welcome to my web log. See the first post for an introduction. See the archive page for all posts. (There is an english language feed if you don't want to see Finnish.)
Archives Tags Moderation policy Main site
Me on Mastodon, for anything that is too small to warrant a blog post.
All content outside of comments is copyrighted by Lars Wirzenius, and licensed under a Creative Commons Attribution-Share Alike 3.0 Unported License. Comments are copyrighted by their authors. (No new comments are allowed.)
sartorial (comparative more sartorial, superlative most sartorial)
(not comparable) Of or relating to the tailoring of clothing.
Synonym: vestiaryOf or relating to the quality of dress.
In his smart suit Jacob was by far the most sartorial of our party.(anatomy) Of or relating to the sartorius muscle.
(From Wiktionary)
Three years ago over the Christmas holidays I embarked on a sartorial adventure. It started with the realization that I found most of my clothes uncomfortable. For the longest time I'd been wearing cargo trousers and T-shirts, and they never fit me well, and for whatever reason I started thinking that maybe I didn't have to live with discomfort.
I also have a troubled relationship with my body. I don't like the way I look. I have self-confidence issues related to this. I don't like seeing myself. After I started thinking about changing what I wear, I realized that I could tackle those issues at the same time.
I wasn't raised to care about clothes or appearance, much. Clean clothes without many holes in them was the goal I took away into adulthood. It's what I've done most of my life.
After some online research, and a brief infatuation with the "corporate Goth" look, I decided that given my overall tendencies, classic European men's style is the one I tend to like, and would like strive for. That means business suits and similar clothing from the past century.
I've taken it slow. I don't want to make sudden big changes and then regret them. I wanted to take my time to learn what I actually like, from experience. I also need to learn about clothing and wearing it that boys learned growing up in the first half of the 20th century.
There is luckily a lot of good material about this online. Here is a condensed, highly biased summary of what I've learned so far.
Comfortable clothing means clothing that fits your body well, and is made mostly of natural materials. Fit matters so the clothes aren't too tight, so they squeeze, or too large, so they snag, or get in the way you move. Materials matter for temperature control: artificial materials tend to make you sweat or don't keep you warm, where natural ones keep you cool or warm as appropriate. I like wool, especially.
Belts are really uncomfortable, but suspenders (or braces) are much better. I have a large stomach, which makes belts especially bad, but for most people, belts are at best mediocre at keeping trousers up. A pair of suspenders keep trousers at the right height more securely.
If I keep nothing else from this adventure, it's suspenders.
I gain much confidence from wearing clothes I have carefully chosen for myself, rather than what everyone else is wearing. It doesn't even matter much if others like how I look, although it's nice when I am complimented. In fact, I've been complimented on my looks more these past three years than the preceding fifty. I felt the confidence boost before the compliments, though.
Taking care of clothes and shoes requires effort and knowledge: laundry, ironing, patching, etc. The benefit of artificial materials is that they tend to require less effort. I find the maintenance effort is worth it, but also that I need to burn some willpower to start. I am a lazy slob.
Given the shape of my body, I've ended up having most of my new clothes made to measure for me. Mass produced clothes rarely fit me well, and I now want something that does.
On terminology: clothing that's ready to wear when it's bought is called "off-the-rack" or "ready-to-wear". It often benefits from some alterations and adjustments, because there's more body shape variation than mass producers are willing to cater for.
Custom-made clothing is either "made-to-measure" or "tailor-made". For made to measure, the clothes are made by adjusting a generic pattern to the measurements of your body. Tailors create a pattern just for you. The generic pattern can be varied, but within limits. A completely new pattern has no limits.
On cost: The upfront cost of custom clothing is higher than for mass produced clothes, but the quality is so much higher, over time the cost is lower. Of course, you have to be able to afford the upfront cost. For shirts, trousers, and jackets, I've found made to measure ones are about two to three times the cost of off the rack ones.
Tailored clothing are many more times more expensive than that and I've not tried that option.
Some garments are OK mass produced. For example, shoes, gloves, hats, overcoats. For these, either there's less variation or fit doesn't need to be as exact.
Colors turn out to be important. A monochromatic look can work, but more colors tends to be more visually interesting. I'm still learning and experimenting with this.
I don't wear a suit at all times. If I'm alone at home, I tend to go for sweat pants and T or polo shirts. This is partly because they're comfortable, and partly to save wear and tear on more expensive garments.
I've come to realize that I detest the fashion industry. I don't detest fashion as such: it's perfectly fine that there are changing trends in clothing. It would be boring if everything always stayed the same. The industry has weaponized this to pressure people to buy much more new clothes than they need, to the detriment of everyone and everything.
Organic trends and changes: yes. Cynical exploitative industry: no.
I'll mention this to be clear: this is my adventure and clothing for me. I don't care what you wear. If you're happy that's all that matters. If you prefer shorts, sandals, and no shirt, that's OK. I'm not interested in trying to influence what you wear. I'm sharing this in the hope someone else finds it interesting.
I collect links to web sites, publications, and companies related to men's style. This is primarily for my own benefit, but I'm happy if it helps anyone else.
This is a summary of the current state of Radicle CI and near future plans. The goal is to make it a monthly newsletter.
Radicle is an open source, peer-to-peer code collaboration stack built on Git. Unlike centralized code hosting platforms, there is no single entity controlling the network. Repositories are replicated across peers in a decentralized manner, and users are in full control of their data and workflow.
Radicle CI adds continuous integration support to Radicle. Any Radicle node can choose to run CI for any repository it has access to. Any project using Radicle can choose which CI nodes it trusts. Radicle CI has integrations with a number of CI systems, and making new ones is easy.
Ambient CI is a CI engine that makes it safe and secure to run CI on untrusted code.
History
As this is the first report, we'll start with a summary of the history of Radicle CI.
Work on Radicle CI started the second half of 2023. We quickly picked the current architecture of a CI broker that listens to events from the node, and runs an adapter program to actually run CI on the change. This allows all the tricky parts to be in one program and makes it fairly easy to add support for new CI systems. At least it's easy from the Radicle side: some external CI systems make it tricky to integrate with them.
In 2024 the CI broker gained enough functionality to be usable. The first release was in April.
From the beginning, the CI broker was accompanied by a "native adapter", the simplest possible implementation of a CI system, which merely runs a shell snippet locally. This made Radicle CI feasible to use in some circumstances. The native adapter is, however, not very safe, because it provides not isolation at all. The main goal for the native adapter is to have some adapter for use when developing the CI broker.
There were soon adapters for Concourse and other CI systems, and a generic webhook adapter to ease integration to external CI systems.
In January 2025 the Ambient adapter was created. This made Radicle CI a realistic standalone CI system. It no longer requires using external CI systems, but use of them continues to be supported.
Early in 2025 the rad-ci program was created. It emulates what happens in
a CI run on the local machine, initially for the native adapter, but soon
also for the Ambient one. This means a developer does not have to wait for
a CI node to have time to run CI for their change, they can just run it
locally. Due to the nature of CI systems, rad-ci only really works with some
adapters, because emulating a complicated CI system is a lot of work.
In mid-2025 Radicle job COBs were implemented in a production ready way. A job COB lets Radicle nodes update the CI status for a commit: they carry information about which node has run CI for which commit, and if it succeeded or failed. Lars had tried to implement them from 2023, but Fintan actually did it well. The COBs are created by the CI broker to notify other Radicle nodes that an automated process has been run for a specific commit. The Radicle desktop app shows CI status using them, and the web view is going to.
Also in mid-2025, the CI broker started supporting concurrent CI runs, although only one at a time per repository.
Current status
Radicle CI is in production use. It is not yet a joy to use. Much work remains to be done to get there. There are a few CI node instances, and Lars runs one using Ambient for open source Rust projects at https://callisto.liw.fi/.
Future plans
First of all, update this report monthly. The current rate of change probably doesn't warrant a weekly update.
Lars plans to concentrate on making it easier to set up and run Radicle CI at least for the rest of 2025.
Notes
If you have an open source Rust project and want to try Radicle and Ambient on
callisto, see https://callisto.liw.fi/callisto/ for instructions.
Links
- Radicle CI home page, unofficial
- Radicle CI broker repository
- Radicle CI Ambient adapter repository
- Radicle CI integrations documentation
Quotes
No quotes yet, but please suggest something for the next issue.
This blog post is a reaction to a blog post on Stopping bad guys. (I've shortened the title. Please follow link to read original.)
I write open source software for the sake of humanity. I want to live off my work, certainly, but more importantly, I want the software I build to make life better for other people in the future.
I don't think open source developers should use licenses to combat bad guys doing evil things. I think doing so will harm the ecosystem, but not prevent actual evil from happening.
It is becoming increasingly common for open source developers to be concerned what other people do with their software. The blog post I linked to above is an example. Many developers object to their software being used by fossil fuel companies, oppressive law enforcement, or others. Some of the developers are trying to change their open source licenses to prevent those groups from using the software.
I concur with the goal: the modern day Gestapo in the US should be stopped, and so should companies who destroy the global ecosystem. Genocide should be stopped and prevented. All of these need to happen, but using licenses as a weapon for this is a bad idea. It doesn't actually work, but it poisons the open source ecosystem.
A fundamental reason why open source thrives at all is because it enables easy, low-friction use of existing software to build new software. I write a library, you combine parts of that and parts of other libraries to make an application, but you do not have to negotiate terms. When we did this tens of millions times we got the world of today where every information system is at least partly made out of open source software.
Open source licenses are not all fully compatible with each other, but there's enough popular, compatible licenses that by and large it's nearly always possible to combine code from different sources to build something new.
I'm old and cynical: those who kidnap or murder people, or who are just immensely wealthy, don't really care if they violate open source licenses, because there not really anyone with the will and resources to stop them. Changing the license of your open source project won't stop, say, the IDF, ICE, Shell, or Meta, who all find they can stand above the law.
The good guys who would build something benign on what you've done will, however, have to tread much more carefully and do much more work to do so. They're good people, and they do their best to follow the rules. A well-meaning, but incompatible, license may cause too much friction for the benign application from ever being created.
We, the non-evil parts of humanity, need other ways to resist evil people and organizations. I'm afraid I don't have an easy solution. You can speak up when you see a problem. You can refuse to help evildoers. You can resist bad things. You can take care of other people. You can do and build good things. These are all going to work in the long run, I'm sure, but they require a lot of courage, a lot of persistence, and a lot of time. In Finnish terms, they require a lot of sisu.
Another aspect that open source developers worry about is if a large corporation makes use of their software to make a profit, but doesn't contribute back in any way. I can understand this worry, but I am myself in the lucky position where I don't need to care. I'm OK with others profiting from what I've built, as long as they don't cause trouble for me, or cause me to have to do more without compensation. If I was trying to, say, run a paid service and Amazon competed with me, I might think differently. But I care more about building things to help humanity.
I mostly don't care about for-profit companies: they can be a useful social construct, but people actually matter. As long as companies, meaning the people who run them, don't harm people, or the environment, and don't get in the way of making things better for people, I'm happy to not care.
If they want me to do something, either they pay me or I say no.
This is my opinion, and I'm fine with others disagreeing with me. I may well change my mind when I think about this further. I publish this on my blog so that I get it out of my head, and make room for my thoughts about this to grow.
Ambient CI is the CI engine I'm building for myself, and tentatively for Radicle CI. I last blogged about it as a project over a year ago. Since then, the focus and goal of the project has crystallized in my head as:
- You should be able to run CI for other people safely and securely, without much effort.
- We as software developers should be able to share our computing resources with each other to run CI for each other. This should be safe and secure for us to do.
- We should be able to run CI locally and get the same exact results as when a server does it.
In other words, safe and securet distributed and local CI for everyone.
The approach I've taken is that any code from the software under test, or any of its dependencies, none of which can inherently be trusted, is only ever run in an isolated, network-less virtual machine. I've proven to myself that this works well enough. It will not, of course, work for any software project that inherently requires network access to build or test, but those are rare excecptions.
For now, I mostly care about automatically building software, and running its automated tests.
Ambient can publish build artifacts with rsync and deb packages with dput; other delivery methods are easy to add, too.
I have not worried about deployment, yet.
Deployment seems a tricky problem in a distributed system, but I'll worry about it when the integration parts work well.
Ambient is not there yet. It will take a lot more work, but there's been some progress.
- Most importantly for me, I've integrated the Ambient engine with the Radicle CI subsystem. This is important to me because Radicle, the distributed Git forge, handles all the boring server parts and I don't need to implement them. I get paid to work on Radicle CI and we're experimenting with using Ambient as the default CI engine.
- I've used Ambient, with Radicle, as my primary CI system for this year.
The combination has worked well.
I also use GitLab CI on
gitlab.comfor one or two projects where I collaborate with people who don't want to use Radicle. - I've set up https://callisto.liw.fi/, a host that runs CI with Ambient+Radicle for open source Rust projects. See https://callisto.liw.fi/callisto/ for instructions, and https://blog.liw.fi/posts/2025/callisto/ for the announcement. I do this to get more experience with running CI for other people.
There are, of course, problems (see open issues). Apart from the recorded issues, I worry about what I don't know about. Please educate me.
Two of the big problems I know about are:
- Before the actual build happens, in a network-less virtual machine, build dependencies need to be downloaded. I've only implmented this for Rust crates, and even that needs improvement. Other languages and other build dependencies need to supported too. This is an area where I will certainly need help, as I don't know most language ecosystems.
- The dependency downloading and the delivery actions are run on the host system. Ambient needs to isolate them, too, into being run in a virtual machine. If nothing else, this relieves the host system from having go have language tool chains installed.
- So far I'm using a custom build Debian image for the virtual machine. I write my own VM image building software, so this is no big deal for me. However, Ambient really should be able to use published "cloud images" for any operating system, as a base image. I only deal with Debian, really, so I'll need help with this, even for other Linux distributions. From a software architecture point of view, Ambient requires fairly little from the VM image. Maybe some day someone will add support for Windows and macOS, too.
- Ambient runs the VM using QEMU, and needs to unlock architecture support by not assuming VM image is in the host architecture.
Emulating a foreign architecture is not very fast, but slow is better than impossible.
Imagine being able to produce binaries for
x86_64,aarch64,riscv64, and any of the other architecture QEMU supports. - Ambient currently only supports a very straightforward CI run plan, with a linear sequence of actions to execute. Ambient needs to support matrix builds, and more generally build graphs. By build graphs I mean things like generating some artifacts once, then using those on a list of architectures to produce more, then process those artifacts once, and finally deliver some or all of the artifact to various places. In a distributed manner, of course.
- I need to learn how to count. I also need to learn to resist the temptation to make a tired joke about hard problems in computer science, but that keeps dropping out of my short term memory.
That's where Ambient stands currently. It works, at least in my simple use, and it's distributed.
If CI systems interest you, give Ambient a look. Let me know what you think, by email or on the fediverse.
I've just released version 0.6.0 for sopass, my
command line password manager that I use instead of pass.
Version 0.6.0, released 2025-10-31
If I were of the American persuasion, this would be a spooky release. But I'm not, so it's a comfy release that doesn't scare anyone.
The
sopass value generatecommand generates a new random value for a name.There have also been other changes. A
debpackage is built and published by CI for every merge into themainbranch. The documentation of acceptance criteria is published at https://doc.liw.fi/sopass/sopass.html. Lars has decided to not work on cross-device syncing, as it's not something he needs, even though it's an interesting technical problem.
I've spent most Sundays for the past half a year implementing Obnam 3, the third generation of my backup program. I've posted a blog post of each three-hour session on the Obnam blog. It's way too much detail for anyone not passionately interested in this project. Here is a summary of what I've done. There is also an appeal for help.
I've implemented the lowermost storage layer of storing backups: the chunk. A chunk is a piece of data, either a small file or a part of a longer file. The chunk is encrypted in a way that also allows verifying the chunk hasn't been modified while in backup storage.
Each chunk is encrypted with a random, computer-generated symmetric key, which the user never sees. There can be any number of such keys, for different groups of chunks, although the implementation doesn't yet make it convenient to choose the key to use when encrypting a chunk. The chunk keys are stored in a client chunk, which itself is encrypted with another random, computer-generated key, the client key.
The client key is encrypted in various ways, and the result of each of those encryption operations is stored in a credential chunk. I've implemented credential encryption methods using OpenPGP software keys, and OpenPGP cards.
This part works and although it needs polish, I'm pretty happy with it.
There is also a rudimentary backup repository, which stores chunks in a local directory and allows searching for chunks by id or label. Chunk labels are short strings cryptographically attached to the chunk to give the type of a chunk, or the encrypted checksum of the plaintext data in a chunk, for de-duplication.
I've intentionally limited myself to a single Sunday session per week, at most three hours per session. This has been quite enjoyable: I am not in a hurry, and I can try to be careful and deliberate. In my profession that is not as common as I would like. Three hours a week has been enough to make progress, even if slowly. But fast enough for a hobby project.
i'm not yet sure what I will do next, but supporting remote backup
repositories seems like a sensible choice. I will need to do some research
for that: I will need to learn about the S3 API, and look at the Rust iroh
library for NAT hole punching.
Obnam is a large project, more than I can do by myself. Obnam needs, for example, documentation, even if at this stage for developers, not yet end users. There's code changes needed, too: more credential methods (password, TPM2 chip, ...), and all the code actually make backups. Someone will need to research and implement ways of splitting different kinds of files into chunks. It would be good to have a better idea of what's needed: use cases, acceptande criteria. There is no shortage of things to do.
What part of building backup software interests you? How would you like to help?
Every program I write is in some sense a command line program, even if it transmogrifies itself into a server process or an interactive terminal user interface. It starts by doing the kinds of things a Unix command line program does: it parses the command line, maybe loads some configuration files, maybe runs some other programs.
I've made a crate, clingwrap, which makes
a couple of the common things easier. I've done my best to implement the
well and put them in a library. This means I don't keep copying the code from
project to project, inevitably resulting in differences, and bugs fixed in one
place, but not the others.
It's a small library, and may never grow big. There's a module for handling
configuration files, and one to run other programs. Note that it's a library,
not a framework. You call clingwrap, it doesn't call you.
I use clap for command line parsing, and
don't feel to wrap or reinvent that.
Example
The code below parses configuration files for a "hello, world" program. It also validates the result of merging many configuration files. The result of the validation is meant to not require checking at run time: if the configuration files can be loaded, the configuration is valid.
use clingwrap::config::*;
use serde::{Deserialize, Serialize};
#[derive(Debug)]
struct Simple {
greeting: String,
whom: String,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize, Eq, PartialEq)]
struct SimpleFile {
greeting: Option<String>,
whom: Option<String>,
}
impl<'a> ConfigFile<'a> for SimpleFile {
type Error = SimpleError;
fn merge(&mut self, config_file: SimpleFile) -> Result<(), Self::Error> {
if let Some(value) = &config_file.greeting {
self.greeting = Some(value.to_string());
}
if let Some(value) = &config_file.whom {
self.whom = Some(value.to_string());
}
Ok(())
}
}
#[derive(Default)]
struct SimpleValidator {}
impl ConfigValidator for SimpleValidator {
type File = SimpleFile;
type Valid = Simple;
type Error = SimpleError;
fn validate(&self, runtime: &Self::File) -> Result<Self::Valid, Self::Error> {
Ok(Simple {
greeting: runtime.greeting.clone().ok_or(SimpleError::Missing)?,
whom: runtime.whom.clone().ok_or(SimpleError::Missing)?,
})
}
}
#[derive(Debug, thiserror::Error)]
enum SimpleError {
#[error("required field has not been set")]
Missing,
}
Last year I wrote a command line password manager, after deciding I didn't
like pass any more, and didn't like anything else I found, either. It's
called sopass. I've switched over to sopass
entirely. I'm happy with it, for my simple needs.
I've been thinking a lot about cross-device and group use. pass supports
storing the encrypted secrets in Git and syncing them across computers, even
between people. This usually works quite well, because each secret is in
a separate file. Thus merge conflicts are unusual, unless the same secret
is updated at the same time on two different hosts. That doesn't work with
sopass, which puts all secrets in one file. That was one of the reasons I
wrote the software.
If I were to support cross-device syncing in sopass, I'd want to do better
than pass. I would want to entirely avoid merge conflicts.
The idea for implementing this that I have is to use a CRDT, a conflict-free
replicated data type. Basically, a sopass database would be a Git repository
and each atomic change would be a separate commit: set key to value,
rename key, remove key. The CRDT would merge the changes in a way that
guarantess there is never a conflict. This might require arbitrarily, but
deterministically, choosing one change from a small set of changes that can't
be ordered otherwise. That might result in occasional surprised users (what
joy!), but no data is lost, it's still there in Git history. The UI could
expose this in some way.
This would actually be an interesting technical challenge to implement, but given that I have a wealth of such challenges, a drought of free time, and no current need for this, I'm going to pass on this. But I thought I'd write up the thought in case it inspires someone else.
It is common to suggest to open source projects that they should ask for donations to fund development. My understanding is that this almost never works: very, very few people donate. I have other reasons to not do that.
I live in Finland. We have a law that requires prior permission from the police to appeal to the public for donations. That's why I don't ask for donations.
I also work full time, and I'm well compensated. I live comfortably, and have no significant unmet needs. I have a home, food, and healthcare, and so I'm lucky to not need donations. That's why I don't accept donations. I'd rather you donate to someone who needs it more than I do.
- The law covers appealing for donations, not accepting donations.
- This is why the Wikimedia Foundation doesn't fund raise in Finland.
- The interpretation of the law by the police, the prosecutors, and the courts is sufficiently inconsistent and unpredictable that I don't want to try my luck. I'd rather avoid gray areas.
- Don't ask me to explain why the law exists.
- Don't ask me to interpret the law.
- Don't ask me to defend the law.
- Do ask me for my availability for your open source develompent needs.
- I'm happy to sell my time to develop software. I have a company that I can use to invoice that work. Contact me privately if you're interested.
I asked a Finnish law firm to write up an expert opinion about funding open source projects in Finland. It's in Finnish, sorry.
(I've written and published this blog post so I have something to point people at, when the topic comes up in discussion.)
Summary: I'd like help maintaining vmdb2, my
software for creating virtual machine images with Debian installed.
In 2011 I needed to create six similar Debian virtual machines, differing
in Debian release and computer architecture. This was tedious, and so it
needed to be automated. I wrote vmdebootstrap, which worked OK for a few
years, but was not very flexible. It had a fixed sequence of operations
that could only be slightly varied using options. When it worked, it was
fine, but increasingly it didn't work. I was facing an ever-growing set
of options, some of which would be mutually incompatible. With N options,
you need to test N2 combinations. That did not appeal.
In 2017 I got tired of the growing complexity and write vmdb2, which didn't
have a fixed sequence of operations. Instead, it read an input file that
lists the operations to do, and their order. This was much more flexible.
Combinatorial explosion averted.
I still maintain vmdb2 but for many years now it has been in a "selfish
maintainership" mode, where I only really fix or change anything if it
affects me, or I have some other such reason do something. I've done this to
protect my free time and my sanity.
Despite this there are a few people using it and I think it's time to make
sure vmdb2 has a better future.
The problem, from my point of view, with maintaining vmdb2 is that many
people use to build images for systems that are wildly different from what
I originally built vmdebootstrap for: Intel architecture virtual machines.
Indeed, I do that myself: I built a Debian installer on top of vmdb2 for
bare metal PC hardware (https://v-i.liw.fi/).
I am not any kind of deep expert in boot loaders, UEFI, or hardware support, or layers close to these in a Linux operating system. Debugging problems with these is tedious and frustratring. Reviewing changes related to them as well.
I also can't spend a ton more time on vmdb2, as I have an embarrassing
plethora of other hobby projects.
Therefore, I'd like help maintaining vmdb2. If you use it, or this area of
system software interests you, and you'd like to help, please let me know.
If I can do something to make it easier for you to help, let me know.
My contact information is public. Email is preferred.
