Great news everyone. @fricklerhandwerk and I as well as @kiara and @lassulus hacked on this during the Zürich 25.05 ZHF hackathon and we made great progress. I invite you to read this section of the report Zürich 25.05 ZHF hackathon report. We created a repo with what we will propose to upstream. GitHub - fricklerhandwerk/module-interfaces
Before upstreaming though, I’ll be now working on migrating SelfHostBlocks (my project from which this originates from) to this new pattern and see if there are any tweaks needed to be made. For example, how does documentation look like and how well does this integrate with the generic NixOS tests for contracts? I already made some tests during the hackathon and things looks good.
The TL; DR: we have worked out a way for the module type system to make the following possible and type check:
{ config, ... }:
{
services.myservice.password.provider = sops.secrets."myservice-password";
}
That’s right, it’s a one way connection from the end user’s perspective but there is still wiring in both directions happening behind the scenes.
As for such a contract, defining it is done this way:
{ lib, ... }:
let
inherit (lib) mkOption types;
in
{
config.interfaces.secrets = {
description = "generate a secret that is passed out of band to the nix store";
input = input: {
options.owner = mkOption {
type = types.str;
};
options.group = mkOption {
type = types.str;
default = "root";
};
options.mode = mkOption {
type = types.str;
default = "0400";
};
};
output = output: {
options.path = mkOption {
type = types.str;
};
};
};
}
Finally, using this contract as a consumer and provider is done this way:
{ lib, ... }:
let
inherit (lib) mkOption;
in
{
options = {
services.myservice.password = mkOption {
type = config.interfaces.password.consumer;
};
sops.secrets = mkOption {
type = config.interfaces.password.provider;
};
};
}
I’m leaving out a few details here, like how the consumer and provider actually access the input and outputs, but that’s all in the repo. I’m quite biased here, but I find this very slick. I’m really happy of this progress.