{ description = "Personal website flake."; inputs = { nixpkgs.url = "nixpkgs/nixos-unstable"; naersk.url = "github:nix-community/naersk"; naersk.inputs.nixpkgs.follows = "nixpkgs"; }; outputs = { self, nixpkgs, naersk, }: let supportedSystems = [ "x86_64-linux" "aarch64-linux" "x86_64-darwin" "aarch64-darwin" ]; forAllSystems = nixpkgs.lib.genAttrs supportedSystems; cargoConfig = builtins.fromTOML (builtins.readFile ./Cargo.toml); pkgName = cargoConfig.package.name; in { packages = forAllSystems ( system: let pkgs = (import nixpkgs) { inherit system; }; naersk' = pkgs.callPackage naersk { }; app = naersk'.buildPackage { src = ./.; postInstall = '' cp -r ./templates ./static $out ''; }; in { default = app; docker = pkgs.dockerTools.streamLayeredImage { name = "localhost/${pkgName}"; tag = "latest"; contents = [ app pkgs.bash ]; config.Cmd = [ "${app}/bin/${pkgName}" ]; }; } ); nixosModules.default = { config, pkgs, lib, ... }: let cfg = config.crashkeys.services.${pkgName}; in { options = { crashkeys.services.${pkgName} = { enable = lib.mkEnableOption "Enables the crashkeys.dev web server."; package = lib.mkOption { type = lib.types.package; default = self.packages.${pkgs.system}.default; description = "The Nix package corresponding to the web server to run."; }; public_url = lib.mkOption { type = lib.types.str; example = "https://crashkeys.dev"; description = '' The base public URL the site will be available at. Note that this value is only used to generate OpenGraph tags. ''; }; address = lib.mkOption { type = lib.types.str; default = "127.0.0.1"; example = "127.0.0.1"; description = "The IP address which the web server will bind to."; }; port = lib.mkOption { type = lib.types.port; default = 8000; example = 8000; description = "The TCP port on which the web server will be exposed."; }; }; }; config = let # The generated systemd service unit's name serviceName = "crashkeys.${pkgName}"; in lib.mkIf cfg.enable { systemd.services."${serviceName}" = { wantedBy = [ "multi-user.target" ]; serviceConfig = let pkg = cfg.package; in { Restart = "on-failure"; ExecStart = "${pkg}/bin/${pkgName}"; DynamicUser = "yes"; # The package includes the web server's static assets, # so we must run it at its output directory in the Nix store WorkingDirectory = "${pkg}"; RuntimeDirectory = "${serviceName}"; RuntimeDirectoryMode = "0755"; StateDirectory = "${serviceName}"; StateDirectoryMode = "0700"; CacheDirectory = "${serviceName}"; CacheDirectoryMode = "0750"; BindReadOnlyPaths = [ "${pkg}" ]; Environment = [ "ROCKET_ADDRESS=${cfg.address}" "ROCKET_PORT=${builtins.toString cfg.port}" "ROCKET_PUBLIC_URL=${cfg.public_url}" ]; NoNewPrivileges = true; PrivateDevices = true; ProtectClock = true; CapabilityBoundingSet = ""; # Clear all capabilities RestrictSUIDSGID = true; ProtectKernelLogs = true; ProtectControlGroups = true; ProtectKernelModules = true; PrivateMounts = true; RestrictNamespaces = true; ProtectHostname = true; RestrictAddressFamilies = [ "AF_INET" "AF_INET6" "AF_UNIX" ]; ProtectSystem = "full"; ProtectProc = "noaccess"; ProcSubset = "pid"; PrivateUsers = true; PrivateTmp = true; ProtectKernelTunables = true; LockPersonality = true; RestrictRealtime = true; MemoryDenyWriteExecute = true; }; }; }; }; # For `nix develop` (optional, can be skipped): devShells = forAllSystems ( system: let pkgs = (import nixpkgs) { inherit system; }; in { default = pkgs.mkShell { nativeBuildInputs = with pkgs; [ rustc cargo jujutsu just ]; }; } ); formatter = forAllSystems ( system: let pkgs = (import nixpkgs) { inherit system; }; in pkgs.nixfmt-tree ); }; }