Compare commits

..

20 Commits

Author SHA1 Message Date
Vegard Bieker Matthey
3c1e9e6229 stub: worblehat settings 2026-03-18 13:39:47 +01:00
Vegard Bieker Matthey
4e5e649361 start worblehat correctly 2026-03-18 13:14:54 +01:00
Vegard Bieker Matthey
e2575f9b71 use variable for screen session 2026-03-18 12:56:01 +01:00
Vegard Bieker Matthey
af838e1ac4 Revert "maybe fix starting worblehat"
This reverts commit 5ee5544749.
2026-03-18 12:44:09 +01:00
Vegard Bieker Matthey
5ee5544749 maybe fix starting worblehat 2026-03-18 12:37:45 +01:00
Vegard Bieker Matthey
0ecf6b0449 test stable worblehat 2026-03-18 12:19:32 +01:00
Vegard Bieker Matthey
ef1fba7009 use unstable python packages 2026-03-18 11:55:06 +01:00
Vegard Bieker Matthey
9e3bc0e9fe test overriding python packages 2026-03-18 11:37:55 +01:00
Vegard Bieker Matthey
28a4824964 try making overlay for python deps 2026-03-17 21:43:24 +01:00
Vegard Bieker Matthey
26970ad53f fix daemon things for worblehat 2026-03-17 17:09:50 +01:00
Vegard Bieker Matthey
94c51d86c2 fix syntax error 2026-03-17 16:53:11 +01:00
Vegard Bieker Matthey
287e7b5e03 add worblehat daemons 2026-03-17 16:51:59 +01:00
Vegard Bieker Matthey
36d9aa426d add database password for worblehat 2026-03-17 16:30:38 +01:00
Vegard Bieker Matthey
2c85161654 fix type stuff for cli args 2026-03-17 15:55:21 +01:00
Vegard Bieker Matthey
98e3e316c4 fix module in overlays 2026-03-17 14:49:40 +01:00
Vegard Bieker Matthey
d5f3934888 add drumknotty.nix module 2026-03-17 14:41:53 +01:00
Vegard Bieker Matthey
53ac8f9a3d actually use drumknotty 2026-03-17 14:38:53 +01:00
Vegard Bieker Matthey
61bd833806 fix typo 2026-03-17 14:26:21 +01:00
Vegard Bieker Matthey
87d394e4c1 drumknotty WIP 2026-03-17 14:22:48 +01:00
Vegard Bieker Matthey
62765d28f1 stub: wasted init 2026-03-07 20:35:06 +01:00
8 changed files with 366 additions and 565 deletions

20
flake.lock generated
View File

@@ -7,11 +7,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1771267058, "lastModified": 1770133120,
"narHash": "sha256-EEL4SmD1b3BPJPsSJJ4wDTXWMumJqbR+BLzhJJG0skE=", "narHash": "sha256-RuAWONXb+U3omSsuIPCrPcgj0XYqv+2djG0cnPGEyKg=",
"ref": "main", "ref": "main",
"rev": "e3962d02c78b9c7b4d18148d931a9a4bf22e7902", "rev": "3123b8b474319bc75ee780e0357dcdea69dc85e6",
"revCount": 254, "revCount": 244,
"type": "git", "type": "git",
"url": "https://git.pvv.ntnu.no/Projects/dibbler.git" "url": "https://git.pvv.ntnu.no/Projects/dibbler.git"
}, },
@@ -492,16 +492,16 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1773932847, "lastModified": 1773832647,
"narHash": "sha256-IklIAdlonrmO8/lkDxNIVz9+ORL4pcVotMTxeyvxzoc=", "narHash": "sha256-VZVQDwqDmMGJcJ8WnD6Ug+DTV0b6S2DKHkdj3QvFO+w=",
"ref": "main", "ref": "stable_deps",
"rev": "0871a319f51d3cb0d1abb5b11edb768b39906d3f", "rev": "9bdaaf6c5180309b7810bb72f9e717a7c3b2a504",
"revCount": 104, "revCount": 102,
"type": "git", "type": "git",
"url": "https://git.pvv.ntnu.no/Projects/worblehat.git" "url": "https://git.pvv.ntnu.no/Projects/worblehat.git"
}, },
"original": { "original": {
"ref": "main", "ref": "stable_deps",
"type": "git", "type": "git",
"url": "https://git.pvv.ntnu.no/Projects/worblehat.git" "url": "https://git.pvv.ntnu.no/Projects/worblehat.git"
} }

