Something went wrong. Try again.
an experimental, infinite-canvas terminal multiplexer using gpui
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336self: { config, lib, pkgs, ...}: let inherit (lib) mkEnableOption mkIf mkOption optional optionals types;
cfg = config.services.tarpd;
system = pkgs.stdenv.hostPlatform.system; flakePackages = self.packages.${system} or {};
defaultDaemon = flakePackages.tarpd or (throw "services.tarpd: the tarp flake has no package for ${system}");
defaultClient = flakePackages.tarp or (throw "services.tarpd: the tarp flake has no headless client for ${system}");
defaultUiClient = flakePackages.tarp-ui or defaultClient;
clientPackage = if cfg.client.ui.enable then if cfg.client.ui.package != null then cfg.client.ui.package else defaultUiClient else if cfg.client.package != null then cfg.client.package else defaultClient;
toml = pkgs.formats.toml {};
settingsFile = if cfg.settings == {} then null else if cfg.configFile != null then cfg.configFile else "/etc/tarpd/config.toml";
relay = if builtins.isList cfg.relay then lib.concatStringsSep "," cfg.relay else cfg.relay;
bindPort = if cfg.bind == null then null else lib.toInt (lib.last (lib.splitString ":" cfg.bind));in { imports = [ (lib.mkRemovedOptionModule ["services" "tarpd" "listen"] '' tarpd no longer listens on TCP: clients dial it over iroh by its endpoint id. Set `services.tarpd.bind` to pin the UDP port it uses, and run `tarp id` on the machine to get the id to dial. '') ];
options.services.tarpd = { enable = mkEnableOption "the tarp terminal daemon";
package = mkOption { type = types.package; default = defaultDaemon; defaultText = lib.literalExpression "tarp.packages.\${system}.tarpd"; description = "The tarpd package to run."; };
bind = mkOption { type = types.nullOr types.str; default = null; example = "0.0.0.0:41641"; description = '' UDP address the daemon's iroh endpoint binds, as `host:port`. Clients dial the daemon by its endpoint id, not this address; pin it only so a firewall can let direct connections in. When unset the daemon takes a random port on every interface, and reaches clients it cannot meet directly through a relay. ''; };
openFirewall = mkOption { type = types.bool; default = false; description = '' Open the UDP port of `bind` in the firewall, so clients can connect directly rather than through a relay. Needs `bind` with a fixed port. ''; };
relay = mkOption { type = types.either (types.enum ["default" "disabled"]) (types.listOf types.str); default = "default"; example = ["https://relay.example.com"]; description = '' Relays that carry traffic when a client cannot reach the daemon directly: `"default"` for n0's public relays, `"disabled"` for none, or a list of relay URLs (`TARPD_RELAY`). ''; };
discovery = mkOption { type = types.bool; default = true; description = '' Publish the daemon's addresses through n0's DNS, so its endpoint id alone is enough to reach it (`TARPD_DISCOVERY`). Without it, clients must be given an address: `<id>@<ip:port>`. ''; };
secretKeyFile = mkOption { type = types.nullOr (types.strMatching "/.*"); default = null; example = "/run/secrets/tarpd.key"; description = '' The daemon's secret key, as hex. Its public half is the endpoint id clients dial, so a key kept here keeps that id across reinstalls. It is handed to the service as a systemd credential, so the file can stay readable by root alone. When unset, the daemon makes a key in `stateDir` on first start. Give an absolute path as a string; a Nix path would copy the key into the world-readable store.
The id, from {command}`tarp id`, only says where the daemon is. A client on another machine gets in only if its own id is in `allowedClients` or was paired with {command}`tarp pair`. ''; };
allowLocal = mkOption { type = types.bool; default = true; description = '' Let in every client that reaches the daemon over loopback, without pairing (`TARPD_ALLOW_LOCAL`). On a machine shared with users who should not run commands as `user`, turn this off and pair each local client too. ''; };
allowedClients = mkOption { type = types.listOf types.str; default = []; example = ["4d1b7f276546c381bc95c939f1507fb419061012864e0a00ff1ad3d09d38d718"]; description = '' Client ids the daemon lets in, as {command}`tarp pair` prints them on each client machine (`TARPD_ALLOWED_CLIENTS`). Clients can also be paired at runtime: {command}`tarp pair <id>`, run as `user`, adds an id to `allowed_clients` in `stateDir`. Each client connects as `user` and can run commands as that user. ''; };
stateDir = mkOption { type = types.path; default = "/var/lib/tarpd"; description = '' Directory the daemon keeps its key and its endpoint file in. Managed through systemd's `StateDirectory` when left at the default. The endpoint file names the daemon's id and is readable by `group`: add a user to that group to let their `tarp` find this daemon. ''; };
configFile = mkOption { type = types.nullOr types.path; default = null; description = '' An existing tarpd configuration file to read. Mutually exclusive with `settings`; if both are set, this file wins. ''; };
settings = mkOption { type = types.submodule {freeformType = toml.type;}; default = {}; example = lib.literalExpression '' { terminal = { shell = "''${pkgs.bash}/bin/bash"; scrollback_lines = 20000; }; script.init = [ "/etc/tarpd/init.lua" ]; } ''; description = '' Extra tarpd configuration, rendered as TOML to {file}`/etc/tarpd/config.toml` and pointed at with `TARPD_CONFIG`. See {command}`tarpd template` for every key. ''; };
logLevel = mkOption { type = types.str; default = "info"; description = "`tracing` filter for the daemon's logs (`TARPD_LOG`)."; };
logFormat = mkOption { type = types.enum ["pretty" "json"]; default = "pretty"; description = "Log format (`TARPD_LOG_FORMAT`)."; };
user = mkOption { type = types.str; default = "tarpd"; description = "User the daemon runs as, and whose shells it starts."; };
group = mkOption { type = types.str; default = "tarpd"; description = "Group the daemon runs as."; };
client = { enable = mkOption { type = types.bool; default = true; description = '' Install the tarp client alongside the daemon, so enabling the service is enough to talk to it: the `tarp` cli queries and drives the daemon, for example `tarp status` or `tarp terminal.list`. ''; };
package = mkOption { type = types.nullOr types.package; default = null; defaultText = lib.literalExpression "tarp.packages.\${system}.tarp"; description = "The headless client package to install."; };
ui = { enable = mkOption { type = types.bool; default = false; description = '' Install the gpui build of the client instead of the headless one. It is the same `tarp` cli with the interface built in. ''; };
package = mkOption { type = types.nullOr types.package; default = null; defaultText = lib.literalExpression "tarp.packages.\${system}.tarp-ui"; description = "The GUI client package to install when `ui.enable` is set."; }; }; }; };
config = mkIf cfg.enable { assertions = [ { assertion = !(cfg.settings != {} && cfg.configFile != null); message = "services.tarpd: set either `settings` or `configFile`, not both."; } { assertion = cfg.openFirewall -> (bindPort != null && bindPort != 0); message = "services.tarpd: `openFirewall` needs `bind` with a fixed, non-zero port."; } ];
environment.systemPackages = optional cfg.client.enable clientPackage;
environment.variables = mkIf cfg.client.enable { TARPD_STATE_DIR = cfg.stateDir; };
networking.firewall.allowedUDPPorts = optionals cfg.openFirewall [bindPort];
environment.etc = mkIf (cfg.settings != {} && cfg.configFile == null) { "tarpd/config.toml".source = toml.generate "tarpd.toml" cfg.settings; };
users.users = mkIf (cfg.user == "tarpd") { tarpd = { isSystemUser = true; group = cfg.group; description = "tarp terminal daemon"; }; };
users.groups = mkIf (cfg.group == "tarpd") {tarpd = {};};
systemd.services.tarpd = { description = "tarp terminal daemon"; wantedBy = ["multi-user.target"]; wants = ["network-online.target"]; after = ["network-online.target"];
environment = { TARPD_STATE_DIR = cfg.stateDir; TARPD_RELAY = relay; TARPD_DISCOVERY = lib.boolToString cfg.discovery; TARPD_LOG = cfg.logLevel; TARPD_LOG_FORMAT = cfg.logFormat; } // lib.optionalAttrs (cfg.bind != null) {TARPD_BIND = cfg.bind;} // lib.optionalAttrs (!cfg.allowLocal) {TARPD_ALLOW_LOCAL = "false";} // lib.optionalAttrs (cfg.allowedClients != []) { TARPD_ALLOWED_CLIENTS = lib.concatStringsSep "," cfg.allowedClients; } // lib.optionalAttrs (cfg.secretKeyFile != null) { TARPD_SECRET_KEY_FILE = "%d/endpoint.key"; } // lib.optionalAttrs (settingsFile != null) {TARPD_CONFIG = settingsFile;};
serviceConfig = { ExecStart = lib.getExe cfg.package; User = cfg.user; Group = cfg.group;
StateDirectory = "tarpd"; StateDirectoryMode = "0750"; WorkingDirectory = cfg.stateDir;
LoadCredential = optional (cfg.secretKeyFile != null) "endpoint.key:${cfg.secretKeyFile}";
Restart = "on-failure"; RestartSec = 2;
NoNewPrivileges = true; ProtectSystem = "full"; ProtectHome = false; }; }; };}