Note: This article builds on my dendritic sops-nix flake template.

Nix is littered with alternative solutions to things.

Most people who use Nix to manage their irssi configuration would use the home-manager irssi extensions. But I have a principle that I’ve held onto: I like to keep program configuration non-nixified so that they can still hypothetically be used outside of Nix.

The problem: irssi keeps password authentication in the config file

Irssi was first released in 1999. Back then you didn’t think about these things.

But having a password in a configuration file means you can’t commit it to git.

Here’s a problematic irssi config section:

chatnets = {
  liberachat = {
    type = "IRC";
    sasl_mechanism = "PLAIN";
    sasl_username = "sshine";
    sasl_password = "p4ssw0rd"; # <- can't commit this!
  };
};

(If you’re wondering, that’s not actually Nix code, but irssi config code. Damn!)

What I want is to keep the plaintext master under version control, redacted, and have Nix splice the real password in at activation, into a file that never touches the Nix store. This way the password lives in ~/.irssi/config which gets constructed from a combination of a passwordless config and a sops-nix secret.

tl;dr

sops.templates does exactly that. You give it content containing config.sops.placeholder.<name> markers, and sops-nix renders the finished file on the box, at a path you choose, with an owner and mode you choose:

{ config, primaryUser, ... }:
{
  sops.secrets.irssi_libera_sasl_password.sopsFile = ../secrets/dao/irssi.yaml;

  sops.templates."irssi-config" = {
    path = "/home/${primaryUser}/.irssi/config";
    owner = primaryUser;
    mode = "0600";
    content =
      builtins.replaceStrings
        [ "@LIBERA_SASL_PASSWORD@" ]
        [ config.sops.placeholder.irssi_libera_sasl_password ]
        (builtins.readFile ../dot.irssi/config);
  };
}

Now I can write the following in my git-committed irssi config:

chatnets = {
  liberachat = {
    type = "IRC";
    sasl_mechanism = "PLAIN";
    sasl_username = "sshine";
    sasl_password = "@LIBERA_SASL_PASSWORD@"; # <- can commit this!
  };
};

I’ll set the password manually:

$ sops secrets/dao/irssi.yaml
irssi_libera_sasl_password: p4ssw0rd

Because .sops.yaml already has a creation_rule for secrets/dao/*.yaml listing both the admin (&sshine) and the host (&dao) as recipients, there’s nothing new to configure.

Disable irssi autosave

I had to disable irssi’s autosave setting.

Otherwise the irssi config will occasionally write to the constructed file:

settings = {
  core = {
    settings_autosave = "no";
  };
};

Now the config is purely declarative in both directions: Nix owns it, irssi only reads it. Every rebuild re-renders it from the master; nothing round-trips back.

$ ls -l ~/.irssi/config
lrwxrwxrwx - sshine users  5 Jul 20:20 /home/sshine/.irssi/config -> /run/secrets/rendered/irssi-config

$ ls -l /run/secrets/rendered/irssi-config
.rw------- 7.6k sshine users  5 Jul 20:20 /run/secrets/rendered/irssi-config