{ config, lib, pkgs, ... }: let cfg = config.services.kotsukotsu; in { options.services.kotsukotsu = { enable = lib.mkEnableOption "kotsukotsu typing app"; package = lib.mkOption { type = lib.types.nullOr lib.types.package; default = null; description = "The kotsukotsu package to run, typically inputs.kotsukotsu.packages.${pkgs.system}.default."; }; host = lib.mkOption { type = lib.types.str; default = "127.0.0.1"; description = "Listen address for the Bun server."; }; port = lib.mkOption { type = lib.types.port; default = 5174; description = "Listen port for the Bun server."; }; domain = lib.mkOption { type = lib.types.nullOr lib.types.str; default = null; example = "typing.example.com"; description = "Public domain used for WebAuthn RP ID and cookie origin."; }; dataDir = lib.mkOption { type = lib.types.str; default = "/var/lib/kotsukotsu"; description = "Directory for the persistent SQLite database."; }; user = lib.mkOption { type = lib.types.str; default = "kotsukotsu"; description = "User account for the service."; }; group = lib.mkOption { type = lib.types.str; default = "kotsukotsu"; description = "Group for the service."; }; secureCookies = lib.mkOption { type = lib.types.bool; default = true; description = "Whether to mark auth cookies as Secure."; }; }; config = lib.mkIf cfg.enable { assertions = [ { assertion = cfg.package != null; message = "services.kotsukotsu.package must be set, usually from this flake input."; } { assertion = cfg.domain != null && cfg.domain != ""; message = "services.kotsukotsu.domain must be set."; } { assertion = lib.hasPrefix "/" cfg.dataDir; message = "services.kotsukotsu.dataDir must be an absolute path."; } ]; users.groups = lib.mkIf (cfg.group == "kotsukotsu") { kotsukotsu = { }; }; users.users = lib.mkIf (cfg.user == "kotsukotsu") { kotsukotsu = { isSystemUser = true; group = cfg.group; home = toString cfg.dataDir; createHome = false; }; }; systemd.tmpfiles.rules = [ "d ${toString cfg.dataDir} 0750 ${cfg.user} ${cfg.group} -" ]; systemd.services.kotsukotsu = { description = "kotsukotsu typing app"; after = [ "network.target" ]; wantedBy = [ "multi-user.target" ]; environment = { APP_ORIGIN = "https://${cfg.domain}"; HOST = cfg.host; PORT = builtins.toString cfg.port; SESSION_COOKIE_SECURE = lib.boolToString cfg.secureCookies; SQLITE_PATH = "${cfg.dataDir}/leo-typing.db"; }; serviceConfig = { ExecStart = lib.getExe cfg.package; Group = cfg.group; Restart = "on-failure"; User = cfg.user; WorkingDirectory = cfg.dataDir; }; }; }; }