View File

@@ -23,7 +23,7 @@
dibbler.url = "git+https://git.pvv.ntnu.no/Projects/dibbler.git?ref=main"; dibbler.url = "git+https://git.pvv.ntnu.no/Projects/dibbler.git?ref=main";
dibbler.inputs.nixpkgs.follows = "nixpkgs"; dibbler.inputs.nixpkgs.follows = "nixpkgs";
worblehat.url = "git+https://git.pvv.ntnu.no/Projects/worblehat.git?ref=main"; worblehat.url = "git+https://git.pvv.ntnu.no/Projects/worblehat.git?ref=stable_deps";
worblehat.inputs.nixpkgs.follows = "nixpkgs"; worblehat.inputs.nixpkgs.follows = "nixpkgs";
matrix-next.url = "github:dali99/nixos-matrix-modules/v0.8.0"; matrix-next.url = "github:dali99/nixos-matrix-modules/v0.8.0";
@@ -193,6 +193,8 @@
modules = [ modules = [
self.nixosModules.drumknotty self.nixosModules.drumknotty
inputs.disko.nixosModules.disko inputs.disko.nixosModules.disko
inputs.dibbler.nixosModules.default
inputs.worblehat.nixosModules.default
]; ];
overlays = overlays =
[ [
@@ -294,7 +296,7 @@
rsync-pull-targets = ./modules/rsync-pull-targets.nix; rsync-pull-targets = ./modules/rsync-pull-targets.nix;
snakeoil-certs = ./modules/snakeoil-certs.nix; snakeoil-certs = ./modules/snakeoil-certs.nix;
snappymail = ./modules/snappymail.nix; snappymail = ./modules/snappymail.nix;
drumknotty = ./modules/drumknotty; drumknotty = ./modules/drumknotty.nix;
}; };
devShells = forAllSystems (system: { devShells = forAllSystems (system: {

View File

@@ -40,40 +40,30 @@
services.drumknotty = { services.drumknotty = {
enable = true; enable = true;
kioskMode = true; kioskMode = true;
limitScreenWidth = 80;
limitScreenHeight = 42;
screen = { dibblerSettings = {
limitWidth = 80; general.quit_allowed = false;
limitHeight = 42; database = {
}; type = "postgresql";
postgresql = {
dibbler = { username = "pvv_vv";
enable = true; dbname = "pvv_vv";
settings = { host = "postgres.pvv.ntnu.no";
general.quit_allowed = false; password_file = config.sops.secrets."dibbler/postgresql/password".path;
database = {
type = "postgresql";
postgresql = {
username = "pvv_vv";
dbname = "pvv_vv";
host = "postgres.pvv.ntnu.no";
password_file = config.sops.secrets."dibbler/postgresql/password".path;
};
}; };
}; };
}; };
worblehatSettings = {
worblehat = { general.quit_allowed = false;
enable = true; database = {
settings = { type = "postgresql";
general.quit_allowed = false; postgresql = {
database = { username = "pvv_vv";
type = "postgresql"; dbname = "pvv_vv";
postgresql = { host = "postgres.pvv.ntnu.no";
username = "worblehat"; password_file = config.sops.secrets."worblehat/postgresql/password".path;
dbname = "worblehat";
host = "postgres.pvv.ntnu.no";
password = config.sops.secrets."worblehat/postgresql/password".path;
};
}; };
}; };
}; };

329
modules/drumknotty.nix Normal file
View File

@@ -0,0 +1,329 @@
{
config,
pkgs,
lib,
...
}:
let
cfg = config.services.drumknotty;
format = pkgs.formats.toml { };
in
{
options.services.drumknotty = {
enable = lib.mkEnableOption "DrumknoTTY";
dibblerPackage = lib.mkPackageOption pkgs "dibbler" { };
worblehatPackage = lib.mkPackageOption pkgs "worblehat" { };
screenPackage = lib.mkPackageOption pkgs "screen" { };
screenSessionName = lib.mkOption {
type = lib.types.str;
default = "drumknotty";
example = "myscreensessionname";
description = ''
Sets the screen session name.
'';
};
createLocalDatabase = lib.mkEnableOption "" // {
description = ''
Whether to set up a local postgres database automatically.
::: {.note}
You must set up postgres manually before enabling this option.
:::
'';
};
kioskMode = lib.mkEnableOption "" // {
description = ''
Whether to let dibbler take over the entire machine.
This will restrict the machine to a single TTY and make the program unquittable.
You can still get access to PTYs via SSH and similar, if enabled.
'';
};
limitScreenHeight = lib.mkOption {
type = with lib.types; nullOr ints.unsigned;
default = null;
example = 42;
description = ''
If set, limits the height of the screen dibbler uses to the given number of lines.
'';
};
limitScreenWidth = lib.mkOption {
type = with lib.types; nullOr ints.unsigned;
default = null;
example = 80;
description = ''
If set, limits the width of the screen dibbler uses to the given number of columns.
'';
};
dibblerSettings = lib.mkOption {
description = "Configuration for dibbler";
default = { };
type = lib.types.submodule {
freeformType = format.type;
};
};
worblehatSettings = lib.mkOption {
description = "Configuration for worblehat";
default = { };
type = lib.types.submodule {
freeformType = format.type;
};
};
deadline-daemon = {
enable = lib.mkEnableOption "" // {
description = ''
Whether to enable the worblehat deadline-daemon service,
which periodically checks for upcoming deadlines and notifies users.
Note that this service is independent of the main worblehat service,
and must be enabled separately.
'';
};
onCalendar = lib.mkOption {
type = lib.types.str;
description = ''
How often to trigger rendering the map,
in the format of a systemd timer onCalendar configuration.
See {manpage}`systemd.timer(5)`.
'';
default = "*-*-* 10:15:00";
};
};
};
config = lib.mkIf cfg.enable (
lib.mkMerge [
{
environment.systemPackages = [
cfg.dibblerPackage
cfg.worblehatPackage
];
environment.etc."dibbler/dibbler.toml".source = format.generate "dibbler.toml" cfg.dibblerSettings;
environment.etc."worblehat/config.toml".source =
format.generate "worblehat-config.toml" cfg.worblehatSettings;
users = {
users.drumknotty = {
group = "drumknotty";
isNormalUser = true;
};
groups.drumknotty = { };
};
services.worblehat.settings = {
logging = {
debug = true;
dbug_sql = false;
};
database = {
type = "sqlite";
sqlite.path = "./worblehat.sqlite";
};
};
services.dibbler.settings.database = lib.mkIf cfg.createLocalDatabase {
type = "postgresql";
postgresql.host = "/run/postgresql";
};
services.postgresql = lib.mkIf cfg.createLocalDatabase {
ensureDatabases = [
"dibbler"
"worblehat"
];
ensureUsers = [
{
name = "drumknotty";
ensureDBOwnership = true;
ensureClauses.login = true;
}
];
};
systemd.services.dibbler-setup-database = lib.mkIf cfg.createLocalDatabase {
description = "Dibbler database setup";
wantedBy = [ "default.target" ];
after = [ "postgresql.service" ];
unitConfig = {
ConditionPathExists = "!/var/lib/dibbler/.db-setup-done";
};
serviceConfig = {
Type = "oneshot";
ExecStart = "${lib.getExe cfg.dibblerPackage} --config /etc/dibbler/dibbler.toml create-db";
ExecStartPost = "${lib.getExe' pkgs.coreutils "touch"} /var/lib/dibbler/.db-setup-done";
StateDirectory = "dibbler";
User = "drumknotty";
Group = "drumknotty";
};
};
systemd.services.worblehat-setup-database = lib.mkIf cfg.createLocalDatabase {
description = "Worblehat database setup";
wantedBy = [ "default.target" ];
after = [ "postgresql.service" ];
unitConfig = {
ConditionPathExists = "!/var/lib/worblehat/.db-setup-done";
};
serviceConfig = {
Type = "oneshot";
ExecStart = "${lib.getExe cfg.worblehatPackage} --config /etc/worblehat/config.toml create-db";
ExecStartPost = "${lib.getExe' pkgs.coreutils "touch"} /var/lib/worblehat/.db-setup-done";
StateDirectory = "worblehat";
User = "drumknotty";
Group = "drumknotty";
};
};
}
(lib.mkIf cfg.kioskMode {
boot.kernelParams = [
"console=tty1"
];
users.users.drumknotty = {
extraGroups = [ "lp" ];
shell =
(pkgs.writeShellScriptBin "login-shell" "${lib.getExe' cfg.screenPackage "screen"} -x ${cfg.screenSessionName}")
// {
shellPath = "/bin/login-shell";
};
};
services.drumknotty.dibblerSettings.general = {
quit_allowed = false;
stop_allowed = false;
};
services.drumknotty.worblehatSettings.general = {
quit_allowed = false;
stop_allowed = false;
};
systemd.services.drumknotty-screen-session = {
description = "Drumknotty Screen Session";
wantedBy = [
"default.target"
];
after =
if cfg.createLocalDatabase then
[
"postgresql.service"
"dibbler-setup-database.service"
"worblehat-setup-database.service"
]
else
[
"network.target"
];
serviceConfig =
let
dibblerArgs = lib.cli.toCommandLineShellGNU { } {
config = "/etc/dibbler/dibbler.toml";
};
worblehatArgs = lib.cli.toCommandLineShellGNU { } {
config = "/etc/worblehat/config.toml";
};
in
{
Type = "forking";
RemainAfterExit = false;
Restart = "always";
RestartSec = "5s";
SuccessExitStatus = 1;
User = "drumknotty";
Group = "drumknotty";
ExecStartPre = "-${lib.getExe' cfg.screenPackage "screen"} -X -S ${cfg.screenSessionName} kill";
ExecStart =
let
screenArgs = lib.escapeShellArgs [
# -dm creates the screen in detached mode without accessing it
"-dm"
# Session name
"-S"
"${cfg.screenSessionName}"
# Window name
"-t"
"dibbler"
# Set optimal output mode instead of VT100 emulation
"-O"
# Enable login mode, updates utmp entries
"-l"
];
in
"${lib.getExe' cfg.screenPackage "screen"} ${screenArgs} ${lib.getExe cfg.dibblerPackage} ${dibblerArgs} loop";
ExecStartPost = [
"${lib.getExe' cfg.screenPackage "screen"} -S ${cfg.screenSessionName} -X screen -t worblehat ${lib.getExe cfg.worblehatPackage} ${worblehatArgs} cli"
]
++ lib.optionals (cfg.limitScreenWidth != null) [
"${lib.getExe' cfg.screenPackage "screen"} -X -S ${cfg.screenSessionName} width ${toString cfg.limitScreenWidth}"
]
++ lib.optionals (cfg.limitScreenHeight != null) [
"${lib.getExe' cfg.screenPackage "screen"} -X -S ${cfg.screenSessionName} height ${toString cfg.limitScreenHeight}"
];
};
};
services.getty.autologinUser = "drumknotty";
})
(lib.mkIf cfg.deadline-daemon.enable {
systemd.timers.worblehat-deadline-daemon = {
description = "Worblehat Deadline Daemon";
wantedBy = [ "timers.target" ];
timerConfig = {
OnCalendar = cfg.deadline-daemon.onCalendar;
Persistent = true;
};
};
systemd.services.worblehat-deadline-daemon = {
description = "Worblehat Deadline Daemon";
wantedBy = [ "multi-user.target" ];
after = [ "network.target" ];
serviceConfig = {
Type = "oneshot";
CPUSchedulingPolicy = "idle";
IOSchedulingClass = "idle";
ExecStart =
let
worblehatArgs = lib.cli.toCommandLineShellGNU { } {
config = "/etc/worblehat/config.toml";
};
in
"${lib.getExe cfg.package} ${worblehatArgs} deadline-daemon";
User = "worblehat";
Group = "worblehat";
};
};
})
]
);
}

View File

@@ -1,198 +0,0 @@
{
config,
pkgs,
lib,
...
}:
let
cfg = config.services.drumknotty;
in
{
imports = [
./dibbler.nix
./worblehat.nix
];
options.services.drumknotty = {
enable = lib.mkEnableOption "DrumknoTTY";
kioskMode = lib.mkEnableOption "" // {
description = ''
Whether to let dibbler take over the entire machine.
This will restrict the machine to a single TTY and make the program unquittable.
You can still get access to PTYs via SSH and similar, if enabled.
'';
};
screen = {
package = lib.mkPackageOption pkgs "screen" { };
sessionName = lib.mkOption {
type = lib.types.str;
default = "drumknotty";
example = "myscreensessionname";
description = ''
Sets the screen session name.
'';
};
limitHeight = lib.mkOption {
type = with lib.types; nullOr ints.unsigned;
default = null;
example = 42;
description = ''
If set, limits the height of the screen dibbler uses to the given number of lines.
'';
};
limitWidth = lib.mkOption {
type = with lib.types; nullOr ints.unsigned;
default = null;
example = 80;
description = ''
If set, limits the width of the screen dibbler uses to the given number of columns.
'';
};
};
};
config = lib.mkIf cfg.enable {
assertions = [
{
assertion = cfg.enable -> lib.any (b: b) [
cfg.dibbler.enable
cfg.worblehat.enable
];
message = "DrumknoTTY must have at least one service enabled";
}
];
users = {
users.drumknotty = {
group = "drumknotty";
extraGroups = [ "lp" ];
isNormalUser = true;
# TODO: make this display the error log or error message in case that
# the screen session service is bootlooping or otherwise off.
shell =
lib.mkIf cfg.kioskMode
(pkgs.writeShellScriptBin "login-shell"
"${lib.getExe' cfg.screen.package "screen"} -x ${cfg.screen.sessionName} -p dibbler"
// {
shellPath = "/bin/login-shell";
});
};
groups.drumknotty = { };
};
boot.kernelParams = lib.mkIf cfg.kioskMode [
"console=tty1"
];
services.getty.autologinUser = lib.mkIf cfg.kioskMode "drumknotty";
systemd.services.drumknotty-screen-session = lib.mkIf cfg.kioskMode {
description = "Drumknotty Screen Session";
wantedBy = [
"default.target"
];
after =
# TODO: this could be refined
if (cfg.dibbler.createLocalDatabase || cfg.worblehat.createLocalDatabase) then
[
"postgresql.service"
"dibbler-setup-database.service"
"worblehat-setup-database.service"
]
else
[
"network.target"
];
serviceConfig = {
Type = "forking";
RemainAfterExit = false;
Restart = "always";
RestartSec = "5s";
SuccessExitStatus = 1;
User = "drumknotty";
Group = "drumknotty";
ExecStartPre =
let
screenArgs = lib.escapeShellArgs [
# Send the specified command to a running screen session
"-X"
# Session name
"-S"
"${cfg.screen.sessionName}"
"kill"
];
in
"-${lib.getExe' cfg.screen.package "screen"} ${screenArgs}";
ExecStart =
let
screenrc = let
convertToFile = lines: lib.pipe lines [
lib.concatLists
(lib.concatStringsSep "\n")
(pkgs.writeText "drumknotty-screenrc")
];
in convertToFile [
(lib.optionals (cfg.screen.limitWidth != null) [
"screen width ${toString cfg.screen.limitWidth}"
])
(lib.optionals (cfg.screen.limitHeight != null) [
"screen height ${toString cfg.screen.limitHeight}"
])
(let
dibblerArgs = lib.cli.toCommandLineShellGNU { } {
config = "/etc/dibbler/dibbler.toml";
};
in lib.optionals cfg.dibbler.enable [
"screen -t worblehat ${lib.getExe cfg.dibbler.package} ${dibblerArgs} loop"
])
(let
worblehatArgs = lib.cli.toCommandLineShellGNU { } {
config = "/etc/worblehat/config.toml";
};
in lib.optionals cfg.worblehat.enable [
"screen -t worblehat ${lib.getExe cfg.worblehat.package} ${worblehatArgs} cli"
])
[ "select 0" ]
];
screenArgs = lib.escapeShellArgs [
# -dm creates the screen in detached mode without accessing it
"-dm"
# Session name
"-S"
"${cfg.screen.sessionName}"
# Set optimal output mode instead of VT100 emulation
"-O"
# Enable login mode, updates utmp entries
"-l"
# Config file path
"-c"
"${screenrc}"
];
in
"${lib.getExe' cfg.screen.package "screen"} ${screenArgs}";
};
};
};
}

