diff --git a/nixos/doc/manual/release-notes/rl-2511.section.md b/nixos/doc/manual/release-notes/rl-2511.section.md index a5c8dbec6474..2b0580a37379 100644 --- a/nixos/doc/manual/release-notes/rl-2511.section.md +++ b/nixos/doc/manual/release-notes/rl-2511.section.md @@ -12,6 +12,7 @@ - [gtklock](https://github.com/jovanlanik/gtklock), a GTK-based lockscreen for Wayland. Available as [programs.gtklock](#opt-programs.gtklock.enable). - [Chrysalis](https://github.com/keyboardio/Chrysalis), a graphical configurator for Kaleidoscope-powered keyboards. Available as [programs.chrysalis](#opt-programs.chrysalis.enable). +- [Chhoto URL](https://github.com/SinTan1729/chhoto-url), a simple, blazingly fast, selfhosted URL shortener with no unnecessary features, written in Rust. Available as [services.chhoto-url](#opt-services.chhoto-url.enable). ## Backward Incompatibilities {#sec-release-25.11-incompatibilities} diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index 7dbea2b3d65c..be174b9e453d 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -1519,6 +1519,7 @@ ./services/web-apps/castopod.nix ./services/web-apps/changedetection-io.nix ./services/web-apps/chatgpt-retrieval-plugin.nix + ./services/web-apps/chhoto-url.nix ./services/web-apps/cloudlog.nix ./services/web-apps/code-server.nix ./services/web-apps/coder.nix diff --git a/nixos/modules/services/web-apps/chhoto-url.nix b/nixos/modules/services/web-apps/chhoto-url.nix new file mode 100644 index 000000000000..8b6df98dc53e --- /dev/null +++ b/nixos/modules/services/web-apps/chhoto-url.nix @@ -0,0 +1,212 @@ +{ + config, + lib, + pkgs, + ... +}: + +let + cfg = config.services.chhoto-url; + + environment = lib.mapAttrs ( + _: value: + if value == true then + "True" + else if value == false then + "False" + else + toString value + ) cfg.settings; +in + +{ + meta.maintainers = with lib.maintainers; [ defelo ]; + + options.services.chhoto-url = { + enable = lib.mkEnableOption "Chhoto URL"; + + package = lib.mkPackageOption pkgs "chhoto-url" { }; + + settings = lib.mkOption { + description = '' + Configuration of Chhoto URL. + See for a list of options. + ''; + example = { + port = 4567; + }; + + type = lib.types.submodule { + freeformType = + with lib.types; + attrsOf (oneOf [ + str + int + bool + ]); + + options = { + db_url = lib.mkOption { + type = lib.types.path; + description = "The path of the sqlite database."; + default = "/var/lib/chhoto-url/urls.sqlite"; + }; + + port = lib.mkOption { + type = lib.types.port; + description = "The port to listen on."; + example = 4567; + }; + + cache_control_header = lib.mkOption { + type = lib.types.nullOr lib.types.str; + description = "The Cache-Control header to send."; + default = null; + example = "no-cache, private"; + }; + + disable_frontend = lib.mkOption { + type = lib.types.bool; + description = "Whether to disable the frontend."; + default = false; + }; + + public_mode = lib.mkOption { + type = lib.types.bool; + description = "Whether to enable public mode."; + default = false; + apply = x: if x then "Enable" else "Disable"; + }; + + public_mode_expiry_delay = lib.mkOption { + type = lib.types.nullOr lib.types.ints.unsigned; + description = "The maximum expiry delay in seconds to force in public mode."; + default = null; + example = 3600; + }; + + redirect_method = lib.mkOption { + type = lib.types.enum [ + "TEMPORARY" + "PERMANENT" + ]; + description = "The redirect method to use."; + default = "PERMANENT"; + }; + + hash_algorithm = lib.mkOption { + type = lib.types.nullOr (lib.types.enum [ "Argon2" ]); + description = '' + The hash algorithm to use for passwords and API keys. + Set to `null` if you want to provide these secrets as plaintext. + ''; + default = null; + }; + + site_url = lib.mkOption { + type = lib.types.nullOr lib.types.str; + description = "The URL under which Chhoto URL is externally reachable."; + default = null; + }; + + slug_style = lib.mkOption { + type = lib.types.enum [ + "Pair" + "UID" + ]; + description = "The slug style to use for auto-generated URLs."; + default = "Pair"; + }; + + slug_length = lib.mkOption { + type = lib.types.addCheck lib.types.int (x: x >= 4); + description = "The length of auto-generated slugs."; + default = 8; + }; + + try_longer_slugs = lib.mkOption { + type = lib.types.bool; + description = "Whether to try a longer UID upon collision."; + default = false; + }; + + allow_capital_letters = lib.mkOption { + type = lib.types.bool; + description = "Whether to allow capital letters in slugs."; + default = false; + }; + + custom_landing_directory = lib.mkOption { + type = lib.types.nullOr lib.types.path; + description = "The path of a directory which contains a custom landing page."; + default = null; + }; + }; + }; + }; + + environmentFiles = lib.mkOption { + type = lib.types.listOf lib.types.path; + default = [ ]; + example = [ "/run/secrets/chhoto-url.env" ]; + description = '' + Files to load environment variables from in addition to [](#opt-services.chhoto-url.settings). + This is useful to avoid putting secrets into the nix store. + See for a list of options. + ''; + }; + }; + + config = lib.mkIf cfg.enable { + systemd.services.chhoto-url = { + wantedBy = [ "multi-user.target" ]; + + inherit environment; + + serviceConfig = { + User = "chhoto-url"; + Group = "chhoto-url"; + DynamicUser = true; + StateDirectory = "chhoto-url"; + EnvironmentFile = cfg.environmentFiles; + + ExecStart = lib.getExe cfg.package; + + # hardening + AmbientCapabilities = ""; + CapabilityBoundingSet = [ "" ]; + DevicePolicy = "closed"; + LockPersonality = true; + MemoryDenyWriteExecute = true; + NoNewPrivileges = true; + PrivateDevices = true; + PrivateTmp = true; + PrivateUsers = true; + ProcSubset = "pid"; + ProtectClock = true; + ProtectControlGroups = true; + ProtectHome = true; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProtectProc = "invisible"; + ProtectSystem = "strict"; + RemoveIPC = true; + RestrictAddressFamilies = [ "AF_INET AF_INET6" ]; + RestrictNamespaces = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + SocketBindAllow = "tcp:${toString cfg.settings.port}"; + SocketBindDeny = "any"; + SystemCallArchitectures = "native"; + SystemCallFilter = [ + "@system-service" + "~@privileged" + "~@resources" + ]; + UMask = "0077"; + }; + }; + }; +}