{ config, lib, pkgs, namespace, ... }: with lib; let cfg = config.${namespace}.services.tabby-web; # Build environment variables from configuration environmentVars = { DATABASE_URL = cfg.databaseUrl; APP_DIST_STORAGE = cfg.appDistStorage; PORT = toString cfg.port; } // optionalAttrs (cfg.socialAuth.github.key != null) { SOCIAL_AUTH_GITHUB_KEY = cfg.socialAuth.github.key; } // optionalAttrs (cfg.socialAuth.github.secret != null) { SOCIAL_AUTH_GITHUB_SECRET = cfg.socialAuth.github.secret; } // optionalAttrs (cfg.socialAuth.gitlab.key != null) { SOCIAL_AUTH_GITLAB_KEY = cfg.socialAuth.gitlab.key; } // optionalAttrs (cfg.socialAuth.gitlab.secret != null) { SOCIAL_AUTH_GITLAB_SECRET = cfg.socialAuth.gitlab.secret; } // optionalAttrs (cfg.socialAuth.microsoftGraph.key != null) { SOCIAL_AUTH_MICROSOFT_GRAPH_KEY = cfg.socialAuth.microsoftGraph.key; } // optionalAttrs (cfg.socialAuth.microsoftGraph.secret != null) { SOCIAL_AUTH_MICROSOFT_GRAPH_SECRET = cfg.socialAuth.microsoftGraph.secret; } // optionalAttrs (cfg.socialAuth.googleOauth2.key != null) { SOCIAL_AUTH_GOOGLE_OAUTH2_KEY = cfg.socialAuth.googleOauth2.key; } // optionalAttrs (cfg.socialAuth.googleOauth2.secret != null) { SOCIAL_AUTH_GOOGLE_OAUTH2_SECRET = cfg.socialAuth.googleOauth2.secret; } // cfg.extraEnvironment; in { imports = [ ./options.nix ]; config = mkIf cfg.enable { # Create user and group users.users.${cfg.user} = { isSystemUser = true; group = cfg.group; home = cfg.dataDir; createHome = true; description = "Tabby Web service user"; }; users.groups.${cfg.group} = { }; # Ensure data directory exists with correct permissions systemd.tmpfiles.rules = [ "d '${cfg.dataDir}' 0750 ${cfg.user} ${cfg.group} - -" "d '${cfg.dataDir}/dist' 0750 ${cfg.user} ${cfg.group} - -" ]; # Create the systemd service systemd.services.tabby-web = { description = "Tabby Web Terminal Application Server"; wantedBy = [ "multi-user.target" ]; after = [ "network.target" ] ++ optional (hasPrefix "postgresql://" cfg.databaseUrl) "postgresql.service"; environment = environmentVars; serviceConfig = { Type = "exec"; User = cfg.user; Group = cfg.group; WorkingDirectory = cfg.dataDir; # Use the tabby-web package from our custom packages ExecStart = "${pkgs.${namespace}.tabby-web}/bin/tabby-web --workers ${toString cfg.workers} --timeout ${toString cfg.timeout}"; # Run database migrations before starting the service ExecStartPre = "${pkgs.${namespace}.tabby-web}/bin/tabby-web-manage migrate"; # Security settings NoNewPrivileges = true; ProtectSystem = "strict"; ProtectHome = true; ReadWritePaths = [ cfg.dataDir ]; PrivateTmp = true; ProtectKernelTunables = true; ProtectKernelModules = true; ProtectControlGroups = true; RestrictSUIDSGID = true; RestrictRealtime = true; RestrictNamespaces = true; LockPersonality = true; MemoryDenyWriteExecute = true; # Restart policy Restart = "always"; RestartSec = "10s"; # Resource limits LimitNOFILE = "65536"; }; # Ensure the service starts after database if using PostgreSQL requisite = mkIf (hasPrefix "postgresql://" cfg.databaseUrl) [ "postgresql.service" ]; }; # Open firewall if requested networking.firewall = mkIf cfg.openFirewall { allowedTCPPorts = [ cfg.port ]; }; # Add the tabby-web package to system packages environment.systemPackages = [ pkgs.${namespace}.tabby-web ]; }; }