View File

@@ -1,113 +0,0 @@
{
config,
pkgs,
lib,
...
}:
let
mainCfg = config.services.drumknotty;
cfg = config.services.drumknotty.dibbler;
format = pkgs.formats.toml { };
in
{
options.services.drumknotty.dibbler = {
enable = lib.mkEnableOption "";
package = lib.mkPackageOption pkgs "dibbler" { };
settings = lib.mkOption {
description = "Configuration for dibbler";
default = { };
type = lib.types.submodule {
freeformType = format.type;
};
};
createLocalDatabase = lib.mkEnableOption "" // {
description = ''
Whether to set up a local postgres database automatically.
::: {.note}
You must set up postgres manually before enabling this option.
:::
'';
};
};
config = lib.mkIf (mainCfg.enable && cfg.enable) {
assertions = [
{
assertion = cfg.createLocalDatabase -> config.services.postgresql.enable;
message = "PostgreSQL must be enabled for dibbler to create a local database";
}
];
environment.systemPackages = [ cfg.package ];
environment.etc."dibbler/dibbler.toml".source = format.generate "dibbler.toml" cfg.settings;
services.drumknotty.dibbler.settings = {
limits = {
low_credit_warning_limit = lib.mkDefault (-100);
user_recent_transaction_limit = lib.mkDefault 100;
};
printer = {
label_type = lib.mkDefault "62";
label_rotate = lib.mkDefault false;
};
database = {
type = lib.mkIf cfg.createLocalDatabase "postgresql";
postgresql = {
username = lib.mkDefault "dibbler";
dbname = lib.mkDefault "dibbler";
host = lib.mkIf cfg.createLocalDatabase "/run/postgresql";
};
};
};
services.drumknotty.dibbler.settings.general = lib.mkIf mainCfg.kioskMode {
quit_allowed = false;
stop_allowed = false;
};
services.postgresql = lib.mkIf cfg.createLocalDatabase {
authentication = ''
local ${cfg.settings.database.postgresql.dbname} ${cfg.settings.database.postgresql.username} peer map=${cfg.settings.database.postgresql.username}
'';
identMap = ''
${cfg.settings.database.postgresql.username} drumknotty ${cfg.settings.database.postgresql.username}
'';
ensureDatabases = [ cfg.settings.database.postgresql.dbname ];
ensureUsers = [{
name = cfg.settings.database.postgresql.username;
ensureDBOwnership = true;
ensureClauses.login = true;
}];
};
systemd.services.dibbler-setup-database = lib.mkIf cfg.createLocalDatabase {
description = "Dibbler database setup";
wantedBy = [ "default.target" ];
requiredBy = [ "drumknotty-screen-session.service" ];
before = [ "drumknotty-screen-session.service" ];
after = [ "postgresql.service" ];
unitConfig = {
ConditionPathExists = "!/var/lib/dibbler/.db-setup-done";
};
serviceConfig = {
Type = "oneshot";
ExecStart = "${lib.getExe cfg.package} --config /etc/dibbler/dibbler.toml create-db";
ExecStartPost = "${lib.getExe' pkgs.coreutils "touch"} /var/lib/dibbler/.db-setup-done";
StateDirectory = "dibbler";
User = "drumknotty";
Group = "drumknotty";
};
};
};
}

