diff options
60 files changed, 1272 insertions, 521 deletions
@@ -3,6 +3,10 @@ This repository contains my personal config files. * `nixos/` -- contains my [NixOS](https://nixos.org/) configurations + * hosts + * ev -- my home server + * hamac -- my laptop + * tente -- my VPS * `user/` -- contains my dotfiles The idea behind this structure is that dotfiles like my diff --git a/nixos/.gitignore b/nixos/.gitignore new file mode 100644 index 0000000..c4a847d --- /dev/null +++ b/nixos/.gitignore @@ -0,0 +1 @@ +/result diff --git a/nixos/README.md b/nixos/README.md index 67b6cf7..075f812 100644 --- a/nixos/README.md +++ b/nixos/README.md @@ -32,7 +32,8 @@ We're assuming that you just installed NixOS by going through the [official inst nixos-generate-config --dir . ``` 3. Rename the `configuration.nix` to `default.nix`. - Add `# channel="..."` to the start of the `default.nix` file where `...` is the key of a channel pinned in `npins/sources.json`. + Create a `metadata.toml` file with a `channel` field + where the value is the key of a channel pinned in `npins/sources.json`. A new channel can be pinned with: ``` diff --git a/nixos/helpers.nix b/nixos/helpers.nix index c6d1ff8..a236c42 100644 --- a/nixos/helpers.nix +++ b/nixos/helpers.nix @@ -1,8 +1,5 @@ -{ config, ... }: +{ config, lib, pkgs, ... }: -let - nixpkgs = import <nixpkgs> {}; -in { mkNginxConfig = name: '' access_log /var/log/nginx/${name}.access.log json; @@ -63,8 +60,32 @@ in '}'; ''; + serviceIndexHost = tailnetHostname: webPorts: + let + li = name: port: ''<li><a href="http://${tailnetHostname}:${toString port}">${name}</a></li>''; + html = "<ul>" + (lib.concatStringsSep "\n" (lib.mapAttrsToList li webPorts)) + "</ul>"; + in + { + listenAddresses = [tailnetHostname]; + locations."= /".extraConfig = '' + default_type text/html; + return 200 '${html}'; + ''; + }; + + writeAlloyConfig = cfg: + let + alloy-nix-config = "${pkgs.callPackage <top/shared/alloy-nix-config> {}}/bin/alloy-nix-config"; + in + pkgs.runCommand "generated-config" {} '' + ${alloy-nix-config} ${pkgs.writeText "input.json" (builtins.toJSON cfg)} $out + # FUTURE: run alloy validate + ''; + + alloyConfigRef = s: { "$ref" = s; }; + joinWgNamespace = ns: cfg: - nixpkgs.lib.attrsets.recursiveUpdate cfg { + lib.attrsets.recursiveUpdate cfg { bindsTo = ["netns@${ns}.service"]; after = ["wireguard-wg-${ns}.service"]; unitConfig.JoinsNamespaceOf = "netns@${ns}.service"; @@ -81,13 +102,9 @@ in TimeoutStopSec = 300; }; wantedBy = ["multi-user.target"]; - script = - let - pkgs = nixpkgs.pkgs; - in - '' - ${pkgs.iproute2}/bin/ip netns exec ${ns} ${pkgs.iproute2}/bin/ip link set dev lo up - ${pkgs.socat}/bin/socat tcp-listen:${toString port},fork,reuseaddr exec:'${pkgs.iproute2}/bin/ip netns exec ${ns} ${pkgs.socat}/bin/socat STDIO "tcp-connect:localhost:${toString port}"',nofork - ''; + script = '' + ${pkgs.iproute2}/bin/ip netns exec ${ns} ${pkgs.iproute2}/bin/ip link set dev lo up + ${pkgs.socat}/bin/socat tcp-listen:${toString port},fork,reuseaddr exec:'${pkgs.iproute2}/bin/ip netns exec ${ns} ${pkgs.socat}/bin/socat STDIO "tcp-connect:localhost:${toString port}"',nofork + ''; }; } diff --git a/nixos/hosts/ev/default.nix b/nixos/hosts/ev/default.nix index e59ec01..fbda40f 100644 --- a/nixos/hosts/ev/default.nix +++ b/nixos/hosts/ev/default.nix @@ -1,25 +1,60 @@ -# channel="nixos-small" # Edit this configuration file to define what should be installed on # your system. Help is available in the configuration.nix(5) man page # and in the NixOS manual (accessible by running ‘nixos-help’). -{ config, pkgs, ... }: +{ config, lib, pkgs, ... }: +let + ports = import ./ports.nix; + helpers = import <top/helpers.nix> { inherit config lib pkgs; }; +in { imports = [ ./hardware-configuration.nix <top/profiles/server> + <top/shared/monitoring.nix> + <top/shared/postgresql.nix> <top/shared/tailscale.nix> <top/shared/basics-physical.nix> <top/shared/vpn.nix> + ./exporters.nix ./home-automation.nix ./kodi.nix + ./miniflux.nix ./torrent.nix + ./hosehawk.nix ]; - home-automation.zigbee2mqttPort = 8080; - torrent.qbittorrentWebUiPort = 7777; + # enable unlocking full disk encryption via SSH + boot.kernelParams = ["ip=dhcp"]; + boot.initrd = { + availableKernelModules = ["r8169"]; # for Ethernet + network = { + enable = true; + ssh = { + enable = true; + port = 2222; + hostKeys = ["/etc/secrets/initrd/ssh_host_ed25519_key"]; + authorizedKeys = [ + "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDo/Y7w3hQgUIOQi63e8+L7eTMsVWl1vqY+Bd4tvwShdAj8ECU6JnD6gkCVzqXfUNdpA0Csd9PZlGAbXU+0kxudryFV6mxbXvYf+z70vcF02L5lDJ1tzCV7t7SwXnoenSNBIra/M2zDFgGM4oUkl9iZ2wxn/X/mvFzopJsM3xe2YNtJhXzCyaQTakKRDdHMyj9E867Ko03H6ZD2PI+9G+S39tk5ZLIcG9qhLTfDPziiZj7AIeTYVoxQycajwSlvp8BLzxxCKH8Mq7qW86jfT4lYvUuL5ItQ1cdFbmvJNKpgGXBzgBU+6kWf5c7P2aajhE3otgpfBXWBZRA3hKk+E+xX martin@hamac" + ]; + shell = "/bin/cryptsetup-askpass"; + }; + }; + }; + # unsure why this is necessary + networking.interfaces.enp3s0.useDHCP = true; + + home-automation.zigbee2mqttPort = ports.zigbee2mqtt; + torrent.qbittorrentWebUiPort = ports.qbittorrent; torrent.networkNamespace = "se"; + monitoring.alloyUiPort = ports.grafanaAlloy; + monitoring.lokiPort = ports.grafanaLoki; + monitoring.prometheusPort = ports.prometheus; + monitoring.prometheusNodeExporterPort = ports.prometheusNodeExporter; + exporters.sqlExporterPort = ports.prometheusSqlExporter; + hosehawk.port = ports.hosehawk; + miniflux.port = ports.miniflux; home-automation.zigbeeSerialPort = "/dev/serial/by-id/usb-ITead_Sonoff_Zigbee_3.0_USB_Dongle_Plus_e2fed465c59ded11962fd7a5a7669f5d-if00-port0"; @@ -60,6 +95,11 @@ ]; networking.firewall.allowedUDPPorts = []; + services.nginx = { + enable = true; + virtualHosts."ev.tailnet" = helpers.serviceIndexHost "ev.tailnet" ports.webUis; + }; + # This value determines the NixOS release from which the default # settings for stateful data, like file locations and database versions # on your system were taken. It‘s perfectly fine and recommended to leave diff --git a/nixos/hosts/ev/exporters.nix b/nixos/hosts/ev/exporters.nix new file mode 100644 index 0000000..1c03c35 --- /dev/null +++ b/nixos/hosts/ev/exporters.nix @@ -0,0 +1,42 @@ +{ config, lib, pkgs, ... }: + +let + helpers = import <top/shared/postgres-collectors.nix> { inherit config lib pkgs; }; + cfg = config.exporters; +in +{ + options.exporters = { + sqlExporterPort = lib.mkOption { + type = lib.types.int; + }; + }; + + imports = [ + <top/shared/prometheus-sql-exporter/service.nix> + ]; + + config = { + services.prometheus-sql-exporter = { + enable = true; + port = cfg.sqlExporterPort; + config = { + target = { + # This URL should be postgresql:///postgres?host=/run/postgresql + # but sql_exporter uses xo/dburl which isn't spec-compliant: https://github.com/xo/dburl/issues/46 + data_source_name = "postgresql:/run/postgresql:/postgres"; + collectors = helpers.collectorNames; + }; + collectors = helpers.collectors; + }; + }; + + monitoring.prometheusScrapeConfigs = [ + { + job_name = "sql"; + static_configs = [{ + targets = [ "localhost:${toString cfg.sqlExporterPort}" ]; + }]; + } + ]; + }; +} diff --git a/nixos/hosts/ev/hosehawk.nix b/nixos/hosts/ev/hosehawk.nix new file mode 100644 index 0000000..09637cd --- /dev/null +++ b/nixos/hosts/ev/hosehawk.nix @@ -0,0 +1,28 @@ +{ config, lib, pkgs, ... }: + +let + cfg = config.hosehawk; +in +{ + imports = [ + /home/martin/repos/hosehawk/services/hosenest.nix + /home/martin/repos/hosehawk/services/hosehawk.nix + ]; + + options.hosehawk = { + port = lib.mkOption { + type = lib.types.int; + }; + }; + + config = { + services.hosenest = { + enable = true; + port = cfg.port; + }; + services.hosehawk = { + enable = true; + hosenestUrl = "http://localhost:${toString cfg.port}/"; + }; + }; +} diff --git a/nixos/hosts/ev/kodi.nix b/nixos/hosts/ev/kodi.nix index 2aea216..3862dfe 100644 --- a/nixos/hosts/ev/kodi.nix +++ b/nixos/hosts/ev/kodi.nix @@ -7,7 +7,10 @@ let in { config = { - users.extraUsers.kodi.isNormalUser = true; + users.users.kodi = { + isNormalUser = true; + extraGroups = ["audio"]; + }; services = { cage = { diff --git a/nixos/hosts/ev/metadata.toml b/nixos/hosts/ev/metadata.toml new file mode 100644 index 0000000..58f0301 --- /dev/null +++ b/nixos/hosts/ev/metadata.toml @@ -0,0 +1 @@ +channel = "nixos-small" diff --git a/nixos/hosts/ev/miniflux.nix b/nixos/hosts/ev/miniflux.nix new file mode 100644 index 0000000..5bf1ccd --- /dev/null +++ b/nixos/hosts/ev/miniflux.nix @@ -0,0 +1,27 @@ +{ config, lib, pkgs, ... }: + +let + cfg = config.miniflux; +in +{ + options.miniflux = { + port = lib.mkOption { + type = lib.types.int; + }; + }; + + config = { + age.secrets.miniflux-admin.file = <top/secrets/miniflux-admin.age>; + + services.miniflux = { + enable = true; + config = { + LISTEN_ADDR = "0.0.0.0:${toString cfg.port}"; + DATABASE_URL = "user=miniflux host=/run/postgresql dbname=miniflux"; + }; + # miniflux does not support disabling its authentication. https://github.com/miniflux/v2/issues/408 + adminCredentialsFile = config.age.secrets.miniflux-admin.path; + createDatabaseLocally = false; + }; + }; +} diff --git a/nixos/hosts/ev/ports.nix b/nixos/hosts/ev/ports.nix new file mode 100644 index 0000000..502e190 --- /dev/null +++ b/nixos/hosts/ev/ports.nix @@ -0,0 +1,22 @@ +rec { + grafanaAlloy = 3001; + grafanaLoki = 3030; + hosehawk = 4000; + miniflux = 4001; + prometheus = 9090; + prometheusNodeExporter = 9002; + prometheusSqlExporter = 9003; + qbittorrent = 7777; + zigbee2mqtt = 8080; + + webUis = { + inherit + grafanaAlloy + hosehawk + miniflux + prometheus + qbittorrent + zigbee2mqtt + ; + }; +} diff --git a/nixos/hosts/ev/torrent.nix b/nixos/hosts/ev/torrent.nix index ddc4dcb..a09c507 100644 --- a/nixos/hosts/ev/torrent.nix +++ b/nixos/hosts/ev/torrent.nix @@ -1,7 +1,7 @@ { config, lib, pkgs, ... }: let - helpers = import <top/helpers.nix> { inherit config; }; + helpers = import <top/helpers.nix> { inherit config lib pkgs; }; cfg = config.torrent; in { diff --git a/nixos/hosts/hamac/default.nix b/nixos/hosts/hamac/default.nix index 7085ed3..523b7d6 100644 --- a/nixos/hosts/hamac/default.nix +++ b/nixos/hosts/hamac/default.nix @@ -1,12 +1,11 @@ -# channel="nixos" # See the configuration.nix(5) man page and the NixOS manual (accessible by running `nixos-help`). +{ config, lib, pkgs, ... }: + let - sources = import ../../npins; + sources = import <top/npins>; pkgs-unstable = import sources.nixpkgs-unstable {}; in -{ config, lib, pkgs, ... }: - { _module.args = { inherit pkgs-unstable; }; imports = [ diff --git a/nixos/hosts/hamac/metadata.toml b/nixos/hosts/hamac/metadata.toml new file mode 100644 index 0000000..fc34e27 --- /dev/null +++ b/nixos/hosts/hamac/metadata.toml @@ -0,0 +1 @@ +channel = "nixos" diff --git a/nixos/hosts/tente/default.nix b/nixos/hosts/tente/default.nix index 8904ba7..a54de6c 100644 --- a/nixos/hosts/tente/default.nix +++ b/nixos/hosts/tente/default.nix @@ -1,4 +1,3 @@ -# channel="nixos-small" # Edit this configuration file to define what should be installed on # your system. Help is available in the configuration.nix(5) man page, on # https://search.nixos.org/options and in the NixOS manual (`nixos-help`). @@ -6,46 +5,73 @@ { config, lib, pkgs, ... }: let - domain = "push-f.com"; + baseDomain = "push-f.com"; acmeEmail = "martin@push-f.com"; - sources = import ../../npins; - helpers = import <top/helpers.nix> { inherit config; }; + ports = import ./ports.nix; + sources = import <top/npins>; + helpers = import <top/helpers.nix> { inherit config lib pkgs; }; + pkgs-unstable = import sources.nixpkgs-unstable {}; in { + _module.args = { inherit pkgs-unstable; }; imports = [ ./hardware-configuration.nix <top/profiles/server> <top/shared/postgresql.nix> <top/shared/tailscale.nix> + <top/shared/monitoring.nix> ./web-personal.nix ./git.nix - ./gotify.nix ./headscale.nix ./matrix.nix - ./monitoring.nix + ./exporters.nix + ./grafana.nix + "${sources.my-lex-surf}/service.nix" "${sources.my-osm-proposals}/service.nix" "${sources.my-geopos-link}/service.nix" + "${sources.my-rust-features}/service.nix" "${sources.my-spec-pub}/service.nix" ]; - web-personal.domain = domain; + web-personal.domain = baseDomain; web-personal.matrixApiDomain = config.matrix.apiDomain; - git.webUiDomain = "git.${domain}"; - headscale.domain = "tailscale.${domain}"; - matrix.serverName = domain; - matrix.apiDomain = "matrix.${domain}"; + git.webUiDomain = "git.${baseDomain}"; + headscale.domain = "tailscale.${baseDomain}"; + matrix.serverName = baseDomain; + matrix.apiDomain = "matrix.${baseDomain}"; + + users.users.www-generator = { + isSystemUser = true; + group = "www-generator"; + }; + users.groups.www-generator = {}; + + services.lex-surf = + let + domain = "lex.surf"; + in + { + enable = true; + domain = domain; + enableACME = true; + fetchUser = "www-generator"; + nginx = { + forceSSL = true; + extraConfig = helpers.mkNginxConfig domain; + }; + }; services.osm_proposals = let - subdomain = "osm-proposals.${domain}"; + domain = "osm-proposals.${baseDomain}"; in { enable = true; - virtualHost = subdomain; + virtualHost = domain; nginx = { enableACME = true; forceSSL = true; - extraConfig = helpers.mkNginxConfig subdomain; + extraConfig = helpers.mkNginxConfig domain; }; }; @@ -63,6 +89,21 @@ in }; }; + services.rust-features = + let + domain = "rust-features.${baseDomain}"; + in + { + enable = true; + user = "www-generator"; + virtualHost = domain; + nginx = { + enableACME = true; + forceSSL = true; + extraConfig = helpers.mkNginxConfig domain; + }; + }; + services.spec-pub = let domain = "spec.pub"; @@ -77,14 +118,16 @@ in }; }; - monitoring.grafanaUiPort = 3000; - monitoring.alloyUiPort = 3001; - monitoring.lokiPort = 3030; - gotify.port = 4000; - monitoring.prometheusNodeExporterPort = 9002; - monitoring.prometheusSqlExporterPort = 9003; - headscale.port = 8080; - matrix.port = 8008; + grafana.port = ports.grafana; + grafana.matrixForwarderPort = ports.grafanaMatrixForwarder; + grafana.matrixServerUrl = "http://localhost:${toString ports.matrix}"; + monitoring.alloyUiPort = ports.grafanaAlloy; + monitoring.lokiPort = ports.grafanaLoki; + monitoring.prometheusPort = ports.prometheus; + monitoring.prometheusNodeExporterPort = ports.prometheusNodeExporter; + exporters.sqlExporterPort = ports.prometheusSqlExporter; + headscale.port = ports.headscale; + matrix.port = ports.matrix; # Use the GRUB 2 boot loader. boot.loader.grub.enable = true; @@ -160,6 +203,8 @@ in enable = true; group = "www-data"; + virtualHosts."tente.tailnet" = helpers.serviceIndexHost "tente.tailnet" ports.webUis; + appendHttpConfig = '' # Close the connection for unknown Host headers. # If we don't do this nginx serves some random virtualhost. diff --git a/nixos/hosts/tente/exporters.nix b/nixos/hosts/tente/exporters.nix new file mode 100644 index 0000000..1c03c35 --- /dev/null +++ b/nixos/hosts/tente/exporters.nix @@ -0,0 +1,42 @@ +{ config, lib, pkgs, ... }: + +let + helpers = import <top/shared/postgres-collectors.nix> { inherit config lib pkgs; }; + cfg = config.exporters; +in +{ + options.exporters = { + sqlExporterPort = lib.mkOption { + type = lib.types.int; + }; + }; + + imports = [ + <top/shared/prometheus-sql-exporter/service.nix> + ]; + + config = { + services.prometheus-sql-exporter = { + enable = true; + port = cfg.sqlExporterPort; + config = { + target = { + # This URL should be postgresql:///postgres?host=/run/postgresql + # but sql_exporter uses xo/dburl which isn't spec-compliant: https://github.com/xo/dburl/issues/46 + data_source_name = "postgresql:/run/postgresql:/postgres"; + collectors = helpers.collectorNames; + }; + collectors = helpers.collectors; + }; + }; + + monitoring.prometheusScrapeConfigs = [ + { + job_name = "sql"; + static_configs = [{ + targets = [ "localhost:${toString cfg.sqlExporterPort}" ]; + }]; + } + ]; + }; +} diff --git a/nixos/hosts/tente/git.nix b/nixos/hosts/tente/git.nix index ee10a43..49ecff0 100644 --- a/nixos/hosts/tente/git.nix +++ b/nixos/hosts/tente/git.nix @@ -2,7 +2,7 @@ let cfg = config.git; - helpers = import <top/helpers.nix> { inherit config; }; + helpers = import <top/helpers.nix> { inherit config lib pkgs; }; in { options.git = { @@ -47,6 +47,19 @@ in enable-git-config = 1; root-title = "push-f.com repositories"; root-desc = "My various repositories."; + readme = ":README.md"; + about-filter = + # about-formatting.sh from cgit uses python-markdown which doesn't + # follow CommonMark, so we're using the lowdown parser instead. + let + # The about-filter is invoked with stdin but also with the filename + # as argv[1] so we wrap lowdown in a script to ignore the argument. + formatScript = pkgs.writeScriptBin "about-format.sh" '' + #!/bin/sh + ${pkgs.lowdown}/bin/lowdown + ''; + in + "${formatScript}/bin/about-format.sh"; enable-index-owner = 0; source-filter = "${pkgs.cgit}/lib/cgit/filters/syntax-highlighting.py"; clone-prefix = "https://${cfg.webUiDomain}"; diff --git a/nixos/hosts/tente/gotify.nix b/nixos/hosts/tente/gotify.nix deleted file mode 100644 index ba0fd02..0000000 --- a/nixos/hosts/tente/gotify.nix +++ /dev/null @@ -1,21 +0,0 @@ -{ config, lib, ... }: - -let - cfg = config.gotify; -in -{ - options.gotify = { - port = lib.mkOption { - type = lib.types.int; - }; - }; - - config = { - services.gotify = { - enable = true; - environment = { - GOTIFY_SERVER_PORT = cfg.port; - }; - }; - }; -} diff --git a/nixos/hosts/tente/grafana.nix b/nixos/hosts/tente/grafana.nix new file mode 100644 index 0000000..f7c8789 --- /dev/null +++ b/nixos/hosts/tente/grafana.nix @@ -0,0 +1,75 @@ +{ config, lib, pkgs, ... }: + +let + cfg = config.grafana; + evPorts = import <top/hosts/ev/ports.nix>; +in +{ + options.grafana = { + port = lib.mkOption { + type = lib.types.int; + }; + matrixForwarderPort = lib.mkOption { + type = lib.types.int; + }; + prometheusSqlExporterPort = lib.mkOption { + type = lib.types.int; + }; + matrixServerUrl = lib.mkOption { + type = lib.types.str; + }; + }; + + imports = [ + <top/shared/prometheus-sql-exporter/service.nix> + <top/shared/grafana-matrix-forwarder/service.nix> + ]; + + config = { + age.secrets.grafana-matrix-forwarder-env.file = <top/secrets/grafana-matrix-forwarder-env.age>; + + services.grafana = { + enable = true; + settings = { + server = { + http_addr = "0.0.0.0"; + http_port = cfg.port; + }; + }; + + provision = { + enable = true; + datasources.settings.datasources = [ + { + name = "Prometheus Tente"; + type = "prometheus"; + url = "http://${config.services.prometheus.listenAddress}:${toString config.monitoring.prometheusPort}"; + } + { + name = "Loki Tente"; + type = "loki"; + access = "proxy"; + url = "http://127.0.0.1:${toString config.monitoring.lokiPort}"; + } + { + name = "Prometheus Ev"; + type = "prometheus"; + url = "http://ev.tailnet:${toString evPorts.prometheus}"; + } + { + name = "Loki Ev"; + type = "loki"; + access = "proxy"; + url = "http://ev.tailnet:${toString evPorts.grafanaLoki}"; + } + ]; + }; + }; + services.grafana-matrix-forwarder = { + enable = true; + port = cfg.matrixForwarderPort; + homeserver = cfg.matrixServerUrl; + environmentFile = config.age.secrets.grafana-matrix-forwarder-env.path; + }; + }; +} diff --git a/nixos/hosts/tente/matrix.nix b/nixos/hosts/tente/matrix.nix index cf0f09a..89782fc 100644 --- a/nixos/hosts/tente/matrix.nix +++ b/nixos/hosts/tente/matrix.nix @@ -1,8 +1,8 @@ -{ config, lib, pkgs, ... }: +{ config, lib, pkgs, pkgs-unstable, ... }: let cfg = config.matrix; - helpers = import <top/helpers.nix> { inherit config; }; + helpers = import <top/helpers.nix> { inherit config lib pkgs; }; in { options.matrix = { @@ -19,32 +19,19 @@ in config = { services = { - matrix-synapse = { + matrix-conduit = { enable = true; + package = pkgs-unstable.matrix-conduit; settings = { - server_name = cfg.serverName; - listeners = [{ - # This listener matches the default of NixOS 24.11 (replicated here to make the port configurable). - bind_addresses = ["127.0.0.1"]; + global = { + server_name = cfg.serverName; port = cfg.port; - resources = [ - { - compress = true; - names = ["client"]; - } - { - compress = false; - names = ["federation"]; - } - ]; - tls = false; - type = "http"; - x_forwarded = true; - }]; + address = "127.0.0.1"; # this is the default of conduit but the nixos service defaults to ::1 + database_backend = "rocksdb"; + enable_lightning_bolt = false; + allow_registration = false; + }; }; - - # The default is INFO which can easily spam the systemd journal with 500k messages a day. - log.root.level = "WARNING"; }; nginx.virtualHosts.${cfg.apiDomain} = { @@ -54,14 +41,12 @@ in # TODO: add locations."/" with some message - # Forward all Matrix API calls to the synapse Matrix homeserver. A trailing slash + # Forward all Matrix API calls to the Conduit Matrix homeserver. A trailing slash # *must not* be used here. locations."/_matrix".proxyPass = "http://127.0.0.1:${toString cfg.port}"; - # Forward requests for e.g. SSO and password-resets. - locations."/_synapse/client".proxyPass = "http://127.0.0.1:${toString cfg.port}"; }; - # I don't really care about these nginx access logs. Synapse has its own + # I don't really care about these nginx access logs. Conduit has its own # log anyway and with the default log rotation (weekly and delaycompress=true) # the access logs from last week took up ~800MB. logrotate.settings.matrix-nginx-access-log = diff --git a/nixos/hosts/tente/metadata.toml b/nixos/hosts/tente/metadata.toml new file mode 100644 index 0000000..58f0301 --- /dev/null +++ b/nixos/hosts/tente/metadata.toml @@ -0,0 +1 @@ +channel = "nixos-small" diff --git a/nixos/hosts/tente/monitoring.nix b/nixos/hosts/tente/monitoring.nix deleted file mode 100644 index 7e92eed..0000000 --- a/nixos/hosts/tente/monitoring.nix +++ /dev/null @@ -1,317 +0,0 @@ -{ config, lib, pkgs, ... }: - -let - cfg = config.monitoring; -in -{ - options.monitoring = { - grafanaUiPort = lib.mkOption { - type = lib.types.int; - }; - lokiPort = lib.mkOption { - type = lib.types.int; - }; - alloyUiPort = lib.mkOption { - type = lib.types.int; - }; - prometheusNodeExporterPort = lib.mkOption { - type = lib.types.int; - }; - prometheusSqlExporterPort = lib.mkOption { - type = lib.types.int; - }; - }; - - imports = [ - <top/shared/prometheus-sql-exporter/service.nix> - ]; - - config = { - services.grafana = { - enable = true; - settings = { - server = { - http_addr = "0.0.0.0"; - http_port = cfg.grafanaUiPort; - }; - }; - - provision = { - enable = true; - datasources.settings.datasources = [ - { - name = "Prometheus"; - type = "prometheus"; - url = "http://${config.services.prometheus.listenAddress}:${toString config.services.prometheus.port}"; - } - { - name = "Loki"; - type = "loki"; - access = "proxy"; - url = "http://127.0.0.1:${toString cfg.lokiPort}"; - } - ]; - }; - }; - - services.prometheus = { - enable = true; - - scrapeConfigs = [ - { - job_name = "node"; - static_configs = [{ - targets = [ "localhost:${toString cfg.prometheusNodeExporterPort}" ]; - }]; - } - { - job_name = "sql"; - static_configs = [{ - targets = [ "localhost:${toString cfg.prometheusSqlExporterPort}" ]; - }]; - } - ]; - - exporters.node = { - enable = true; - enabledCollectors = [ "systemd" ]; - port = cfg.prometheusNodeExporterPort; - }; - }; - - services.prometheus-sql-exporter = { - enable = true; - port = cfg.prometheusSqlExporterPort; - config = { - target = { - # This URL should be postgresql:///postgres?host=/run/postgresql - # but sql_exporter uses xo/dburl which isn't spec-compliant: https://github.com/xo/dburl/issues/46 - data_source_name = "postgresql:/run/postgresql:/postgres"; - collectors = ["db-sizes"]; - }; - collectors = [ - { - collector_name = "db-sizes"; - metrics = [ - { - metric_name = "pg_db_size_bytes"; - help = "disk space used by the database"; - type = "gauge"; - key_labels = ["database_name"]; - values = ["size"]; - query = "SELECT datname AS database_name, pg_database_size(datname) as size from pg_database"; - } - ]; - } - ]; - }; - }; - - services.loki = { - enable = true; - configuration = { - server.http_listen_port = cfg.lokiPort; - auth_enabled = false; - - ingester = { - lifecycler = { - address = "127.0.0.1"; - ring = { - kvstore.store = "inmemory"; - replication_factor = 1; - }; - }; - }; - - schema_config = { - configs = [{ - store = "tsdb"; - object_store = "filesystem"; - schema = "v13"; - index = { - prefix = "index_"; - period = "24h"; - }; - }]; - }; - - storage_config = { - tsdb_shipper = { - active_index_directory = "/var/lib/loki/tsdb-active"; - cache_location = "/var/lib/loki/tsdb-cache"; - }; - }; - - compactor = { - working_directory = "/var/lib/loki"; - }; - - limits_config = { - allow_structured_metadata = true; - }; - }; - }; - - systemd.services.alloy = { - serviceConfig = { - SupplementaryGroups = [ - "systemd-journal" - "www-data" - ]; - }; - }; - - services.alloy = { - enable = true; - extraFlags = ["--server.http.listen-addr=0.0.0.0:${toString cfg.alloyUiPort}"]; - # TODO: submit PR to nixpkgs so that the alloy config can be specified as a JSON expression - configPath = pkgs.writeText "config.alloy" '' - loki.source.journal "journal" { - max_age = "12h0m0s" - relabel_rules = discovery.relabel.journal.rules - forward_to = [loki.process.journal.receiver] - labels = { - host = "tente", - job = "systemd-journal", - } - } - - loki.process "journal" { - forward_to = [loki.write.default.receiver] - - stage.match { - // Select messages from systemd services that have LogExtraFields=log_format=logfmt. - selector = "{__journal_log_format=\"logfmt\"}" - stage.logfmt { - mapping = { time = "", level = "" } - } - stage.timestamp { - source = "time" - format = "RFC3339" - } - stage.structured_metadata { - values = { level = "" } - } - } - } - - discovery.relabel "journal" { - targets = [] - - rule { - source_labels = ["__journal__systemd_unit"] - target_label = "unit" - } - } - - loki.source.file "nginx_access" { - targets = local.file_match.nginx_access.targets - forward_to = [loki.process.nginx_access.receiver] - } - - local.file_match "nginx_access" { - path_targets = [{ - __path__ = "/var/log/nginx/*.access.log", - }] - } - - loki.process "nginx_access" { - forward_to = [loki.write.default.receiver] - - stage.static_labels { - values = { - job = "nginx", - } - } - - // Extracting the log file name as vhost because it's more convenient - // to query for than the full filename. We could also use server_name - // but there could be wildcard server_names and Loki labels should have - // a low cardinality for performance reasons. - stage.regex { - source = "filename" - expression = "(?P<vhost>[^/]+)\\.access\\.log$" - } - - stage.labels { - values = { - vhost = "", - } - } - - stage.json { - expressions = { "msec" = "", path = "" } - } - - stage.timestamp { - source = "msec" - format = "Unix" - } - - // Setting level=info to prevent Loki's log level detection from wrongly - // detecting messages with paths containing "error" as errors. - // Creating the filetype entry via stage.template because there's no - // static_structured_metadata stage yet. (https://github.com/grafana/loki/issues/16703) - stage.template { - source = "level" - template = "info" - } - stage.structured_metadata { - values = { level = "" } - } - - stage.labels { - values = { - // Temporarily adding path as a label so that we can use it in the match selectors. - path = "", - } - } - - stage.match { - selector = "{path=~\"/\\\\.well-known/.*\"}" - // Creating the filetype entry via stage.template because there's no - // static_structured_metadata stage yet. (https://github.com/grafana/loki/issues/16703) - stage.template { - source = "filetype" - template = "well-known" - } - } - - stage.match { - selector = "{path=\"/robots.txt\"}" - stage.template { - source = "filetype" - template = "robots.txt" - } - } - - stage.match { - selector = "{path=~\".*\\\\.atom$\"}" - stage.template { - source = "filetype" - template = "feed" - } - } - - stage.structured_metadata { - values = { - filetype = "", - } - } - - // Dropping path again because it has a too high cardinality for a label. - stage.label_drop { - values = [ "path" ] - } - } - - loki.write "default" { - endpoint { - url = "http://127.0.0.1:${toString cfg.lokiPort}/loki/api/v1/push" - } - external_labels = {} - } - ''; - }; - }; -} diff --git a/nixos/hosts/tente/ports.nix b/nixos/hosts/tente/ports.nix new file mode 100644 index 0000000..133d0f7 --- /dev/null +++ b/nixos/hosts/tente/ports.nix @@ -0,0 +1,19 @@ +rec { + grafana = 3000; + grafanaAlloy = 3001; + grafanaLoki = 3030; + grafanaMatrixForwarder = 3002; + headscale = 8080; + matrix = 8008; + prometheus = 9090; + prometheusNodeExporter = 9002; + prometheusSqlExporter = 9003; + + webUis = { + inherit + grafana + grafanaAlloy + prometheus + ; + }; +} diff --git a/nixos/hosts/tente/web-personal.nix b/nixos/hosts/tente/web-personal.nix index 109a87d..d706b9d 100644 --- a/nixos/hosts/tente/web-personal.nix +++ b/nixos/hosts/tente/web-personal.nix @@ -2,7 +2,7 @@ let cfg = config.web-personal; - helpers = import <top/helpers.nix> { inherit config; }; + helpers = import <top/helpers.nix> { inherit config lib pkgs; }; in { options.web-personal = { diff --git a/nixos/npins/default.nix b/nixos/npins/default.nix index 5e7d086..6592476 100644 --- a/nixos/npins/default.nix +++ b/nixos/npins/default.nix @@ -1,10 +1,53 @@ +/* + This file is provided under the MIT licence: + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ # Generated by npins. Do not modify; will be overwritten regularly let data = builtins.fromJSON (builtins.readFile ./sources.json); version = data.version; + # https://github.com/NixOS/nixpkgs/blob/0258808f5744ca980b9a1f24fe0b1e6f0fecee9c/lib/lists.nix#L295 + range = + first: last: if first > last then [ ] else builtins.genList (n: first + n) (last - first + 1); + + # https://github.com/NixOS/nixpkgs/blob/0258808f5744ca980b9a1f24fe0b1e6f0fecee9c/lib/strings.nix#L257 + stringToCharacters = s: map (p: builtins.substring p 1 s) (range 0 (builtins.stringLength s - 1)); + + # https://github.com/NixOS/nixpkgs/blob/0258808f5744ca980b9a1f24fe0b1e6f0fecee9c/lib/strings.nix#L269 + stringAsChars = f: s: concatStrings (map f (stringToCharacters s)); + concatMapStrings = f: list: concatStrings (map f list); + concatStrings = builtins.concatStringsSep ""; + + # If the environment variable NPINS_OVERRIDE_${name} is set, then use + # the path directly as opposed to the fetched source. + # (Taken from Niv for compatibility) + mayOverride = + name: path: + let + envVarName = "NPINS_OVERRIDE_${saneName}"; + saneName = stringAsChars (c: if (builtins.match "[a-zA-Z0-9]" c) == null then "_" else c) name; + ersatz = builtins.getEnv envVarName; + in + if ersatz == "" then + path + else + # this turns the string into an actual Nix path (for both absolute and + # relative paths) + builtins.trace "Overriding path of \"${name}\" with \"${ersatz}\" due to set \"${envVarName}\"" ( + if builtins.substring 0 1 ersatz == "/" then + /. + ersatz + else + /. + builtins.getEnv "PWD" + "/${ersatz}" + ); + mkSource = - spec: + name: spec: assert spec ? type; let path = @@ -16,16 +59,19 @@ let mkPyPiSource spec else if spec.type == "Channel" then mkChannelSource spec + else if spec.type == "Tarball" then + mkTarballSource spec else builtins.throw "Unknown source type ${spec.type}"; in - spec // { outPath = path; }; + spec // { outPath = mayOverride name path; }; mkGitSource = { repository, revision, url ? null, + submodules, hash, branch ? null, ... @@ -33,31 +79,39 @@ let assert repository ? type; # At the moment, either it is a plain git repository (which has an url), or it is a GitHub/GitLab repository # In the latter case, there we will always be an url to the tarball - if url != null then - (builtins.fetchTarball { + if url != null && !submodules then + builtins.fetchTarball { inherit url; sha256 = hash; # FIXME: check nix version & use SRI hashes - }) + } else - assert repository.type == "Git"; let + url = + if repository.type == "Git" then + repository.url + else if repository.type == "GitHub" then + "https://github.com/${repository.owner}/${repository.repo}.git" + else if repository.type == "GitLab" then + "${repository.server}/${repository.repo_path}.git" + else + throw "Unrecognized repository type ${repository.type}"; urlToName = url: rev: let - matched = builtins.match "^.*/([^/]*)(\\.git)?$" repository.url; + matched = builtins.match "^.*/([^/]*)(\\.git)?$" url; short = builtins.substring 0 7 rev; appendShort = if (builtins.match "[a-f0-9]*" rev) != null then "-${short}" else ""; in "${if matched == null then "source" else builtins.head matched}${appendShort}"; - name = urlToName repository.url revision; + name = urlToName url revision; in builtins.fetchGit { - url = repository.url; rev = revision; inherit name; # hash = hash; + inherit url submodules; }; mkPyPiSource = @@ -73,8 +127,20 @@ let inherit url; sha256 = hash; }; + + mkTarballSource = + { + url, + locked_url ? url, + hash, + ... + }: + builtins.fetchTarball { + url = locked_url; + sha256 = hash; + }; in -if version == 3 then - builtins.mapAttrs (_: mkSource) data.pins +if version == 5 then + builtins.mapAttrs mkSource data.pins else throw "Unsupported format version ${toString version} in sources.json. Try running `npins upgrade`" diff --git a/nixos/npins/sources.json b/nixos/npins/sources.json index 5d0d4d9..082a652 100644 --- a/nixos/npins/sources.json +++ b/nixos/npins/sources.json @@ -10,11 +10,27 @@ "pre_releases": false, "version_upper_bound": null, "release_prefix": null, + "submodules": false, "version": "0.15.0", "revision": "564595d0ad4be7277e07fa63b5a991b3c645655d", "url": "https://api.github.com/repos/ryantm/agenix/tarball/0.15.0", "hash": "01dhrghwa7zw93cybvx4gnrskqk97b004nfxgsys0736823956la" }, + "grafana-matrix-forwarder": { + "type": "GitRelease", + "repository": { + "type": "Git", + "url": "https://gitlab.com/hctrdev/grafana-matrix-forwarder.git" + }, + "pre_releases": false, + "version_upper_bound": null, + "release_prefix": null, + "submodules": false, + "version": "v0.8.3", + "revision": "a77a360503ea12c8244646753a3bf271428ddd02", + "url": null, + "hash": "1mdgv6jfbjs3xsqk9dxli6w345jws8j5qgzqf6wljmxlvibbic6y" + }, "my-geopos-link": { "type": "Git", "repository": { @@ -22,9 +38,22 @@ "url": "https://git.push-f.com/geopos.link" }, "branch": "master", - "revision": "ae67ef4f60656557d2408f3a4c2ba01959eef2c5", + "submodules": false, + "revision": "ab9198d989e0889816e510b66dad1548ce0cfb48", + "url": null, + "hash": "1xq5sl6rsxk8716ff8hghb3aihdp41ynfwaihllnzqghc6qpigjw" + }, + "my-lex-surf": { + "type": "Git", + "repository": { + "type": "Git", + "url": "https://git.push-f.com/lex-surf" + }, + "branch": "master", + "submodules": false, + "revision": "ef3318a78050ea8aa4f6a65dd4673958c0eadf85", "url": null, - "hash": "0r2m5q7zwxms9spbg12gq6q5pc5hz2x6i14qq5s2nxvvf4fsmnnv" + "hash": "0v1xmv3slsb965047wi968j3n58dzy227pc5y2g79g2zdppvx94d" }, "my-osm-proposals": { "type": "Git", @@ -33,9 +62,22 @@ "url": "https://git.push-f.com/osm-proposals" }, "branch": "master", - "revision": "2ee6afdf3356dea2aebdd48d20dfe9eb07037ba3", + "submodules": false, + "revision": "f6e5ec8c75b434b74d9e2ed2abdb36e92f7255a1", "url": null, - "hash": "14j48alysvx4n06my3kspsbplx23sv07nvswrya1x3swmwn4mnnc" + "hash": "1ycdi1dp4d20rbxbr51n4hmskbgwwb4xz243gxky1jmy5lixww3d" + }, + "my-rust-features": { + "type": "Git", + "repository": { + "type": "Git", + "url": "https://git.push-f.com/rust-features" + }, + "branch": "master", + "submodules": false, + "revision": "60c929acab8bfcd3fdc355288f1a72c1ed303f11", + "url": null, + "hash": "1vnwhgqqi8ihic6fz6grzikc1dra6myl2b2aly2fgzrcbzrd5390" }, "my-spec-pub": { "type": "Git", @@ -44,28 +86,56 @@ "url": "https://git.push-f.com/spec.pub" }, "branch": "master", - "revision": "65610a959cc75482001f14241eb521d7b18c8011", + "submodules": false, + "revision": "152c8021bef7310ce2501e23de12e892c3ed5032", + "url": null, + "hash": "1z83lhqzr7yx07qb3icz0pph64aij9a1z17rqxzqmyvjsyyn8nfd" + }, + "my-vdf": { + "type": "Git", + "repository": { + "type": "Git", + "url": "https://git.push-f.com/vdf" + }, + "branch": "master", + "submodules": false, + "revision": "ba6b72b4c7e257b9514ce21b7fbbc469e4c8a849", "url": null, - "hash": "0y9wz8kvjc3gv5qf8il5gh9wrx46vinaav1kq0yjiybibhpjskz7" + "hash": "0yrlxfzsi13zngr32zf6ca9fhh3z1lj3960rv2ipp51d09zvgwjq" }, "nixos": { "type": "Channel", - "name": "nixos-24.11", - "url": "https://releases.nixos.org/nixos/24.11/nixos-24.11.714826.04ef94c4c158/nixexprs.tar.xz", - "hash": "1xfpmwi69c50dbsr00r04v3czh6scccvdai2f5qhr0vi1p1sgvgz" + "name": "nixos-25.05", + "url": "https://releases.nixos.org/nixos/25.05/nixos-25.05.808129.3385ca0cd7e1/nixexprs.tar.xz", + "hash": "0fm3h18h23nz1zyqjii1dfv4az7lmicx7h07klsig6zz4pp9by6s" }, "nixos-small": { "type": "Channel", - "name": "nixos-24.11-small", - "url": "https://releases.nixos.org/nixos/24.11-small/nixos-24.11.714830.060b03c5d950/nixexprs.tar.xz", - "hash": "1lxm3y9jqkf8p63xrn6phyp5d763jfwcjxb3vpfqgc8iwq5z4cb9" + "name": "nixos-25.05-small", + "url": "https://releases.nixos.org/nixos/25.05-small/nixos-25.05.808273.5d35709e6ca2/nixexprs.tar.xz", + "hash": "1srzriwhnmhfgjwvb45ca2rk30kh68wfkh07s11idn8zyrr1qdly" }, "nixpkgs-unstable": { "type": "Channel", "name": "nixpkgs-unstable", - "url": "https://releases.nixos.org/nixpkgs/nixpkgs-25.05pre763845.de0fe301211c/nixexprs.tar.xz", - "hash": "1iq1a41c1c6i7lr8xpp2bwbm36v8qijisx1jv4126624swr9467l" + "url": "https://releases.nixos.org/nixpkgs/nixpkgs-25.11pre852399.4c202d26483c/nixexprs.tar.xz", + "hash": "0aaj59kn5q6n5cwq3i2a9sa89z0q71cy69kmpiy0kkn53hv3dxch" + }, + "prometheus-sql-exporter": { + "type": "GitRelease", + "repository": { + "type": "Git", + "url": "https://github.com/burningalchemist/sql_exporter" + }, + "pre_releases": false, + "version_upper_bound": null, + "release_prefix": null, + "submodules": false, + "version": "0.18.1", + "revision": "6e73ff71a8976939c28ce9a0ddbeefe0801d0a37", + "url": null, + "hash": "1a3j3rsjvw6wqyayml5x5c7x18bvn45ldr2p280yn9pqdzihk480" } }, - "version": 3 -}
\ No newline at end of file + "version": 5 +} diff --git a/nixos/profiles/common/basics.nix b/nixos/profiles/common/basics.nix index aa6eff9..3f1859c 100644 --- a/nixos/profiles/common/basics.nix +++ b/nixos/profiles/common/basics.nix @@ -1,9 +1,10 @@ -{ config, pkgs, ... }: +{ lib, config, pkgs, ... }: { environment.systemPackages = with pkgs; [ git - vim + # lowPrio because we use vim-full in the workstation profile + (lowPrio vim) file htop @@ -32,4 +33,7 @@ ZDOTDIR = "${config}/zsh"; }; + + # By default installs plugins that pull in GTK. + networking.networkmanager.plugins = lib.mkForce []; } diff --git a/nixos/profiles/common/default.nix b/nixos/profiles/common/default.nix index 2b07724..a508a32 100644 --- a/nixos/profiles/common/default.nix +++ b/nixos/profiles/common/default.nix @@ -1,7 +1,7 @@ { config, pkgs, ... }: let - sources = import ../../npins; + sources = import <top/npins>; in { imports = [ diff --git a/nixos/profiles/common/nixpkgs/overlays.nix b/nixos/profiles/common/nixpkgs/overlays.nix index 613338f..40f9041 100644 --- a/nixos/profiles/common/nixpkgs/overlays.nix +++ b/nixos/profiles/common/nixpkgs/overlays.nix @@ -1,4 +1,29 @@ # https://nixos.org/manual/nixpkgs/stable/#sec-overlays-definition +{ pkgs, ... }: [ + # features + (final: prev: { + scc = prev.scc.overrideAttrs (old: { + # https://github.com/boyter/scc/pull/622 + src = pkgs.fetchFromGitHub { + owner = "boyter"; + repo = "scc"; + rev = "b73ea06bdc5890821d03502a2cfc4224b19a9b67"; + hash = "sha256-vcuoKrvludBE0KpXVLkKzB38n0mZJWVB8bYrgJDHKfY="; + }; + }); + }) + # bug fixes + (final: prev: { + sway-unwrapped = prev.sway-unwrapped.overrideAttrs (old: { + patches = old.patches ++ [ + # https://github.com/swaywm/sway/pull/8761 + (pkgs.fetchpatch { + url = "https://github.com/swaywm/sway/commit/af7c6ec7b1daeeec67dd17e27fb75f1f1c347327.patch"; + hash = "sha256-DxN/3IdswZ6q/ksBVr+wgwGe7ScJeg8gFHYQdQAueww="; + }) + ]; + }); + }) ] diff --git a/nixos/profiles/common/sanix.nix b/nixos/profiles/common/sanix.nix index a20db24..626aa73 100644 --- a/nixos/profiles/common/sanix.nix +++ b/nixos/profiles/common/sanix.nix @@ -2,14 +2,13 @@ # Use the Nixpkgs config and overlays from the local files for this NixOS build nixpkgs = { config = import ./nixpkgs/config.nix; - overlays = import ./nixpkgs/overlays.nix; + overlays = import ./nixpkgs/overlays.nix { inherit pkgs; }; }; # Makes commands default to the same Nixpkgs, config, overlays and NixOS configuration nix.nixPath = [ "nixpkgs=${pkgs.path}" "nixos-config=${toString <nixos-config>}" - "nixpkgs-overlays=${toString ./nixpkgs/overlays.nix}" ]; environment.variables.NIXPKGS_CONFIG = lib.mkForce (toString ./nixpkgs/config.nix); diff --git a/nixos/profiles/server/default.nix b/nixos/profiles/server/default.nix index ac24e20..07dcd4b 100644 --- a/nixos/profiles/server/default.nix +++ b/nixos/profiles/server/default.nix @@ -13,4 +13,9 @@ }; }; }; + + security.sudo.wheelNeedsPassword = false; + + # For remote rebuilds with --target-host. + nix.settings.trusted-users = ["@wheel"]; } diff --git a/nixos/profiles/workstation/create.nix b/nixos/profiles/workstation/create.nix index 924717f..1c955a6 100644 --- a/nixos/profiles/workstation/create.nix +++ b/nixos/profiles/workstation/create.nix @@ -4,8 +4,14 @@ environment.systemPackages = with pkgs; [ graphviz-nox # for dot + hugo typst + (inkscape-with-extensions.override { + inkscapeExtensions = [ + inkscape-extensions.inkstitch + ]; + }) gimp krita ]; diff --git a/nixos/profiles/workstation/default.nix b/nixos/profiles/workstation/default.nix index 0332200..9df5328 100644 --- a/nixos/profiles/workstation/default.nix +++ b/nixos/profiles/workstation/default.nix @@ -2,13 +2,7 @@ let cfg = config.workstation; - sources = import ../../npins; - nixGit = import (pkgs.fetchFromGitHub { - owner = "NixOS"; - repo = "nix"; - rev = "7a8a28629c61c75af010ff0a5a88c16c4ce536c7"; - sha256 = "sha256-oqG9AFPXBneKVmiWa9b9ai0hGZqHVKVFaFLdBZitSUA="; - }); + sources = import <top/npins>; in { options.workstation = { @@ -33,6 +27,7 @@ in "wheel" "networkmanager" "dialout" + "wireshark" ]; }; @@ -44,6 +39,11 @@ in environment.systemPackages = with pkgs; [ npins (callPackage "${sources.agenix}/pkgs/agenix.nix" {}) + (callPackage sources.my-vdf {}) + + vim-full + skim + (writeShellScriptBin "ed" (builtins.readFile ./scripts/ed)) unzip @@ -60,9 +60,5 @@ in # Age defaults to this anyway when openssh is enabled. # We're setting this here for workstations where openssh is disabled. age.identityPaths = ["/etc/ssh/ssh_host_ed25519_key"]; - - # Using nix from git for --raw support in nix-instantiate --eval. - # FUTURE: remove once upgrading to NixOS 25.05 - nix.package = nixGit.packages.${builtins.currentSystem}.default; }; } diff --git a/nixos/profiles/workstation/dev.nix b/nixos/profiles/workstation/dev.nix index e550a4c..a40d574 100644 --- a/nixos/profiles/workstation/dev.nix +++ b/nixos/profiles/workstation/dev.nix @@ -4,16 +4,19 @@ environment.systemPackages = with pkgs; [ rustup go + golangci-lint python313 nodejs_22 # CLI tools + skim docker-compose gnumake jq + pkgs-unstable.jujutsu just sqlite-interactive - tokei + scc fastmod license-generator @@ -22,12 +25,21 @@ gcc # rustc fails if cc linker isn't found chromium - pkgs-unstable.zed-editor + zed-editor vscodium # I'm installing extensions via my install-imperative script. platformio + + # language servers + # rust-analyzer is installed via rustup + nixd ]; + programs.wireshark = { + enable = true; + package = pkgs.wireshark; + }; + virtualisation.podman = { enable = true; dockerSocket.enable = true; diff --git a/nixos/profiles/workstation/graphical.nix b/nixos/profiles/workstation/graphical.nix index dc05f05..9e39092 100644 --- a/nixos/profiles/workstation/graphical.nix +++ b/nixos/profiles/workstation/graphical.nix @@ -32,7 +32,9 @@ # graphical tools imv - mpv + (mpv.override { + youtubeSupport = false; # disable yt-dlp dependency + }) zathura # TODO: zathura doesn't adjust zoom on PDF open even though adjust-open="best-fit" is the default according to zathurarc(5) element-desktop @@ -67,6 +69,7 @@ pulse.enable = true; }; security.rtkit.enable = true; # recommended for PipeWire + hardware.bluetooth.enable = true; programs.thunderbird = { enable = true; diff --git a/nixos/profiles/workstation/scripts/ed b/nixos/profiles/workstation/scripts/ed new file mode 100644 index 0000000..a3dd433 --- /dev/null +++ b/nixos/profiles/workstation/scripts/ed @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +$VISUAL $(find . -not -path "*/.*" | sk --no-info "$@") diff --git a/nixos/rebuild b/nixos/rebuild index 80a52cd..1bc6c39 100755 --- a/nixos/rebuild +++ b/nixos/rebuild @@ -11,19 +11,16 @@ if [ ! -f $configPath ]; then exit 1 fi -firstLine=$(head -n1 "$configPath") - -if ! echo "$firstLine" | grep -E ^"# channel *=" > /dev/null; then - echo "aborting: $configPath doesn't start with \`# channel=\"...\"\`, where ... is a pin from sources.json" - exit 1 -fi - nix-eval() { - nix-instantiate --eval --read-write-mode "$@" \ - | tr -d \" # nix-instantiate has no raw output yet (like the experimental nix eval) + nix-instantiate --eval --raw --read-write-mode "$@" } -channel=$(nix-eval --argstr line "$(echo $firstLine | tr -d \#)" --expr '{line}: (builtins.fromTOML line).channel') +channel=$(nix-eval --argstr host "$HOSTNAME" --expr '{host}: +let + toml = builtins.readFile ./hosts/${host}/metadata.toml; +in + (builtins.fromTOML toml).channel +') nixpkgsPath=$(nix-eval --argstr channel "$channel" --expr '{channel}: (import ./npins).${channel}.outPath') # nixos-rebuild always reads Nixpkgs from the NIX_PATH, diff --git a/nixos/secrets/grafana-matrix-forwarder-env.age b/nixos/secrets/grafana-matrix-forwarder-env.age Binary files differnew file mode 100644 index 0000000..84cab14 --- /dev/null +++ b/nixos/secrets/grafana-matrix-forwarder-env.age diff --git a/nixos/secrets/miniflux-admin.age b/nixos/secrets/miniflux-admin.age new file mode 100644 index 0000000..b656b07 --- /dev/null +++ b/nixos/secrets/miniflux-admin.age @@ -0,0 +1,9 @@ +age-encryption.org/v1 +-> ssh-ed25519 PMTW+A CIjI5374KCn9hZRM6vj4PTcUj4FzHan/buPBD5gQmw8 +6mXVcux6zZQ16U1y3bipsKD4y1QiMQ498IQAzpDJqt0 +-> ssh-ed25519 PHC5tQ IQiEl8meNDHIE0HI2SErDxFDVSEd/TCRH5PplnIC9H4 +mq/N57ex2NpbptzaMpAfYJ1Y/exIj1+J2X9CRW28aOg +-> e#-grease TdfbQVa$ L X4("^:7K V&L1z +cHZi1Yyb7tz7gw9zkPqbbhEZeGVIncPZfTpIvch0e/wDCIjMxY7e2Pb6nOVDxQ +--- b32AUhCBWDVXlj4pUbDW91Y80nQ5Wp9zTy+rl6vnFHU +A5'`eYZ%Fdk*&8IBe@Aa[/{eEu<)>ItMY};b
\ No newline at end of file diff --git a/nixos/secrets/secrets.nix b/nixos/secrets/secrets.nix index db19967..f1c8ab8 100644 --- a/nixos/secrets/secrets.nix +++ b/nixos/secrets/secrets.nix @@ -1,10 +1,12 @@ let - martin = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICRBAAt77GXrDtIp6fSjeMHCV3e1ujCE0meetqX3YZpn"; + martin = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIF+moAzcnDJsyUalRVdLeJS1D5wezwMDyHuM+Cyk1nQh"; hamac = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJmjbC0gk2s/qDQ+QR//GJH0ZPld99L0EtX7dPP5h2RN"; ev = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINCSypbTOnAYBO32vUUieOsb6ws32gCsDg8nB8JhuFuI"; + tente = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGTyZdGXxkAjzRulRFB23AX0T/lVLZOaSaFJi+thsjgx"; in { "vpn-se-privKey.age".publicKeys = [ martin hamac ev ]; - "vpn-se-presharedKey.age".publicKeys = [ martin hamac ev ]; + "grafana-matrix-forwarder-env.age".publicKeys = [ martin tente ]; + "miniflux-admin.age".publicKeys = [ martin ev ]; } diff --git a/nixos/secrets/vpn-se-presharedKey.age b/nixos/secrets/vpn-se-presharedKey.age deleted file mode 100644 index 1f56d86..0000000 --- a/nixos/secrets/vpn-se-presharedKey.age +++ /dev/null @@ -1,14 +0,0 @@ -age-encryption.org/v1 --> ssh-ed25519 0iFcGg 33qVwdJ+x5d9ezpvYayjZqrwAZQDb7AxvOjQucyVYgQ -z5/tRvXEE+xSJ4BKQORu4yI+UG2GNaKdTZe0FkN4VyY --> ssh-ed25519 zNg/mg 9rug5AzVUH/fIDvtSVA0SZkQ0tR+T59VY2UYPrxZfFM -g49jKdkf+tz/sl9g/RdfkRv/CKneO04rkXPprQYv9rw --> ssh-ed25519 PHC5tQ H9j903SKpztlrUN/nP7Q8Io/iJLY8ka6aBlOc4d/+iA -hfQIpLuvT2D8eo5T0MmydivcQE7DFbHhLO809YET0HI --> W}8>=-grease -hy4mn+TGe6QPbZud+cppGmgzyed8SgVcaYBumJdxIRy6NQXW8PH2itg6GTIR6Npb -Nhj9zeSsQvAq8no7z+Q5DsXI7o6iVUDgvoQ1HcUan7WYGqR5MA0 ---- WbaGYSFVrJ8+YgzYeLaLXfOsCDMMruUpmFtKvtym57E -Ik"a#L!1\: -=.+$@].Hnd]U/q-~-T -
\ No newline at end of file diff --git a/nixos/secrets/vpn-se-privKey.age b/nixos/secrets/vpn-se-privKey.age index 93de475..ca2a8eb 100644 --- a/nixos/secrets/vpn-se-privKey.age +++ b/nixos/secrets/vpn-se-privKey.age @@ -1,11 +1,12 @@ age-encryption.org/v1 --> ssh-ed25519 0iFcGg KyKfFePFWpX9AOPw5Sy9UQkucPsQDwrEwRL66e3wdA4 -7DNSkNroD1HaRld0M5uMWtu7dojKUB7DPU9hdTPCXZ8 --> ssh-ed25519 zNg/mg Y0w7MCgwQKqb8FurFOyEshSmfCNoIKi0OnOJUsDeh14 -xILssj5y6XbZ10b39MqqhN42DRQt1AKIdh/Eidin8dA --> ssh-ed25519 PHC5tQ nfW6lDN4vrv5EOCZGmfe9LEto5FDbU9Vh1LOvrnpvR0 -KrGBpZh7+DalPFoM0rW6ylehDnrmCz2JAOKqMEN4BoQ --> k4]AeAV-grease pSemvkw @C;y -vBW8ETA ---- 8Eel9tqXmZ3s7J1CqXlCMTOPAHWD/ftxB7t2DRtHi4A -T{vgmWe 5Yxr嶘v} ]MCMbHAZ?\Hl'L,
\ No newline at end of file +-> ssh-ed25519 PMTW+A WNGr/0aVmRRmhZ6P6PaWxPML3VRBi59QTD5kQ2AuqXk +6ivhC1Cm/DVWdCxx+48U0YzJqhj0rmZ195SmjEX+Y4k +-> ssh-ed25519 zNg/mg I4MbADHlAgwced5uYaEWXNgKvzfyGxKAWiXtg7rgRCg +ipQGQn7rCu59gHq5DjQIckvqGO5P/LJTtSP8yViEVdQ +-> ssh-ed25519 PHC5tQ xsy2ivym+ymHacFJeTwll7aJyypoQTg8h/DyaVp98xs +rwPb2fCAtHKzUQgZ/0SxKZ37MnVb8u74vesd0o5G7yc +-> @n>>$-grease @J3N{1sN qN_Oh]I K(# +dA +--- P0ZI10bTAqn+OPH84hF1V+Qa/X7gqvMZHkYAZQg5zgo +v:LՓQ "[YU +KY?+$Aa?`o[k369U0Ȯ8PH[H*g`w
\ No newline at end of file diff --git a/nixos/shared/alloy-nix-config/alloy_nix_config.go b/nixos/shared/alloy-nix-config/alloy_nix_config.go new file mode 100644 index 0000000..4b6eb63 --- /dev/null +++ b/nixos/shared/alloy-nix-config/alloy_nix_config.go @@ -0,0 +1,129 @@ +package main + +import ( + "encoding/json" + "fmt" + "maps" + "os" + "slices" + "strconv" + "strings" +) + +func main() { + if len(os.Args) != 3 { + fmt.Fprintf(os.Stderr, "usage: %s <json_path> <out_path>\n", os.Args[0]) + os.Exit(1) + } + + jsonPath := os.Args[1] + outPath := os.Args[2] + + jsonData, err := os.ReadFile(jsonPath) + if err != nil { + fmt.Fprintf(os.Stderr, "error reading file %s: %v\n", jsonPath, err) + os.Exit(1) + } + + // It would be nice to preserve the order of blocks ... except that we can't + // because Nix already doesn't preserve the order of attribute sets. + var config map[string]any + if err := json.Unmarshal(jsonData, &config); err != nil { + fmt.Fprintf(os.Stderr, "error parsing JSON: %v\n", err) + os.Exit(1) + } + + result := formatConfig(config) + + if err := os.WriteFile(outPath, []byte(result), 0644); err != nil { + fmt.Fprintf(os.Stderr, "error writing file %s: %v\n", outPath, err) + os.Exit(1) + } +} + +func formatConfig(config map[string]any) string { + var s strings.Builder + + for _, blockName := range slices.Sorted(maps.Keys(config)) { + labels := config[blockName] + + if labelsMap, ok := labels.(map[string]any); ok { + for label, block := range labelsMap { + if blockMap, ok := block.(map[string]any); ok { + s.WriteString(formatBlock(blockName, label, blockMap, 0)) + } + } + } + } + + return s.String() +} + +func formatBlock(blockName string, label string, block map[string]any, indent int) string { + var s strings.Builder + + s.WriteString(strings.Repeat(" ", indent)) + s.WriteString(blockName) + if label != "" { + s.WriteString(fmt.Sprintf(` %s`, strconv.Quote(label))) + } + s.WriteString(" {\n") + + var blocks []any + if blocksValue, exists := block["blocks"]; exists { + if blocksList, ok := blocksValue.([]any); ok { + blocks = blocksList + } + delete(block, "blocks") + } + + for _, key := range slices.Sorted(maps.Keys(block)) { + s.WriteString(strings.Repeat(" ", indent+1)) + s.WriteString(fmt.Sprintf("%s = %s\n", key, formatValue(block[key]))) + } + + for _, blockItem := range blocks { + if blockMap, ok := blockItem.(map[string]any); ok { + var name string + if nameValue, exists := blockMap["name"]; exists { + if nameStr, ok := nameValue.(string); ok { + name = nameStr + } + delete(blockMap, "name") + } + + s.WriteString(formatBlock(name, "", blockMap, indent+1)) + } + } + + s.WriteString(strings.Repeat(" ", indent)) + s.WriteString("}\n") + + return s.String() +} + +func formatValue(value any) string { + switch v := value.(type) { + case string: + return strconv.Quote(v) + case map[string]any: + if ref, exists := v["$ref"]; exists { + if refStr, ok := ref.(string); ok { + return refStr + } + } + var parts []string + for _, name := range slices.Sorted(maps.Keys(v)) { + parts = append(parts, fmt.Sprintf("%s=%s,", name, formatValue(v[name]))) + } + return "{" + strings.Join(parts, " ") + "}" + case []any: + var parts []string + for _, item := range v { + parts = append(parts, formatValue(item)) + } + return "[" + strings.Join(parts, ", ") + "]" + default: + return fmt.Sprintf("%v", v) + } +} diff --git a/nixos/shared/alloy-nix-config/default.nix b/nixos/shared/alloy-nix-config/default.nix new file mode 100644 index 0000000..d4efe02 --- /dev/null +++ b/nixos/shared/alloy-nix-config/default.nix @@ -0,0 +1,8 @@ +{ pkgs ? import <nixpkgs> {} }: + +pkgs.buildGoModule { + pname = "alloy-nix-config"; + version = "git"; + src = ./.; + vendorHash = null; +} diff --git a/nixos/shared/alloy-nix-config/go.mod b/nixos/shared/alloy-nix-config/go.mod new file mode 100644 index 0000000..2916089 --- /dev/null +++ b/nixos/shared/alloy-nix-config/go.mod @@ -0,0 +1,3 @@ +module push-f.com/alloy-nix-config + +go 1.24.5 diff --git a/nixos/shared/grafana-matrix-forwarder/default.nix b/nixos/shared/grafana-matrix-forwarder/default.nix new file mode 100644 index 0000000..7a04dcb --- /dev/null +++ b/nixos/shared/grafana-matrix-forwarder/default.nix @@ -0,0 +1,10 @@ +{ buildGoModule }: +let + sources = import <top/npins>; +in +buildGoModule { + pname = "grafana-matrix-forwarder"; + version = sources.grafana-matrix-forwarder.version; + src = sources.grafana-matrix-forwarder; + vendorHash = "sha256-ifkeakyRkIF2Y/4otUWhTvUzsPwRb1Wxx6gqN0806c4="; +} diff --git a/nixos/shared/grafana-matrix-forwarder/service.nix b/nixos/shared/grafana-matrix-forwarder/service.nix new file mode 100644 index 0000000..5ad511c --- /dev/null +++ b/nixos/shared/grafana-matrix-forwarder/service.nix @@ -0,0 +1,31 @@ +{ config, lib, pkgs, ... }: + +let + grafanaMatrixForwarder = pkgs.callPackage ./default.nix {}; + cfg = config.services.grafana-matrix-forwarder; +in +{ + options.services.grafana-matrix-forwarder = { + enable = lib.mkEnableOption "grafana-matrix-forwarder"; + port = lib.mkOption { + type = lib.types.int; + }; + homeserver = lib.mkOption { + type = lib.types.str; + }; + environmentFile = lib.mkOption { + type = lib.types.path; + }; + }; + + config = lib.mkIf cfg.enable { + systemd.services.grafana-matrix-forwarder = { + serviceConfig = { + ExecStart = "${grafanaMatrixForwarder}/bin/grafana-matrix-forwarder --port=${toString cfg.port} --homeserver ${cfg.homeserver}"; + EnvironmentFile = cfg.environmentFile; + DynamicUser = "true"; + }; + wantedBy = ["multi-user.target"]; + }; + }; +} diff --git a/nixos/shared/monitoring.nix b/nixos/shared/monitoring.nix new file mode 100644 index 0000000..eecad59 --- /dev/null +++ b/nixos/shared/monitoring.nix @@ -0,0 +1,235 @@ +{ config, lib, pkgs, ... }: + +let + cfg = config.monitoring; + helpers = import <top/helpers.nix> { inherit config lib pkgs; }; +in +{ + options.monitoring = { + lokiPort = lib.mkOption { + type = lib.types.int; + }; + alloyUiPort = lib.mkOption { + type = lib.types.int; + }; + prometheusPort = lib.mkOption { + type = lib.types.int; + }; + prometheusNodeExporterPort = lib.mkOption { + type = lib.types.int; + }; + prometheusScrapeConfigs = lib.mkOption { + type = lib.types.listOf lib.types.attrs; + default = []; + }; + }; + config = { + services.prometheus = { + enable = true; + port = cfg.prometheusPort; + + retentionTime = "1y"; + + scrapeConfigs = [ + { + job_name = "node"; + static_configs = [{ + targets = [ "localhost:${toString cfg.prometheusNodeExporterPort}" ]; + }]; + } + ] ++ cfg.prometheusScrapeConfigs; + + exporters.node = { + enable = true; + enabledCollectors = [ "systemd" ]; + port = cfg.prometheusNodeExporterPort; + }; + }; + + services.loki = { + enable = true; + configuration = { + server.http_listen_port = cfg.lokiPort; + auth_enabled = false; + + ingester = { + lifecycler = { + address = "127.0.0.1"; + ring = { + kvstore.store = "inmemory"; + replication_factor = 1; + }; + }; + }; + + schema_config = { + configs = [{ + store = "tsdb"; + object_store = "filesystem"; + schema = "v13"; + index = { + prefix = "index_"; + period = "24h"; + }; + }]; + }; + + storage_config = { + tsdb_shipper = { + active_index_directory = "/var/lib/loki/tsdb-active"; + cache_location = "/var/lib/loki/tsdb-cache"; + }; + }; + + compactor = { + working_directory = "/var/lib/loki"; + }; + + limits_config = { + allow_structured_metadata = true; + }; + }; + }; + + systemd.services.alloy = { + serviceConfig = { + SupplementaryGroups = [ + "systemd-journal" + ] ++ lib.optional config.services.nginx.enable config.services.nginx.group; + }; + }; + + services.alloy = { + enable = true; + extraFlags = ["--server.http.listen-addr=0.0.0.0:${toString cfg.alloyUiPort}"]; + configPath = + let + ref = helpers.alloyConfigRef; + in + helpers.writeAlloyConfig { + "loki.source.journal".journal = { + max_age = "12h0m0s"; + relabel_rules = ref "discovery.relabel.journal.rules"; + forward_to = [(ref "loki.process.journal.receiver")]; + labels = { + host = "tente"; + job = "systemd-journal"; + }; + }; + "loki.process".journal = { + forward_to = [(ref "loki.write.default.receiver")]; + blocks = [ + { + name = "stage.match"; + # Select messages from systemd services that have LogExtraFields=LOG_FORMAT=logfmt. + selector = ''{__journal_LOG_FORMAT="logfmt"}''; + blocks = [ + { name = "stage.logfmt"; mapping = { time = ""; level = ""; }; } + { name = "stage.timestamp"; source = "time"; format = "RFC3339"; } + { + # The slog package of the Go standard library prints levels as uppercase. + name = "stage.template"; + source = "level"; + template = "{{ ToLower .Value }}"; + } + { name = "stage.structured_metadata"; values = { level = ""; }; } + ]; + } + ]; + }; + "discovery.relabel".journal = { + targets = []; + blocks = [ + { + name = "rule"; + source_labels = ["__journal__systemd_unit"]; + target_label = "unit"; + } + ]; + }; + + "loki.source.file".nginx_access = { + targets = ref "local.file_match.nginx_access.targets"; + forward_to = [(ref "loki.process.nginx_access.receiver")]; + }; + "local.file_match".nginx_access = { + path_targets = [{ + __path__ = "/var/log/nginx/*.access.log"; + }]; + }; + "loki.process".nginx_access = { + forward_to = [(ref "loki.write.default.receiver")]; + blocks = [ + { name = "stage.static_labels"; values = { job = "nginx"; }; } + + { + # Extracting the log file name as vhost because it's more convenient + # to query for than the full filename. We could also use server_name + # but there could be wildcard server_names and Loki labels should have + # a low cardinality for performance reasons. + name = "stage.regex"; + source = "filename"; + expression = "(?P<vhost>[^/]+)\\.access\\.log$"; + } + + { name = "stage.labels"; values = { vhost = ""; }; } + { name = "stage.json"; expressions = { msec = ""; path = ""; }; } + { name = "stage.timestamp"; source = "msec"; format = "Unix"; } + { + # Setting level=info to prevent Loki's log level detection from wrongly + # detecting messages with paths containing "error" as errors. + # Creating the filetype entry via stage.template because there's no + # static_structured_metadata stage yet. (https://github.com/grafana/loki/issues/16703) + name = "stage.template"; + source = "level"; + template = "info"; + } + { name = "stage.structured_metadata"; values = { level = ""; }; } + + # Temporarily adding path as a label so that we can use it in the match selectors. + { name = "stage.labels"; values = { path = ""; }; } + { + name = "stage.match"; + selector = "{path=~\"/\\\\.well-known/.*\"}"; + # Creating the filetype entry via stage.template because there's no + # static_structured_metadata stage yet. (https://github.com/grafana/loki/issues/16703) + blocks = [ + { name = "stage.template"; source = "filetype"; template = "well-known"; } + ]; + } + { + name = "stage.match"; + selector = "{path=\"/robots.txt\"}"; + blocks = [ + { name = "stage.template"; source = "filetype"; template = "robots.txt"; } + ]; + } + { + name = "stage.match"; + selector = "{path=~\".*\\\\.atom$\"}"; + blocks = [ + { name = "stage.template"; source = "filetype"; template = "feed"; } + ]; + } + { + name = "stage.structured_metadata"; + values = { filetype = ""; }; + } + + # Dropping path again because it has a too high cardinality for a label. + { name = "stage.label_drop"; values = ["path"]; } + ]; + }; + "loki.write".default = { + blocks = [ + { + name = "endpoint"; + url = "http://127.0.0.1:${toString cfg.lokiPort}/loki/api/v1/push"; + } + ]; + external_labels = {}; + }; + }; + }; + }; +} diff --git a/nixos/shared/postgres-collectors.nix b/nixos/shared/postgres-collectors.nix new file mode 100644 index 0000000..fd44cb0 --- /dev/null +++ b/nixos/shared/postgres-collectors.nix @@ -0,0 +1,22 @@ +# collectors for the prometheus-sql-exporter +{ config, lib, pkgs, ... }: + +{ + collectorNames = ["db-sizes"]; + + collectors = [ + { + collector_name = "db-sizes"; + metrics = [ + { + metric_name = "pg_db_size_bytes"; + help = "disk space used by the database"; + type = "gauge"; + key_labels = ["database_name"]; + values = ["size"]; + query = "SELECT datname AS database_name, pg_database_size(datname) as size from pg_database"; + } + ]; + } + ]; +} diff --git a/nixos/shared/prometheus-sql-exporter/default.nix b/nixos/shared/prometheus-sql-exporter/default.nix index 81f1660..5d80a62 100644 --- a/nixos/shared/prometheus-sql-exporter/default.nix +++ b/nixos/shared/prometheus-sql-exporter/default.nix @@ -1,21 +1,15 @@ { lib, buildGoModule, - fetchFromGitHub, }: - -buildGoModule rec { +let + sources = import <top/npins>; +in +buildGoModule { pname = "sql_exporter"; - version = "0.17.1"; - - src = fetchFromGitHub { - owner = "burningalchemist"; - repo = pname; - rev = version; - sha256 = "sha256-AEPFXPplHtny1P3gMvB1gbMj10bpu9PXc6ywliF+dCc="; - }; - - vendorHash = "sha256-KFWDqbdbXvgEtz1nlasWrvIckpzasUdzbb+AKfXmYf8="; + version = sources.prometheus-sql-exporter.version; + src = sources.prometheus-sql-exporter; + vendorHash = "sha256-eZxxmqoiXPdjZs/lwbzvWco9mDFy0zmpGDcqTIyWbK4="; meta = with lib; { description = "Database-agnostic SQL exporter for Prometheus"; diff --git a/nixos/shared/prometheus-sql-exporter/service.nix b/nixos/shared/prometheus-sql-exporter/service.nix index a887f91..a79528c 100644 --- a/nixos/shared/prometheus-sql-exporter/service.nix +++ b/nixos/shared/prometheus-sql-exporter/service.nix @@ -4,7 +4,16 @@ let sqlExporter = pkgs.callPackage ./default.nix {}; cfg = config.services.prometheus-sql-exporter; - configFile = builtins.toFile "config.yaml" (builtins.toJSON cfg.config); + configFile = builtins.toFile "config.yaml" (builtins.toJSON cfg.config); + validateConfig = file: + pkgs.runCommand "validate-config" + { + nativeBuildInputs = [sqlExporter]; + } + '' + sql_exporter -config.check -config.file "${file}" + ln -s "${file}" "$out" + ''; in { options.services.prometheus-sql-exporter = { @@ -20,7 +29,7 @@ in config = lib.mkIf cfg.enable { systemd.services.prometheus-sql-exporter = { serviceConfig = { - ExecStart = "${sqlExporter}/bin/sql_exporter -config.file ${configFile} -web.listen-address :${toString cfg.port}"; + ExecStart = "${sqlExporter}/bin/sql_exporter -config.file ${validateConfig configFile} -web.listen-address :${toString cfg.port}"; DynamicUser = "true"; User = "prometheus-sql-exporter"; }; diff --git a/nixos/shared/vpn.nix b/nixos/shared/vpn.nix index 59fb225..9cbcf45 100644 --- a/nixos/shared/vpn.nix +++ b/nixos/shared/vpn.nix @@ -2,7 +2,6 @@ { age.secrets.vpn-se-privKey.file = ../secrets/vpn-se-privKey.age; - age.secrets.vpn-se-presharedKey.file = ../secrets/vpn-se-presharedKey.age; # We're creating the wireguard interfaces in network namespaces so that # we can use them on demand: @@ -14,15 +13,14 @@ interfaces.wg-se = { interfaceNamespace = "se"; - ips = ["10.148.171.71/32"]; + ips = ["10.128.241.130/32"]; privateKeyFile = config.age.secrets.vpn-se-privKey.path; peers = [ { - publicKey = "PyLCXAQT8KkM4T+dUsOQfn+Ub3pGxfGlxkIApuig+hk="; - presharedKeyFile = config.age.secrets.vpn-se-presharedKey.path; + publicKey = "sb61ho9MhaxhJd6WSrryVmknq0r6oHEW7PP5i4lzAgM="; allowedIPs = ["0.0.0.0/0"]; - endpoint = "se3.vpn.airdns.org:1637"; + endpoint = "se.gw.xeovo.com:51820"; } ]; }; diff --git a/user/inkscape/pages.csv b/user/inkscape/pages.csv new file mode 100644 index 0000000..75f8696 --- /dev/null +++ b/user/inkscape/pages.csv @@ -0,0 +1,47 @@ +#Inkscape page sizes +#NAME, WIDTH, HEIGHT, UNIT +Big embroidery hoop, 200, 280, mm +Medium embroidery hoop, 200, 200, mm +Small embroidery hoop, 140, 200, mm +A4, 210, 297, mm +A0, 841, 1189, mm +A1, 594, 841, mm +A2, 420, 594, mm +A3, 297, 420, mm +A5, 148, 210, mm +A6, 105, 148, mm +A7, 74, 105, mm +A8, 52, 74, mm +A9, 37, 52, mm +A10, 26, 37, mm +B0, 1000, 1414, mm +B1, 707, 1000, mm +B2, 500, 707, mm +B3, 353, 500, mm +B4, 250, 353, mm +B5, 176, 250, mm +B6, 125, 176, mm +B7, 88, 125, mm +B8, 62, 88, mm +B9, 44, 62, mm +B10, 31, 44, mm +C0, 917, 1297, mm +C1, 648, 917, mm +C2, 458, 648, mm +C3, 324, 458, mm +C4, 229, 324, mm +C5, 162, 229, mm +C6, 114, 162, mm +C7, 81, 114, mm +C8, 57, 81, mm +C9, 40, 57, mm +C10, 28, 40, mm +D1, 545, 771, mm +D2, 385, 545, mm +D3, 272, 385, mm +D4, 192, 272, mm +D5, 136, 192, mm +D6, 96, 136, mm +D7, 68, 96, mm +ID Card (ISO 7810), 85.60, 53.98, mm +Business Card (Europe), 85, 55, mm diff --git a/user/jj/config.toml b/user/jj/config.toml new file mode 100644 index 0000000..72e8c4e --- /dev/null +++ b/user/jj/config.toml @@ -0,0 +1,8 @@ +"$schema" = "https://jj-vcs.github.io/jj/latest/config-schema.json" + +[user] +name = "Martin Fischer" +email = "martin@push-f.com" + +[ui] +pager = ["less", "--quit-if-one-screen", "--RAW-CONTROL-CHARS"] diff --git a/user/sway/config b/user/sway/config index 2fafff8..3b65353 100644 --- a/user/sway/config +++ b/user/sway/config @@ -18,7 +18,7 @@ set $term foot set $menu wmenu-run ### Output configuration -exec_always <~/repos/pad/roadmap.md sed 's/(.\+)//' | set-wallpaper +exec_always <~/repos/notes/roadmap.md sed 's/(.\+)//' | set-wallpaper # Example configuration: # @@ -57,6 +57,13 @@ input "type:keyboard" { xkb_file "~/config/colematik/colematik.xkb" } +input "10429:2395:UGTABLET_Artist_Pro_16_(Gen2)" { + map_to_output "DP-1" +} +input "10429:514:Hanvon_Ugee_Shortcut_Remote_Keyboard" { + xkb_layout "us" +} + ### Key bindings # # Basics: @@ -82,6 +89,8 @@ input "type:keyboard" { # Exit sway (logs you out of your Wayland session) bindsym $mod+Shift+q exec swaynag -t warning -m 'You pressed the exit shortcut. Do you really want to exit sway? This will end your Wayland session.' -B 'Yes, exit sway' 'swaymsg exit' + + bindsym $mod+m exec foot zsh -c 'cd ~/repos/notes && ed --min-query-length 1' # # Moving around: # diff --git a/user/vim/vimrc b/user/vim/vimrc index b65deb0..952f863 100644 --- a/user/vim/vimrc +++ b/user/vim/vimrc @@ -1,4 +1,23 @@ syntax on +" Don't break words in the middle. +set linebreak + +" Enable easy copying between different vim instances. +set clipboard=unnamed + +" Wayland clipboard workaround (see https://github.com/vim/vim/issues/5157). +if !empty($WAYLAND_DISPLAY) && executable('wl-copy') && executable('wl-paste') + " enable focus reporting + let &t_fe = "\<Esc>[?1004h" + let &t_fd = "\<Esc>[?1004l" + + augroup wl-clipboard + autocmd! + autocmd FocusLost * if @" != '' | call system('wl-copy --trim-newline', @") | endif + autocmd FocusGained * let @" = system('wl-paste -n') + augroup END +endif + " Enable pasting with middle mouse click in insert mode. set mouse= diff --git a/user/zed/settings.json b/user/zed/settings.json index 8614205..d2945a4 100644 --- a/user/zed/settings.json +++ b/user/zed/settings.json @@ -25,6 +25,24 @@ } ] }, + // By default Zed autoformats a lot of file formats (e.g. HTML, TOML, SQL) with + // formatters that aren't used by most projects. So I'm turning format_on_save + // off by default and then on for the programming languages that I use. + "format_on_save": "off", + "languages": { + "Go": { + "format_on_save": "on" + }, + "Nix": { + "language_servers": ["nixd"] + }, + "Rust": { + "format_on_save": "on" + }, + "TypeScript": { + "format_on_save": "on" + } + }, "lsp": { "rust-analyzer": { "binary": { @@ -52,7 +70,7 @@ "features": { "edit_prediction_provider": "none" }, - "assistant": { + "agent": { "enabled": false, "version": "2" }, @@ -61,7 +79,7 @@ "button": false }, "chat_panel": { - "button": false + "button": "never" }, "notification_panel": { "button": false diff --git a/user/zsh/.zshenv b/user/zsh/.zshenv new file mode 100644 index 0000000..56a09b1 --- /dev/null +++ b/user/zsh/.zshenv @@ -0,0 +1 @@ +export VISUAL=vim diff --git a/user/zsh/.zshrc b/user/zsh/.zshrc index bc9fe0c..aa65059 100644 --- a/user/zsh/.zshrc +++ b/user/zsh/.zshrc @@ -48,7 +48,6 @@ SAVEHIST=1000000000 export XDG_CACHE_HOME=$HOME/.cache export XDG_CONFIG_HOME=$HOME/.config -export VISUAL=vim export INPUTRC=$XDG_CONFIG_HOME/readline/inputrc export PYTHON_BASIC_REPL=1 # use readline (Python >=3.13 defaults to its own REPL which has no vi keybindings) diff --git a/user/zsh/zshrc-workstation.sh b/user/zsh/zshrc-workstation.sh index e027208..93ac430 100644 --- a/user/zsh/zshrc-workstation.sh +++ b/user/zsh/zshrc-workstation.sh @@ -3,7 +3,7 @@ ## Aliases alias code='codium' alias zed=zeditor -alias tokei='tokei -s code -n commas' +alias scc='scc --no-cocomo --no-size --no-gen --sort code' ## Environment variables export CARGO_HOME="$HOME/.local/share/cargo" |