View File

@@ -1,209 +0,0 @@
{
config,
pkgs,
lib,
...
}:
let
mainCfg = config.services.drumknotty;
cfg = config.services.drumknotty.worblehat;
format = pkgs.formats.toml { };
in
{
options.services.drumknotty.worblehat = {
enable = lib.mkEnableOption "";
package = lib.mkPackageOption pkgs "worblehat" { };
settings = lib.mkOption {
description = "Configuration for worblehat";
default = { };
type = lib.types.submodule {
freeformType = format.type;
};
};
createLocalDatabase = lib.mkEnableOption "" // {
description = ''
Whether to set up a local postgres database automatically.
::: {.note}
You must set up postgres manually before enabling this option.
:::
'';
};
deadline-daemon = {
enable = lib.mkEnableOption "" // {
description = ''
Whether to enable the worblehat deadline-daemon service,
which periodically checks for upcoming deadlines and notifies users.
Note that this service is independent of the main worblehat service,
and must be enabled separately.
'';
};
onCalendar = lib.mkOption {
type = lib.types.str;
description = ''
How often to trigger rendering the map,
in the format of a systemd timer onCalendar configuration.
See {manpage}`systemd.timer(5)`.
'';
default = "*-*-* 10:15:00";
};
};
};
config = lib.mkMerge [
{
assertions = [
{
assertion = cfg.createLocalDatabase -> config.services.postgresql.enable;
message = "PostgreSQL must be enabled for worblehat to create a local database";
}
];
# TODO: Retrieve defaults from the example config file in the project code.
services.drumknotty.worblehat.settings = {
logging = {
debug = lib.mkDefault true;
debug_sql = lib.mkDefault false;
};
database = {
type = lib.mkDefault "sqlite";
sqlite.path = lib.mkDefault "./worblehat.sqlite";
postgresql = {
host = lib.mkDefault "localhost";
port = lib.mkDefault 5432;
username = lib.mkDefault "worblehat";
password = lib.mkDefault "/var/lib/worblehat/db-password";
database = lib.mkDefault "worblehat";
};
};
flask = {
TESTING = lib.mkDefault true;
DEBUG = lib.mkDefault true;
FLASK_ENV = lib.mkDefault "development";
SECRET_KEY = lib.mkDefault "change-me";
};
smtp = {
enabled = lib.mkDefault false;
host = lib.mkDefault "smtp.pvv.ntnu.no";
port = lib.mkDefault 587;
username = lib.mkDefault "worblehat";
password = lib.mkDefault "/var/lib/worblehat/smtp-password";
from = lib.mkDefault "worblehat@pvv.ntnu.no";
subject_prefix = lib.mkDefault "[Worblehat]";
};
deadline_daemon = {
enabled = lib.mkDefault true;
dryrun = lib.mkDefault false;
warn_days_before_borrowing_deadline = lib.mkDefault [
5
1
];
days_before_queue_position_expires = lib.mkDefault 14;
warn_days_before_expiring_queue_position_deadline = lib.mkDefault [
3
1
];
};
};
}
(lib.mkIf ((mainCfg.enable && cfg.enable) || cfg.deadline-daemon.enable) {
environment.systemPackages = [ cfg.package ];
environment.etc."worblehat/config.toml".source = format.generate "worblehat-config.toml" cfg.settings;
})
(lib.mkIf (mainCfg.enable && cfg.enable) {
services.drumknotty.worblehat.settings.general = lib.mkIf mainCfg.kioskMode {
quit_allowed = false;
stop_allowed = false;
};
services.drumknotty.worblehat.settings.database = lib.mkIf cfg.createLocalDatabase {
type = "postgresql";
postgresql.host = "/run/postgresql";
};
services.postgresql = lib.mkIf cfg.createLocalDatabase {
authentication = ''
local ${cfg.settings.database.postgresql.database} ${cfg.settings.database.postgresql.username} peer map=${cfg.settings.database.postgresql.username}
'';
identMap = ''
${cfg.settings.database.postgresql.username} drumknotty ${cfg.settings.database.postgresql.username}
'';
ensureDatabases = [ cfg.settings.database.postgresql.database ];
ensureUsers = [{
name = cfg.settings.database.postgresql.username;
ensureDBOwnership = true;
ensureClauses.login = true;
}];
};
systemd.services.worblehat-setup-database = lib.mkIf cfg.createLocalDatabase {
description = "Worblehat database setup";
wantedBy = [ "default.target" ];
requiredBy = [ "drumknotty-screen-session.service" ];
before = [ "drumknotty-screen-session.service" ];
after = [ "postgresql.service" ];
unitConfig = {
ConditionPathExists = "!/var/lib/worblehat/.db-setup-done";
};
serviceConfig = {
Type = "oneshot";
ExecStart = "${lib.getExe cfg.package} --config /etc/worblehat/config.toml create-db";
ExecStartPost = "${lib.getExe' pkgs.coreutils "touch"} /var/lib/worblehat/.db-setup-done";
StateDirectory = "worblehat";
User = "drumknotty";
Group = "drumknotty";
};
};
})
(lib.mkIf cfg.deadline-daemon.enable {
systemd.timers.worblehat-deadline-daemon = lib.mkIf cfg.deadline-daemon.enable {
description = "Worblehat Deadline Daemon";
wantedBy = [ "timers.target" ];
timerConfig = {
OnCalendar = cfg.deadline-daemon.onCalendar;
Persistent = true;
};
};
systemd.services.worblehat-deadline-daemon = lib.mkIf cfg.deadline-daemon.enable {
description = "Worblehat Deadline Daemon";
wantedBy = [ "multi-user.target" ];
after = [ "network.target" ];
serviceConfig = {
Type = "oneshot";
CPUSchedulingPolicy = "idle";
IOSchedulingClass = "idle";
ExecStart =
let
worblehatArgs = lib.cli.toCommandLineShellGNU { } {
config = "/etc/worblehat/config.toml";
};
in
"${lib.getExe cfg.package} ${worblehatArgs} deadline-daemon";
User = "drumknotty";
Group = "drumknotty";
};
};
})
];
}

View File

@@ -3,7 +3,7 @@ dibbler:
password: ENC[AES256_GCM,data:3X9A3jOpFVRuBg0gRiCEsZVKfLI=,iv:XC7LBNUhALk9IEhItV8fO5p/m7VKL0REBY1W2IZt7G4=,tag:l18R7EhbOlucZHFQiEvpHw==,type:str] password: ENC[AES256_GCM,data:3X9A3jOpFVRuBg0gRiCEsZVKfLI=,iv:XC7LBNUhALk9IEhItV8fO5p/m7VKL0REBY1W2IZt7G4=,tag:l18R7EhbOlucZHFQiEvpHw==,type:str]
worblehat: worblehat:
postgresql: postgresql:
password: ENC[AES256_GCM,data:WpJR6MumY+7WUYdVVgAqv1af+NmqecTMO9aP5lidSpE=,iv:7aoN8mjXckd81LxasMSG3R2vqj0SvzSl7wrEQ1LwToo=,tag:zeeNcEpkYnqyd8be0ZS+kQ==,type:str] password: ENC[AES256_GCM,data:5xEiz+Op0dpxO/x75iEZs0VVqhbr+85BusXGt7xyQZk=,iv:MvnRPx+segcEzUzQsJXGXDaQlKpf5AN9pSfXP34Cz6k=,tag:AG8JUl3+8ZJz+gbLti78Vw==,type:str]
sops: sops:
age: age:
- recipient: age1hzkvnktkr8t5gvtq0ccw69e44z5z6wf00n3xhk3hj24emf07je5s6q2evr - recipient: age1hzkvnktkr8t5gvtq0ccw69e44z5z6wf00n3xhk3hj24emf07je5s6q2evr
@@ -69,8 +69,8 @@ sops:
MmxPMWNPYzJiOFRqY2VYczhvRm5IR3cKpUVV+zsMolsHI2YK9YqC6ecNT6QXv0TV MmxPMWNPYzJiOFRqY2VYczhvRm5IR3cKpUVV+zsMolsHI2YK9YqC6ecNT6QXv0TV
d1SpXRAexZBeWCCHBjSdvQBl8AT4EwrAIP2M2o++6i5DaGoGiEIWZQ== d1SpXRAexZBeWCCHBjSdvQBl8AT4EwrAIP2M2o++6i5DaGoGiEIWZQ==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----
lastmodified: "2026-03-18T14:56:22Z" lastmodified: "2026-03-17T15:30:20Z"
mac: ENC[AES256_GCM,data:nBKtFmFKx/Mt9TIFnKuuznsPAXCQpc3+WIspNu5TN9TpIqw75nzYXpxIb2hxRfRu0nbjHXpBy4bkzeMi41BGkvkvV57CZyq11J5i/iIKwuvllaB1IWrdDT2u+6RH3jIspp3KoyxFWdRqcGfNma9dSmtI+1Dd5z7XaxVaoVK2QMI=,iv:6joviyJ2cXmGh/9HH7VEcoK3+4GK5I6i2N/1d65PAN0=,tag:0BFVPWL3BByJH8HbrBTKOw==,type:str] mac: ENC[AES256_GCM,data:byJbaGBKiW8P6Z9EAaXFIAurNPZ1Yu3H4wISL3YESGPVy5GSbMlfSTLElw8BcQNgOuLq8+BVZcRZn+UaLx7vNKfmpbOmthmrMqilcW/vmgsQ5+of+r+O9bOqqXAmlBLvB82trkLUWsPfO4hHJBfjFweLZFqzszW3t/zeLMocM18=,iv:EqiPEg0xgRPsuJfjUKg/ba10m1rqopO4gtPwKABuNk4=,tag:5l4sJxsV69ha4hf+9iBmMw==,type:str]
pgp: pgp:
- created_at: "2026-02-10T20:01:32Z" - created_at: "2026-02-10T20:01:32Z"
enc: |- enc: |-