├── .github └── workflows │ └── ci.yaml ├── .gitignore ├── .sops.yaml ├── LICENSE ├── README.md ├── flake.lock ├── flake.nix ├── lib ├── default.nix ├── identity.nix └── my.nix ├── machines ├── fu.nix ├── iso.nix ├── ki.nix ├── ku.nix ├── mo.nix ├── no.nix ├── tsu.nix └── u.nix ├── modules ├── cachix.nix ├── clipboard.nix ├── console.nix ├── direnv.nix ├── emacs.nix ├── environment │ ├── default.nix │ ├── order.sh │ ├── toprc │ └── upload.sh ├── fonts.nix ├── git.nix ├── gpg.nix ├── haskell.nix ├── home-manager.nix ├── jellyfin.nix ├── localisation.nix ├── networking.nix ├── nix.nix ├── secrets.nix ├── server │ ├── acme.nix │ ├── bothendieck.nix │ ├── default.nix │ ├── dns.nix │ ├── dwarf-fortress.nix │ ├── lambdabot.nix │ ├── mailserver.nix │ ├── mastodon.nix │ ├── miniflux.nix │ ├── nginx.nix │ ├── nix.nix │ ├── ulmaoc-topic.nix │ └── weechat │ │ ├── alias.conf │ │ ├── autosort.conf │ │ ├── buflist.conf │ │ ├── charset.conf │ │ ├── colorize_nicks.conf │ │ ├── default.nix │ │ ├── exec.conf │ │ ├── fifo.conf │ │ ├── fset.conf │ │ ├── irc.conf │ │ ├── logger.conf │ │ ├── matrix.conf │ │ ├── perl.conf │ │ ├── plugins.conf │ │ ├── python.conf │ │ ├── relay.conf │ │ ├── script.conf │ │ ├── sec.conf │ │ ├── spell.conf │ │ ├── trigger.conf │ │ ├── weechat.conf │ │ └── xfer.conf ├── shell │ ├── default.nix │ ├── functions.bash │ └── prompt.bash ├── ssh.nix ├── station │ ├── agda.xcompose │ ├── alacritty.nix │ ├── backlight.nix │ ├── broadcasting.nix │ ├── bspwm │ │ ├── bar.sh │ │ ├── default.nix │ │ └── wm.sh │ ├── default.nix │ ├── discord │ │ ├── default.nix │ │ └── settings.json │ ├── dunst.nix │ ├── feh.nix │ ├── firefox.nix │ ├── games │ │ ├── default.nix │ │ └── minecraft │ │ │ ├── default.nix │ │ │ ├── options.txt │ │ │ └── sodium.json │ ├── ghostty.nix │ ├── gtk.nix │ ├── im.nix │ ├── imv.nix │ ├── internet-sharing.nix │ ├── mpv.nix │ ├── music │ │ ├── default.nix │ │ ├── music-add.sh │ │ ├── music-notify.sh │ │ ├── music-play.sh │ │ └── music-rofi.sh │ ├── openrgb.nix │ ├── printing.nix │ ├── qt.nix │ ├── river.nix │ ├── rofi.nix │ ├── shoot.sh │ ├── sound.nix │ ├── thunar │ │ ├── accels.scm │ │ ├── dbus-gen-thumbnails.py │ │ ├── default.nix │ │ ├── tumbler.rc │ │ └── uca.xml │ ├── vscode │ │ ├── default.nix │ │ ├── keybindings.json │ │ └── settings.json │ ├── x.nix │ └── xcompose.nix ├── syncthing.nix ├── systemd.nix ├── theme.nix ├── tmux.nix ├── users.nix ├── vim │ ├── default.nix │ └── init.vim ├── wireguard.nix └── xdg.nix └── secrets ├── bothendieck ├── cachix ├── dkim ├── lambdabot-ulminfo ├── miniflux ├── networkmanager ├── nix-access-tokens ├── nix-binary-cache ├── syncthing.yaml ├── ulmaoc-topic ├── weechat └── wireguard.yaml /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: [push, pull_request] 3 | concurrency: 4 | group: ${{ github.workflow }}-${{ github.ref }} 5 | cancel-in-progress: true 6 | jobs: 7 | check-links: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - name: Checkout code 11 | uses: actions/checkout@v4 12 | - name: Check Markdown links 13 | uses: gaurav-nelson/github-action-markdown-link-check@v1 14 | build-iso: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - name: Checkout code 18 | uses: actions/checkout@v4 19 | - name: Install Nix 20 | uses: cachix/install-nix-action@v26 21 | with: 22 | extra_nix_config: | 23 | access-tokens = github.com=${{ secrets.GITHUB_TOKEN }} 24 | - name: Set up Cachix 25 | uses: cachix/cachix-action@v14 26 | with: 27 | name: ${{ github.actor }} 28 | authToken: ${{ secrets.CACHIX_AUTH_TOKEN }} 29 | extraPullNames: nix-community, mic92 30 | pathsToPush: iso 31 | # - name: Check flake 32 | # run: nix flake check 33 | - name: Build ISO 34 | run: nix -L build .#iso -o iso 35 | - name: Create release 36 | run: | 37 | gh release upload iso iso/iso/nixos*.iso --clobber 38 | env: 39 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 40 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | result 2 | iso 3 | -------------------------------------------------------------------------------- /.sops.yaml: -------------------------------------------------------------------------------- 1 | creation_rules: 2 | - path_regex: secrets/.* 3 | age: 'age1nklm02ejf57n0x208mjrww9s8np44kkmwfl7wdn78dtmh7x2g5wqh987h9' 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020 Naïm Favier 2 | 3 | Permission to use, copy, modify, and/or distribute this software for any purpose 4 | with or without fee is hereby granted, provided that the above copyright notice 5 | and this permission notice appear in all copies. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 8 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND 9 | FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 10 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS 11 | OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 12 | TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF 13 | THIS SOFTWARE. 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This repository defines almost all of the 2 | configuration for my machines running the [NixOS](https://nixos.org/) Linux 3 | distribution, including installed programs, configuration files and running 4 | services. 5 | 6 | NixOS is built on top of [Nix](https://nixos.org/manual/nix/stable/#chap-introduction), 7 | a package manager and build system with a declarative approach based on a 8 | [lazy](https://en.wikipedia.org/wiki/Lazy_evaluation) 9 | [functional programming](https://en.wikipedia.org/wiki/Functional_programming) 10 | language. (See [GNU Guix](https://guix.gnu.org/) 11 | for an alternative with a stronger focus on software freedoms.) 12 | -------------------------------------------------------------------------------- /flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | description = "ncfavier's configurations"; 3 | 4 | inputs = { 5 | nixpkgs.url = "nixpkgs/nixos-unstable"; 6 | nixpkgs-stable.url = "nixpkgs/nixos-22.11"; 7 | nixpkgs-unstable.url = "nixpkgs/nixpkgs-unstable"; 8 | nixos-hardware.url = "nixos-hardware"; 9 | sops-nix = { 10 | url = "github:Mic92/sops-nix"; 11 | inputs.nixpkgs.follows = "nixpkgs"; 12 | }; 13 | home-manager = { 14 | url = "github:nix-community/home-manager"; 15 | inputs.nixpkgs.follows = "nixpkgs"; 16 | }; 17 | xhmm.url = "github:schuelermine/xhmm"; 18 | nur.url = "nur"; 19 | dns = { 20 | url = "github:kirelagin/dns.nix"; 21 | inputs.nixpkgs.follows = "nixpkgs"; 22 | }; 23 | simple-nixos-mailserver = { 24 | url = "gitlab:simple-nixos-mailserver/nixos-mailserver"; 25 | inputs.nixpkgs.follows = "nixpkgs"; 26 | }; 27 | www = { 28 | url = "github:ncfavier/monade.li"; 29 | flake = false; 30 | }; 31 | bothendieck = { 32 | url = "github:ncfavier/bothendieck"; 33 | inputs.nixpkgs.follows = "nixpkgs"; 34 | inputs.qeval.follows = "qeval"; 35 | }; 36 | nixpkgs-qeval.url = "nixpkgs/nixos-unstable"; 37 | qeval = { 38 | url = "github:ncfavier/qeval"; 39 | inputs.nixpkgs.follows = "nixpkgs-qeval"; 40 | inputs.nur.follows = "nur"; 41 | }; 42 | ghostty = { 43 | url = "github:ghostty-org/ghostty"; 44 | }; 45 | }; 46 | 47 | outputs = inputs@{ self, nixpkgs, home-manager, ... }: let 48 | lib = (nixpkgs.lib.extend (_: _: home-manager.lib)).extend (import ./lib machines); 49 | machines = lib.exprsIn ./machines; 50 | in with lib; { 51 | inherit lib; 52 | 53 | nixosConfigurations = mapAttrs (k: nixos: let 54 | this = my.machines.${k}; 55 | in 56 | lib.nixosSystem { 57 | inherit lib; 58 | inherit (this) system; 59 | modules = optionals (! this.isISO) (attrValues (modulesIn ./modules)) ++ [ 60 | { system.configurationRevision = self.rev or "dirty"; } 61 | nixos 62 | ]; 63 | specialArgs = { 64 | inherit inputs this; 65 | myModulesPath = toString ./modules; 66 | hardware = nixpkgs.nixosModules // inputs.nixos-hardware.nixosModules; 67 | pkgsBase = nixpkgs.legacyPackages.${this.system}; # for use in imports without infinite recursion 68 | }; 69 | } 70 | ) (catAttrs' "nixos" machines); 71 | 72 | packages."x86_64-linux" = mapAttrs (_: c: c.config.system.build.toplevel) self.nixosConfigurations // { 73 | iso = self.nixosConfigurations.iso.config.system.build.isoImage.overrideAttrs { 74 | unsafeDiscardReferences.out = true; 75 | }; 76 | }; 77 | }; 78 | } 79 | -------------------------------------------------------------------------------- /lib/default.nix: -------------------------------------------------------------------------------- 1 | machines: lib: prev: with lib; { 2 | # Collects the top-level modules in a directory into an attribute set of paths. 3 | # A module `foo` can be either a file (`foo.nix`) or a directory (`foo/default.nix`). 4 | modulesIn = dir: pipe dir [ 5 | builtins.readDir 6 | (mapAttrsToList (name: type: 7 | if type == "regular" && hasSuffix ".nix" name && name != "default.nix" then 8 | [ { name = removeSuffix ".nix" name; value = dir + "/${name}"; } ] 9 | else if type == "directory" && pathExists (dir + "/${name}/default.nix") then 10 | [ { inherit name; value = dir + "/${name}"; } ] 11 | else 12 | [] 13 | )) 14 | concatLists 15 | listToAttrs 16 | ]; 17 | 18 | # Like modulesIn, but imports the files. 19 | exprsIn = dir: mapAttrs (_: f: import f) (modulesIn dir); 20 | 21 | # Like catAttrs, but operates on an attribute set of attribute sets 22 | # instead of a list of attribute sets. 23 | catAttrs' = key: set: 24 | listToAttrs (concatMap (name: 25 | let v = set.${name}; in 26 | if v ? ${key} then [(nameValuePair name v.${key})] else [] 27 | ) (attrNames set)); 28 | 29 | 30 | # Collects the inputs of a flake recursively (with possible duplicates). 31 | collectFlakeInputs = input: 32 | [ input ] ++ concatMap collectFlakeInputs (builtins.attrValues (input.inputs or {})); 33 | 34 | # Gets all the outputs of a derivation as a list. 35 | getAllOutputs = drv: 36 | if drv ? outputs then attrVals drv.outputs drv else [ drv ]; 37 | 38 | versionAtMost = a: b: versionAtLeast b a; 39 | 40 | # Creates a simple module with an `enable` option. 41 | mkEnableModule = name: cfg: { 42 | options = setAttrByPath name { enable = mkEnableOption (last name); }; 43 | imports = cfg.imports or [] ++ [ 44 | ({ config, ... }: { config = mkIf (getAttrFromPath name config).enable (removeAttrs cfg [ "imports" ]); }) 45 | ]; 46 | }; 47 | 48 | my = import ./my.nix lib machines; 49 | } 50 | -------------------------------------------------------------------------------- /lib/identity.nix: -------------------------------------------------------------------------------- 1 | { lib, name, config, ... }: with lib; with types; { 2 | freeformType = attrs; 3 | options = let 4 | mkMachineTypeOption = type: mkOption { 5 | description = "Whether the machine is a ${type}"; 6 | type = bool; 7 | default = false; 8 | }; 9 | in { 10 | hostname = mkOption { 11 | description = "The machine's hostname"; 12 | type = nullOr str; 13 | default = name; 14 | }; 15 | system = mkOption { 16 | description = "The machine's platform"; 17 | type = str; 18 | default = "x86_64-linux"; 19 | }; 20 | isServer = mkMachineTypeOption "server"; 21 | isStation = mkMachineTypeOption "station"; 22 | isPhone = mkMachineTypeOption "phone"; 23 | isISO = mkOption { 24 | description = "Whether this is an ISO image"; 25 | type = bool; 26 | default = false; 27 | }; 28 | ipv4 = mkOption { 29 | description = "The machine's public IPv4 addresses"; 30 | type = listOf str; 31 | default = []; 32 | }; 33 | ipv6 = mkOption { 34 | description = "The machine's public IPv6 addresses"; 35 | type = listOf str; 36 | default = []; 37 | }; 38 | sshPort = mkOption { 39 | description = "The machine's SSH port"; 40 | type = nullOr int; 41 | default = null; 42 | }; 43 | hasKVM = mkOption { 44 | description = "Whether the machine supports KVM."; 45 | type = bool; 46 | default = !config.isServer; 47 | }; 48 | }; 49 | } 50 | -------------------------------------------------------------------------------- /lib/my.nix: -------------------------------------------------------------------------------- 1 | lib: machines: with lib; let 2 | modules = [ 3 | (mkAliasOptionModule [ "server" ] [ "machines" "ku" ]) 4 | ({ config, ... }: { 5 | freeformType = types.attrs; 6 | 7 | options.machines = mkOption { 8 | description = "My machines"; 9 | type = with types; attrsOf (submodule ./identity.nix); 10 | default = {}; 11 | }; 12 | 13 | config = { 14 | username = "n"; 15 | githubUsername = "ncfavier"; 16 | chalmersId = "naimf"; 17 | realName = "Naïm Favier"; 18 | domain = "monade.li"; 19 | email = "${my.username}@${my.domain}"; 20 | pgpFingerprint = "F3EB4BBB4E7199BC299CD4E995AFCE8211908325"; 21 | sshKeys = [ 22 | "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMy4X7ieNZEbVQQtz4PpWWv5bBeG0a7cXp74RjRRoTNX ${my.email}" # SSH 23 | "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIA9Kj3Zjnou6w4tZn60SAIYvrFlFQhSiKbLxTR9sVC1I ${my.email}" # GPG 24 | "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQD7KZW1RCBXJY1uDLbmaDUm50eshkv1rT8eK0JJXR3MfuCaJ/Kqrg547ZjczxED98Qy8A7d1BrIsOiKEoFVou+jCcjU19hlkQiMce3IZmYm0h6MOmZqB0MR6EGTlAgDfkiDMYqnAUGst4p2xqqmH/gM/UI2d5ZFrxAbK+PC4d7yMxs5QJkJ0buXRnbKL/LGRWwyUCV8UDzQ26kYufVyAhS2Iz2SvUSqca5BaJOzAPJ74CFScbICFK5nlsc2kHH35ZqK3f1Jxmbpi8ZwXUyxT+pFUClzY/s5H4w8c70ItvOyD3T0B+a8MF2Ft/c1kLFnHfYJd2FET+RZJQ5P+kXW+iZb ${my.email}" # GPG 25 | ]; 26 | gravatar = "https://www.gravatar.com/avatar/1fea7494d69948ab0a50d9ee9318ae50"; 27 | 28 | machines = catAttrs' "identity" machines; 29 | machinesWith = key: filterAttrs (_: v: v ? ${key} && v.${key} != null) config.machines; 30 | machinesThat = pred: filterAttrs (_: pred) config.machines; 31 | }; 32 | }) 33 | ]; 34 | in (evalModules { inherit modules; }).config 35 | -------------------------------------------------------------------------------- /machines/fu.nix: -------------------------------------------------------------------------------- 1 | { 2 | identity = { 3 | isStation = true; 4 | wireguard = { 5 | ipv4 = "10.42.1.1"; 6 | ipv6 = "fd42::1:1"; 7 | publicKey = "v1MDB6hEYKdBwdVN/rOnOGB82h3xTQpHwU3CAcctGWg="; 8 | }; 9 | syncthing.id = "VVLGMST-LA633IY-KWESSFD-7FFF7LE-PNJAEML-ZXZSBLL-ATLQHPT-MUHEDAR"; 10 | }; 11 | 12 | nixos = { hardware, config, pkgs, ... }: { 13 | imports = with hardware; [ 14 | notDetected 15 | common-cpu-intel-cpu-only 16 | common-pc-ssd 17 | ]; 18 | 19 | boot = { 20 | loader = { 21 | efi.canTouchEfiVariables = true; 22 | systemd-boot = { 23 | enable = true; 24 | configurationLimit = 25; 25 | consoleMode = "max"; 26 | }; 27 | }; 28 | 29 | kernelModules = [ "kvm-intel" ]; 30 | # initrd.kernelModules = [ "nouveau" ]; 31 | initrd.availableKernelModules = [ "ehci_pci" "ahci" "usbhid" "usb_storage" "sd_mod" ]; 32 | 33 | initrd.luks.devices.nixos = { 34 | device = "/dev/disk/by-partlabel/nixos"; 35 | allowDiscards = true; 36 | bypassWorkqueues = true; 37 | }; 38 | 39 | swraid.enable = false; 40 | }; 41 | 42 | services.xserver.videoDrivers = [ "nvidia" ]; 43 | nixpkgs.config.nvidia.acceptLicense = true; 44 | 45 | hardware.nvidia = { 46 | package = config.boot.kernelPackages.nvidiaPackages.legacy_390; 47 | nvidiaSettings = true; 48 | }; 49 | 50 | fileSystems = { 51 | "/" = { 52 | device = "/dev/disk/by-label/nixos"; 53 | fsType = "ext4"; 54 | }; 55 | 56 | "/boot" = { 57 | device = "/dev/disk/by-label/boot"; 58 | fsType = "vfat"; 59 | options = [ "umask=0077" ]; 60 | }; 61 | }; 62 | 63 | swapDevices = [ { 64 | device = "/swap"; 65 | size = 8 * 1024; 66 | } ]; 67 | 68 | networking.wireless.interfaces = [ "wlp3s0" ]; 69 | 70 | environment.systemPackages = with pkgs; [ 71 | efibootmgr 72 | ]; 73 | 74 | my.hashedPassword = "$y$j9T$4ixQiecsV/ucuBhr6jEte1$4mQUZgQsZXNlA2rY5RfntCTPEZ7ZuZc64L1k9VO5tQ8"; 75 | 76 | services.syncthing.cert = builtins.toFile "syncthing-cert" '' 77 | -----BEGIN CERTIFICATE----- 78 | MIIBmjCCASCgAwIBAgIIRaL8rxe74e0wCgYIKoZIzj0EAwMwFDESMBAGA1UEAxMJ 79 | c3luY3RoaW5nMB4XDTE4MDcwMzIwMTExM1oXDTQ5MTIzMTIzNTk1OVowFDESMBAG 80 | A1UEAxMJc3luY3RoaW5nMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEjVHr6jb7E73k 81 | sJwqCw4AE2WoIV3WRZy4ITdg/XxP19N5reFdqyVfjp4LXIoZto8SWfbQ9pPlgY21 82 | eDTr/QIASnMI2Oc5Hcmb6ozv49AuQSef85UoSUU90YWkGNGODWuxoz8wPTAOBgNV 83 | HQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMAwGA1Ud 84 | EwEB/wQCMAAwCgYIKoZIzj0EAwMDaAAwZQIxAMcB86gR/XSiEVrDEQsDbRxn/kzJ 85 | vSzd4X9+RhQ+4i9dYYNPigt4xCxgewpyt0UmVAIwMf25wwJosKeBNFIH6CaEn/4g 86 | kjz2Vh63ayu5iLe7cOhUUIvmDuEW2wmxe/6Iz3LR 87 | -----END CERTIFICATE----- 88 | ''; 89 | 90 | system.stateVersion = "21.05"; 91 | }; 92 | } 93 | -------------------------------------------------------------------------------- /machines/iso.nix: -------------------------------------------------------------------------------- 1 | { 2 | identity = { 3 | isISO = true; 4 | hostname = null; 5 | }; 6 | 7 | nixos = { lib, modulesPath, myModulesPath, pkgs, ... }: with lib; { 8 | imports = [ 9 | "${modulesPath}/installer/cd-dvd/installation-cd-minimal.nix" 10 | "${myModulesPath}/theme.nix" 11 | "${myModulesPath}/networking.nix" 12 | "${myModulesPath}/home-manager.nix" 13 | "${myModulesPath}/users.nix" 14 | "${myModulesPath}/localisation.nix" 15 | "${myModulesPath}/console.nix" 16 | "${myModulesPath}/shell" 17 | "${myModulesPath}/gpg.nix" 18 | "${myModulesPath}/git.nix" 19 | "${myModulesPath}/nix.nix" 20 | ]; 21 | 22 | options = { 23 | secrets = mkSinkUndeclaredOptions {}; 24 | }; 25 | 26 | config = { 27 | boot.supportedFilesystems = genAttrs [ "reiserfs" "xfs" "cifs" "f2fs" ] (_: mkForce false); 28 | 29 | console.font = "Lat2-Terminus16"; 30 | 31 | services.getty.autologinUser = mkForce my.username; 32 | 33 | # start wpa-supplicant on boot 34 | systemd.services.wpa_supplicant.wantedBy = mkForce [ "multi-user.target" ]; 35 | 36 | # reduce size by removing unneeded firmware 37 | nixpkgs.overlays = [ (pkgs: prev: { 38 | linux-firmware = prev.linux-firmware.overrideAttrs (o: { 39 | postInstall = '' 40 | rm -rf "$out"/lib/firmware/{netronome,qcom,mellanox,mrvl,ath11k,ath10k,libertas} 41 | find -L "$out" -type l -delete # remove dangling symlinks so that compressFirmwareXz doesn't complain 42 | ''; 43 | }); 44 | }) ]; 45 | 46 | environment.systemPackages = with pkgs; [ 47 | sops 48 | age 49 | ssh-to-age 50 | ]; 51 | 52 | environment.variables.EDITOR = "vim"; 53 | }; 54 | }; 55 | } 56 | -------------------------------------------------------------------------------- /machines/ki.nix: -------------------------------------------------------------------------------- 1 | { 2 | identity = { 3 | isStation = true; 4 | wireguard = { 5 | ipv4 = "10.42.1.2"; 6 | ipv6 = "fd42::1:2"; 7 | publicKey = "uXNQlIA85TylU/PcHtrFTfJHjagXcYsG7UztgebU4hc="; 8 | }; 9 | syncthing.id = "2IXUK3S-SC5ZNJF-UUPGWJZ-5MP646N-K3PZBIW-PL6OWZ4-NGXI6HU-I2YSYAC"; 10 | }; 11 | 12 | nixos = { hardware, lib, config, pkgs, ... }: with lib; { 13 | imports = with hardware; [ 14 | notDetected 15 | common-cpu-amd 16 | common-cpu-amd-pstate 17 | common-gpu-amd 18 | common-pc-ssd 19 | ]; 20 | 21 | boot = { 22 | loader = { 23 | efi.canTouchEfiVariables = true; 24 | systemd-boot = { 25 | enable = true; 26 | configurationLimit = 25; 27 | }; 28 | }; 29 | 30 | kernelPackages = pkgs.linuxPackages_latest; 31 | kernelModules = [ "kvm-amd" ]; 32 | initrd.availableKernelModules = [ "nvme" "xhci_pci" "ahci" "usb_storage" "usbhid" "sd_mod" ]; 33 | 34 | initrd.luks.devices."nixos" = { 35 | device = "/dev/disk/by-partlabel/nixos"; 36 | allowDiscards = true; 37 | bypassWorkqueues = true; 38 | }; 39 | }; 40 | 41 | fileSystems = { 42 | "/" = { 43 | device = "/dev/disk/by-label/nixos"; 44 | fsType = "ext4"; 45 | }; 46 | 47 | "/boot" = { 48 | device = "/dev/disk/by-label/boot"; 49 | fsType = "vfat"; 50 | options = [ "umask=0077" ]; 51 | }; 52 | }; 53 | 54 | networking.wireless.interfaces = [ "wlp10s0" ]; 55 | 56 | environment.systemPackages = with pkgs; [ 57 | efibootmgr 58 | radeontop 59 | ]; 60 | 61 | hardware.bluetooth.enable = true; 62 | services.blueman.enable = true; 63 | hm.services.blueman-applet.enable = true; 64 | 65 | my-services.openrgb.enable = true; 66 | 67 | keys.composeKey = "rwin"; 68 | keys.printScreenKey = "Insert"; 69 | 70 | broadcasting.enable = true; 71 | 72 | services.xserver.videoDrivers = [ "amdgpu" ]; 73 | 74 | my.hashedPassword = "$y$j9T$HVlzhk1CJa7IPyHjmHTFN.$c1go/wt0izX52Ej/EWtykusUCmqJLCXtvgXGvjcrHu8"; 75 | 76 | services.syncthing.cert = builtins.toFile "syncthing-cert" '' 77 | -----BEGIN CERTIFICATE----- 78 | MIICHTCCAaOgAwIBAgIJAIiFIMuBBHvJMAoGCCqGSM49BAMCMEoxEjAQBgNVBAoT 79 | CVN5bmN0aGluZzEgMB4GA1UECxMXQXV0b21hdGljYWxseSBHZW5lcmF0ZWQxEjAQ 80 | BgNVBAMTCXN5bmN0aGluZzAeFw0yNDEwMTIwMDAwMDBaFw00NDEwMDcwMDAwMDBa 81 | MEoxEjAQBgNVBAoTCVN5bmN0aGluZzEgMB4GA1UECxMXQXV0b21hdGljYWxseSBH 82 | ZW5lcmF0ZWQxEjAQBgNVBAMTCXN5bmN0aGluZzB2MBAGByqGSM49AgEGBSuBBAAi 83 | A2IABKzijChL3fyXVLOpHM55LrqHvSugKa0Kk1CGMcgy8cv2fmVRuaqD3BWDvqv7 84 | F64kVv1Nk2bp9Uk0OI5Grs1NkwbnhgSzltbmCYt14S77AjW+gKZd1vWPE28AvCw6 85 | 4ZI9FKNVMFMwDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggr 86 | BgEFBQcDAjAMBgNVHRMBAf8EAjAAMBQGA1UdEQQNMAuCCXN5bmN0aGluZzAKBggq 87 | hkjOPQQDAgNoADBlAjBoQwjVRAe4GoKURlTNUi4SphAch+pUMW/4SzMkFciG0bxz 88 | HqaMbjsXXA/NN/9aiLQCMQC0Lw0Ha2NmLZiwIukpZGk3vYP5odxymvtOk7NUuPWw 89 | X5aebMnRwUqYFqFQcTT4yDo= 90 | -----END CERTIFICATE----- 91 | ''; 92 | 93 | system.stateVersion = "24.11"; 94 | }; 95 | } 96 | -------------------------------------------------------------------------------- /machines/ku.nix: -------------------------------------------------------------------------------- 1 | { 2 | identity = { 3 | isServer = true; 4 | ipv4 = [ "152.53.123.186" ]; 5 | ipv6 = [ "2a0a:4cc0:c0:4ccc::42" ]; 6 | sshPort = 2242; 7 | wireguard = { 8 | ipv4 = "10.42.0.2"; 9 | ipv6 = "fd42::0:2"; 10 | publicKey = "Bh4e4iDqtG0KI2ey4FwFjQfKmJTnKsqMhbYh5eUx0ys="; 11 | }; 12 | syncthing.id = "7ZXLXN2-LP3EWQZ-DSSPX4E-UHEIZIZ-45Z3R6J-IHFYDUP-N63R4R3-R5CGMQM"; 13 | }; 14 | 15 | nixos = { lib, this, config, modulesPath, pkgs, ... }: with lib; let 16 | interface = "ens3"; 17 | in { 18 | imports = [ 19 | "${modulesPath}/profiles/qemu-guest.nix" 20 | ]; 21 | 22 | boot = { 23 | loader = { 24 | efi.canTouchEfiVariables = true; 25 | systemd-boot = { 26 | enable = true; 27 | configurationLimit = 3; 28 | }; 29 | }; 30 | 31 | kernelPackages = pkgs.linuxPackages_latest; 32 | initrd.availableKernelModules = [ "ata_piix" "uhci_hcd" "virtio_pci" "sr_mod" "virtio_blk" ]; 33 | 34 | initrd.luks.devices.nixos = { 35 | device = "/dev/disk/by-partlabel/nixos"; 36 | allowDiscards = true; 37 | bypassWorkqueues = true; 38 | }; 39 | 40 | initrd.network = { 41 | enable = true; 42 | ssh = { 43 | enable = true; 44 | hostKeys = map (k: k.path) config.services.openssh.hostKeys; 45 | }; 46 | }; 47 | 48 | swraid.enable = false; 49 | }; 50 | 51 | fileSystems = { 52 | "/" = { 53 | device = "/dev/disk/by-label/nixos"; 54 | fsType = "ext4"; 55 | }; 56 | 57 | "/boot" = { 58 | device = "/dev/disk/by-label/boot"; 59 | fsType = "vfat"; 60 | options = [ "umask=0077" ]; 61 | }; 62 | }; 63 | 64 | swapDevices = [ { 65 | device = "/swap"; 66 | size = 8 * 1024; 67 | } ]; 68 | 69 | services.fstrim.enable = true; 70 | 71 | environment.systemPackages = with pkgs; [ 72 | efibootmgr 73 | ]; 74 | 75 | networking.defaultGateway6 = { 76 | inherit interface; 77 | address = "fe80::1"; 78 | }; 79 | networking.interfaces.${interface} = { 80 | useDHCP = true; 81 | ipv6.addresses = map (address: { 82 | inherit address; 83 | prefixLength = 64; 84 | }) this.ipv6; 85 | }; 86 | 87 | networking.nat.externalInterface = interface; 88 | 89 | my-services.nginx.enable = true; 90 | my-services.mailserver.enable = true; 91 | my-services.weechat.enable = true; 92 | my-services.bothendieck.enable = true; 93 | my-services.lambdabot.enable = true; 94 | 95 | my.hashedPassword = "$y$j9T$gdhcWVYa8vkGfJpYE7XbQ.$QN9N0v0BZpFbAfMkqDoYN0O8KiTwXTN193kRowdrG0B"; 96 | 97 | services.syncthing.cert = builtins.toFile "syncthing-cert" '' 98 | -----BEGIN CERTIFICATE----- 99 | MIICHTCCAaOgAwIBAgIJAIWEYSmi2c8yMAoGCCqGSM49BAMCMEoxEjAQBgNVBAoT 100 | CVN5bmN0aGluZzEgMB4GA1UECxMXQXV0b21hdGljYWxseSBHZW5lcmF0ZWQxEjAQ 101 | BgNVBAMTCXN5bmN0aGluZzAeFw0yNTAyMDEwMDAwMDBaFw00NTAxMjcwMDAwMDBa 102 | MEoxEjAQBgNVBAoTCVN5bmN0aGluZzEgMB4GA1UECxMXQXV0b21hdGljYWxseSBH 103 | ZW5lcmF0ZWQxEjAQBgNVBAMTCXN5bmN0aGluZzB2MBAGByqGSM49AgEGBSuBBAAi 104 | A2IABJjFwJ08JbC+TM+2roKBR2BgsL60H1fk0VhuCN3yj0j1xKgnGJes4UseroTU 105 | j4u+ZIIURfK6vsSVZ2CLHsq4xiTs46QulOgUjNOONhFGaCWYxwD59WGF15RJVYsu 106 | kpyXCqNVMFMwDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggr 107 | BgEFBQcDAjAMBgNVHRMBAf8EAjAAMBQGA1UdEQQNMAuCCXN5bmN0aGluZzAKBggq 108 | hkjOPQQDAgNoADBlAjApMKp5fFrfmXrsTMLSTAVPeWXRzMX3TY90qmyal7ucLeDG 109 | jPQbpAJ3MjGGbZItx84CMQD7rgA4l003CABvcTo3MahQMuaL2397KzEAyXeZLGyG 110 | Ma7GFmlNJQ9+bbgeXpKjN7A= 111 | -----END CERTIFICATE----- 112 | ''; 113 | 114 | system.stateVersion = "22.05"; 115 | }; 116 | } 117 | -------------------------------------------------------------------------------- /machines/mo.nix: -------------------------------------------------------------------------------- 1 | { 2 | identity = { 3 | isStation = true; 4 | wireguard = { 5 | ipv4 = "10.42.2.1"; 6 | ipv6 = "fd42::2:1"; 7 | publicKey = "tsvrIdHACcHMhtaHQt2tVE+2FO1LMdtiAlSXPNMuHFc="; 8 | }; 9 | syncthing.id = "WO4GV6E-AJGKLLQ-M7RZGFT-WY7CCOW-LXODXRY-F3QPEJ2-AXDVWKR-SWBGDQP"; 10 | }; 11 | 12 | nixos = { inputs, hardware, pkgs, ... }: { 13 | imports = with hardware; [ 14 | notDetected 15 | lenovo-thinkpad-t420 16 | common-pc-ssd 17 | ]; 18 | 19 | services.tlp.settings = { 20 | START_CHARGE_THRESH_BAT0 = 75; 21 | STOP_CHARGE_THRESH_BAT0 = 80; 22 | RESTORE_THRESHOLDS_ON_BAT = 1; 23 | }; 24 | 25 | boot = { 26 | loader = { 27 | efi.canTouchEfiVariables = true; 28 | systemd-boot = { 29 | enable = true; 30 | configurationLimit = 25; 31 | consoleMode = "max"; 32 | }; 33 | }; 34 | 35 | kernelPackages = pkgs.linuxPackages_latest; 36 | kernelModules = [ "kvm-intel" ]; 37 | initrd.availableKernelModules = [ "ehci_pci" "ahci" "firewire_ohci" "sdhci_pci" ]; 38 | 39 | initrd.luks.devices.home = { 40 | device = "/dev/disk/by-partlabel/home"; 41 | }; 42 | 43 | swraid.enable = false; 44 | }; 45 | 46 | fileSystems = { 47 | "/" = { 48 | device = "/dev/disk/by-label/nixos"; 49 | fsType = "ext4"; 50 | }; 51 | 52 | "/boot" = { 53 | device = "/dev/disk/by-label/boot"; 54 | fsType = "vfat"; 55 | options = [ "umask=0077" ]; 56 | }; 57 | 58 | "/home" = { 59 | device = "/dev/disk/by-label/home"; 60 | fsType = "ext4"; 61 | neededForBoot = true; 62 | }; 63 | }; 64 | 65 | swapDevices = [ { 66 | device = "/swap"; 67 | size = 4 * 1024; 68 | } ]; 69 | 70 | networking.wireless.interfaces = [ "wlp3s0" ]; 71 | 72 | environment.systemPackages = with pkgs; [ 73 | efibootmgr 74 | v4l-utils 75 | ]; 76 | 77 | services.xserver.videoDrivers = [ "intel" ]; 78 | 79 | my.hashedPassword = "$y$j9T$qOTfNGYwzLlwcvYq8OwdR1$br09oz/1NqEPjpPz8LsKyCTve9tv4.k5hkfOlxS53C5"; 80 | 81 | services.syncthing.cert = builtins.toFile "syncthing-cert" '' 82 | -----BEGIN CERTIFICATE----- 83 | MIIBmjCCASCgAwIBAgIIU2Crk9b6ZekwCgYIKoZIzj0EAwMwFDESMBAGA1UEAxMJ 84 | c3luY3RoaW5nMB4XDTE4MDgwNzIzMjMzNVoXDTQ5MTIzMTIzNTk1OVowFDESMBAG 85 | A1UEAxMJc3luY3RoaW5nMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEh18xcRgABADs 86 | 7eMwystTMeUC65E+dP/MJf6tOBPRAumbP2LanrtRAW4it1KjJ8QiwtRe3t7+SlvN 87 | CdC26ni4NH6B9fYhN1vL0pjHy3cun5ouwLxC4tTISyrirJZl4UAPoz8wPTAOBgNV 88 | HQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMAwGA1Ud 89 | EwEB/wQCMAAwCgYIKoZIzj0EAwMDaAAwZQIxAJwtZBBG6iGgiCE5Xsfebxltw/Uy 90 | kjrbRaEBW8Dp+DcmfJjWPz1tW8WwBd3LGdadswIwaE6CkCKXg7/Om2O9WCs8qnjU 91 | qR/eLxSYOw2/n12rN2cEsWz6SI+vpfDIZoTYxvDP 92 | -----END CERTIFICATE----- 93 | ''; 94 | 95 | system.stateVersion = "21.05"; 96 | }; 97 | } 98 | -------------------------------------------------------------------------------- /machines/no.nix: -------------------------------------------------------------------------------- 1 | { 2 | identity = { 3 | isStation = true; 4 | wireguard = { 5 | ipv4 = "10.42.2.2"; 6 | ipv6 = "fd42::2:2"; 7 | publicKey = "mQe4b0adN/BDQUTAzc+0rZp8M+ZjV17ewEtBLRIdM0I="; 8 | }; 9 | syncthing.id = "MN3PICD-LGLVMZ2-SSNK5CG-LXNWL5R-U2QMWNM-AIA4UAG-NQ5WT5Y-B3TKXQV"; 10 | }; 11 | 12 | nixos = { hardware, lib, config, pkgs, ... }: with lib; { 13 | imports = with hardware; [ 14 | notDetected 15 | lenovo-thinkpad-t14s-amd-gen1 16 | common-pc-ssd 17 | ]; 18 | 19 | services.fwupd.enable = true; 20 | systemd.timers.fwupd-refresh.enable = false; # https://github.com/NixOS/nixpkgs/issues/271834 21 | 22 | services.tlp.settings = { 23 | RUNTIME_PM_DENYLIST = "02:00.0"; # otherwise the Ethernet adapter doesn't work on battery mode 24 | START_CHARGE_THRESH_BAT0 = 85; 25 | STOP_CHARGE_THRESH_BAT0 = 91; 26 | RESTORE_THRESHOLDS_ON_BAT = 1; 27 | }; 28 | 29 | boot = { 30 | loader = { 31 | efi.canTouchEfiVariables = true; 32 | systemd-boot = { 33 | enable = true; 34 | configurationLimit = 25; 35 | }; 36 | }; 37 | 38 | # kernelPackages = mkForce pkgs.linuxPackages_latest; 39 | kernelModules = [ "kvm-amd" ]; 40 | initrd.availableKernelModules = [ "nvme" "ehci_pci" "xhci_pci" "usb_storage" "sd_mod" "rtsx_pci_sdmmc" ]; 41 | 42 | initrd.luks.devices.nixos = { 43 | device = "/dev/disk/by-partlabel/nixos"; 44 | allowDiscards = true; 45 | bypassWorkqueues = true; 46 | }; 47 | 48 | swraid.enable = false; 49 | }; 50 | 51 | fileSystems = { 52 | "/" = { 53 | device = "/dev/disk/by-label/nixos"; 54 | fsType = "ext4"; 55 | }; 56 | 57 | "/boot" = { 58 | device = "/dev/disk/by-label/boot"; 59 | fsType = "vfat"; 60 | options = [ "umask=0077" ]; 61 | }; 62 | }; 63 | 64 | swapDevices = [ { 65 | device = "/swap"; 66 | size = 8 * 1024; 67 | } ]; 68 | 69 | networking.wireless.interfaces = [ "wlp3s0" ]; 70 | 71 | networking.sharing = { 72 | enable = false; 73 | internalInterface = "enp2s0f0"; 74 | externalInterface = "wlp3s0"; 75 | }; 76 | 77 | environment.systemPackages = with pkgs; [ 78 | efibootmgr 79 | radeontop 80 | v4l-utils 81 | ]; 82 | 83 | hardware.bluetooth.enable = true; 84 | services.blueman.enable = true; 85 | hm.services.blueman-applet.enable = true; 86 | 87 | services.xserver.videoDrivers = [ "amdgpu" ]; 88 | services.xserver.dpi = 120; 89 | 90 | services.autorandr = let 91 | eDP = "*"; 92 | HDMI-A-0 = "*"; 93 | in { 94 | profiles = { 95 | default = { 96 | fingerprint = { inherit eDP; }; 97 | config = { 98 | eDP = { 99 | enable = true; 100 | mode = "1920x1080"; 101 | }; 102 | }; 103 | hooks.postswitch.bspwm = '' 104 | bspc monitor HDMI-A-0 -r 105 | ''; 106 | }; 107 | hdmi = { 108 | fingerprint = { inherit eDP HDMI-A-0; }; 109 | config = { 110 | eDP = { 111 | enable = true; 112 | primary = true; 113 | mode = "1920x1080"; 114 | }; 115 | HDMI-A-0 = { 116 | enable = true; 117 | mode = "1920x1080"; 118 | position = "0x0"; 119 | }; 120 | }; 121 | }; 122 | }; 123 | }; 124 | 125 | keys.composeKey = "prsc"; 126 | keys.printScreenKey = "XF86Favorites"; 127 | 128 | # services.fprintd.enable = true; 129 | 130 | broadcasting.enable = true; 131 | 132 | my-services.jellyfin.enable = true; 133 | 134 | my.hashedPassword = "$y$j9T$Z68zdBJVmsTe5BneXrjFH1$jGJpIx5jFgUo8FSjrAqh.O4daLKNsybkUPoWJawPcX."; 135 | 136 | services.syncthing.cert = builtins.toFile "syncthing-cert" '' 137 | -----BEGIN CERTIFICATE----- 138 | MIICHTCCAaOgAwIBAgIJAN36G61Lv2lfMAoGCCqGSM49BAMCMEoxEjAQBgNVBAoT 139 | CVN5bmN0aGluZzEgMB4GA1UECxMXQXV0b21hdGljYWxseSBHZW5lcmF0ZWQxEjAQ 140 | BgNVBAMTCXN5bmN0aGluZzAeFw0yMTEwMjkwMDAwMDBaFw00MTEwMjQwMDAwMDBa 141 | MEoxEjAQBgNVBAoTCVN5bmN0aGluZzEgMB4GA1UECxMXQXV0b21hdGljYWxseSBH 142 | ZW5lcmF0ZWQxEjAQBgNVBAMTCXN5bmN0aGluZzB2MBAGByqGSM49AgEGBSuBBAAi 143 | A2IABCHVJtspig0zthYj74Y9B0zcLRDWPaTJxtvsY0UufLuKDK3UtzKNeAY8Z35c 144 | PmjcN7tIqqOENTtemnFgkDk36WmJRRMw4YY0kGLTqEdqpmCNhRUxwQFQnMjrgv8K 145 | r13Xg6NVMFMwDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggr 146 | BgEFBQcDAjAMBgNVHRMBAf8EAjAAMBQGA1UdEQQNMAuCCXN5bmN0aGluZzAKBggq 147 | hkjOPQQDAgNoADBlAjEA+zIz9AZ+w1B3OIzjb8QviWpPo8xWSoHSJJjQu7dsE2Z+ 148 | mN6999x5soD/i6P9pX/vAjB1jhur5Bsjp26h1lHzB7jvJCauH4/XWKwkDaH4VS1N 149 | O8cpIdmFMm03uszmurRQxe8= 150 | -----END CERTIFICATE----- 151 | ''; 152 | 153 | system.stateVersion = "21.11"; 154 | }; 155 | } 156 | -------------------------------------------------------------------------------- /machines/tsu.nix: -------------------------------------------------------------------------------- 1 | { 2 | identity = { 3 | isPhone = true; 4 | wireguard = { 5 | ipv4 = "10.42.3.1"; 6 | ipv6 = "fd42::3:1"; 7 | publicKey = "fRJFAT9BrQW5Wis3Jxq3mTR66IF6YlhvcCtMmjm78kI="; 8 | }; 9 | syncthing.id = "KXGLMP5-D2RKWZR-QUASDWC-T6H337M-HMEYLX7-D7EW4LM-UARXLZN-NXKVZAU"; 10 | }; 11 | } 12 | -------------------------------------------------------------------------------- /machines/u.nix: -------------------------------------------------------------------------------- 1 | { 2 | identity = { 3 | isPhone = true; 4 | wireguard = { 5 | ipv4 = "10.42.3.2"; 6 | ipv6 = "fd42::3:2"; 7 | publicKey = "mnEKscultRQ+e6z87hUDUKPnZwmiCCx+gxl/Jmt8fUg="; 8 | }; 9 | syncthing.id = "5LJKMOL-FHRI6G6-JXPTSOI-CYX5RNE-QSC567O-UZJ4RPB-O3FPQVU-OLXBCQ6"; 10 | }; 11 | } 12 | -------------------------------------------------------------------------------- /modules/cachix.nix: -------------------------------------------------------------------------------- 1 | { lib, config, pkgs, ... }: with lib; { 2 | options.cachix.derivationsToPush = mkOption { 3 | description = "A list of derivations to push to cachix."; 4 | type = with types; listOf package; 5 | default = []; 6 | }; 7 | 8 | config = { 9 | secrets.cachix = { 10 | owner = my.username; 11 | inherit (config.my) group; 12 | }; 13 | 14 | hm.xdg.configFile."cachix/cachix.dhall".source = config.hm.lib.file.mkOutOfStoreSymlink config.secrets.cachix.path; 15 | 16 | system.extraSystemBuilderCmds = '' 17 | { 18 | printf '%s\n' ${escapeShellArgs (concatMap getAllOutputs config.cachix.derivationsToPush)} 19 | grep -oE '\S*-man-cache' "$out/etc/man_db.conf" 2> /dev/null || true 20 | } > "$out/derivations-to-push" 21 | ''; 22 | environment.systemPackages = with pkgs; [ 23 | cachix 24 | (writeShellScriptBin "cachix-push" '' 25 | exec cachix push ${my.githubUsername} "$@" < /run/current-system/derivations-to-push 26 | '') 27 | ]; 28 | }; 29 | } 30 | -------------------------------------------------------------------------------- /modules/clipboard.nix: -------------------------------------------------------------------------------- 1 | { lib, this, pkgs, ... }: with lib; { 2 | config = mkMerge [ 3 | { 4 | environment.systemPackages = with pkgs; [ 5 | xsel 6 | (writeShellScriptBin "clip" '' 7 | clipin() { 8 | if [[ -v DISPLAY ]]; then 9 | xsel -bi 10 | else 11 | printf '\e]52;c;%s\a' "$(base64)" >&2 12 | fi 13 | } 14 | 15 | clipout() { xsel -bo 2> /dev/null; } 16 | 17 | newline=0 edit=0 unfold=0 18 | while getopts :neu o; do case $o in 19 | n) newline=1;; 20 | e) edit=1;; 21 | u) unfold=1;; 22 | esac done 23 | shift "$(( OPTIND - 1 ))" 24 | 25 | if (( edit )); then 26 | tmpfile=$(mktemp) || exit 27 | clipout > "$tmpfile" 28 | ''${EDITOR:-vim} "$tmpfile" 29 | clipin < "$tmpfile" 30 | rm -f -- "$tmpfile" 31 | elif (( unfold )); then 32 | clipout | tr -s '[:space:]' '[ *]' | clipin 33 | else 34 | if [[ -t 0 ]] && (( ! $# )); then 35 | clipout | awk 1 36 | else 37 | if (( $# )); then 38 | data=$* 39 | else 40 | data=$(< /dev/stdin) 41 | fi 42 | if (( newline )); then 43 | printf '%s\n' "$data" 44 | else 45 | printf '%s' "$data" 46 | fi | clipin 47 | fi 48 | fi 49 | '') 50 | ]; 51 | 52 | programs.bash.shellAliases = { 53 | cxa = "xargs -a <(clip)"; 54 | cxan = "xargs -a <(clip) -d'\\n'"; 55 | cxan1 = "xargs -a <(clip) -d'\\n' -n 1"; 56 | }; 57 | } 58 | 59 | (mkIf this.isStation { 60 | hm = { 61 | home.packages = [ pkgs.clipster ]; 62 | xdg.configFile."clipster/clipster.ini".text = '' 63 | [clipster] 64 | default_selection = CLIPBOARD 65 | history_size = 0 66 | extract_uris = no 67 | extract_emails = no 68 | ''; 69 | xsession.windowManager.bspwm.startupPrograms = [ "clipster -d" ]; 70 | }; 71 | }) 72 | ]; 73 | } 74 | -------------------------------------------------------------------------------- /modules/console.nix: -------------------------------------------------------------------------------- 1 | { lib, config, ... }: with lib; { 2 | console = { 3 | earlySetup = true; 4 | useXkbConfig = true; 5 | 6 | colors = with config.theme; map (removePrefix "#") [ 7 | "000000" hot cold hot cold hot cold foregroundAlt 8 | backgroundAlt hot cold hot cold hot cold foreground 9 | ]; 10 | }; 11 | 12 | environment.etc.issue.text = " \\e{magenta}\\n\\e{reset} | \\e{reset}\\l\\e{reset} | \\d \\t\n\n"; 13 | 14 | services.getty.extraArgs = [ "--nohostname" ]; 15 | } 16 | -------------------------------------------------------------------------------- /modules/direnv.nix: -------------------------------------------------------------------------------- 1 | { 2 | hm = { 3 | programs.direnv = { 4 | enable = true; 5 | nix-direnv.enable = true; 6 | config = { 7 | global.warn_timeout = "999h"; 8 | }; 9 | }; 10 | 11 | home.sessionVariables.DIRENV_LOG_FORMAT = ""; 12 | }; 13 | } 14 | -------------------------------------------------------------------------------- /modules/emacs.nix: -------------------------------------------------------------------------------- 1 | { pkgs, ... }: { 2 | hm.programs.emacs = { 3 | enable = true; 4 | package = pkgs.emacs-gtk; 5 | extraPackages = epkgs: [ epkgs.agda2-mode ]; 6 | }; 7 | } 8 | -------------------------------------------------------------------------------- /modules/environment/default.nix: -------------------------------------------------------------------------------- 1 | { inputs, lib, config, pkgs, ... }: with lib; { 2 | documentation = { 3 | nixos.enable = inputs.nixpkgs ? rev; 4 | dev.enable = true; 5 | }; 6 | 7 | environment.systemPackages = with pkgs; [ 8 | man-pages 9 | man-pages-posix 10 | rlwrap 11 | bat 12 | xxd 13 | dos2unix 14 | ripgrep 15 | file 16 | fd 17 | tree 18 | ncdu 19 | lsof 20 | lm_sensors 21 | hwinfo 22 | dosfstools 23 | mtools 24 | gptfdisk 25 | pciutils 26 | usbutils 27 | zip 28 | unzip 29 | binutils 30 | gcc 31 | gnumake 32 | http-server 33 | openssl 34 | bc 35 | fortune 36 | imagemagickBig 37 | ffmpeg-full 38 | jq 39 | htmlq 40 | python3 41 | (writeShellScriptBin "mutate" '' 42 | # replace a read-only symlink with a mutable copy 43 | f=''${1%/} 44 | if [[ -L $f ]]; then 45 | r=$(realpath -- "$f") 46 | rm -f -- "$f" 47 | cp -Tr --remove-destination --preserve=mode -- "$r" "$f" 48 | fi 49 | chmod -R u+w -- "$f" 50 | '') 51 | (shellScriptWith "upload" ./upload.sh {}) 52 | (shellScriptWith "order" ./order.sh {}) 53 | ]; 54 | 55 | programs.bcc.enable = true; # for execsnoop 56 | 57 | environment.etc.topdefaultrc.source = config.lib.meta.mkMutableSymlink ./toprc; 58 | 59 | hm.programs.htop = { 60 | enable = true; 61 | settings = { 62 | hide_kernel_threads = true; 63 | hide_userland_threads = true; 64 | show_thread_names = true; 65 | highlight_base_name = true; 66 | show_cpu_frequency = true; 67 | show_cpu_temperature = true; 68 | screen_tabs = true; 69 | }; 70 | }; 71 | 72 | hm.xdg.configFile."htop".force = true; 73 | 74 | hm.programs.yt-dlp = { 75 | enable = true; 76 | package = pkgs.yt-dlp.override { withAlias = true; }; 77 | settings = { 78 | cookies-from-browser = "firefox"; 79 | }; 80 | }; 81 | 82 | nixpkgs.overlays = [ (pkgs: prev: { 83 | inherit (pkgs.unstable) yt-dlp; 84 | shellScriptWith = name: src: { deps ? [], vars ? {} }: 85 | # can't use `writeScriptBin` because no check phase, 86 | # can't use `writeShellScriptBin` because no interactive shell 87 | pkgs.writeTextFile { 88 | inherit name; 89 | executable = true; 90 | destination = "/bin/${name}"; 91 | text = '' 92 | #!${config.my.shellPath} 93 | ${optionalString (deps != []) '' 94 | PATH=${makeBinPath deps}''${PATH+:$PATH} 95 | ''} 96 | ${toShellVars vars} 97 | ${readFile src} 98 | ''; 99 | checkPhase = '' 100 | ${pkgs.stdenv.shellDryRun} "$target" 101 | ''; 102 | }; 103 | 104 | pythonScriptWithDeps = name: src: deps: 105 | pkgs.stdenv.mkDerivation { 106 | inherit name; 107 | buildInputs = [ (pkgs.python3.withPackages deps) ]; 108 | dontUnpack = true; 109 | installPhase = '' 110 | install -D -m555 ${src} "$out/bin/${name}" 111 | ''; 112 | }; 113 | 114 | tmsu = prev.tmsu.overrideAttrs (o: { 115 | patches = o.patches or [] ++ [ (builtins.toFile "tmsu-patch" '' 116 | --- a/common/path/path.go 117 | +++ b/common/path/path.go 118 | @@ -92,14 +92 @@ func Dereference(path string) (string, error) { 119 | - stat, err := os.Lstat(path) 120 | - if err != nil { 121 | - return "", err 122 | - } 123 | - if stat.Mode()&os.ModeSymlink != 0 { 124 | - path, err := os.Readlink(path) 125 | - if err != nil { 126 | - return "", err 127 | - } 128 | - 129 | - return Dereference(path) 130 | - } 131 | - 132 | - return path, nil 133 | + return filepath.EvalSymlinks(path) 134 | '') ]; 135 | }); 136 | }) ]; 137 | } 138 | -------------------------------------------------------------------------------- /modules/environment/order.sh: -------------------------------------------------------------------------------- 1 | shopt -s nullglob lastpipe 2 | 3 | tmsu info &> /dev/null && tmsu=1 4 | 5 | get_extension() { 6 | local f=${1##*/} 7 | if [[ ${f#.} =~ (\.[[:alnum:]]{1,10})+$ ]] && 8 | [[ ${BASH_REMATCH[0]} =~ (\.[[:alnum:]]*[[:alpha:]][[:alnum:]]*)+$ ]]; then 9 | printf '%s\n' "${BASH_REMATCH[0]}" 10 | fi 11 | } 12 | 13 | dir=$(realpath -se "$1") || exit 14 | if [[ ! -d $dir ]]; then 15 | echo "not a directory" >&2 16 | exit 1 17 | fi 18 | shift 19 | 20 | # Gather files to order 21 | 22 | inside=() 23 | for f in "$dir"/*; do 24 | [[ -f $f ]] && inside+=("$f") 25 | done 26 | 27 | outside=() 28 | for f do 29 | f=$(realpath -se "$f") || continue 30 | if [[ ! -f $f ]]; then 31 | echo "not a regular file: $f" >&2 32 | continue 33 | fi 34 | [[ ! ${f%/*} -ef $dir ]] && outside+=("$f") 35 | done 36 | 37 | # Sort filenames version-style 38 | 39 | for f in "${inside[@]}"; do printf '%s\0' "$f"; done | 40 | sort -zV | 41 | readarray -d '' ordered 42 | ordered+=("${outside[@]}") 43 | 44 | # Rename files in two passes 45 | 46 | rename() { 47 | local source=$1 destination=$2 48 | printf '%s -> %s\n' "$source" "$destination" 49 | mv -n -- "$source" "$destination" && 50 | if (( tmsu )) && ! tmsu repair --manual "$source" "$destination"; then 51 | printf '%s -> %s\n' "$destination" "$source" 52 | mv -n -- "$destination" "$source" 53 | return 1 54 | fi 55 | } 56 | 57 | (( length = ${#inside[@]} + ${#outside[@]} )) 58 | length=${#length} i=1 59 | pending_source=() pending_destination=() 60 | for source in "${ordered[@]}"; do 61 | printf -v destination '%0*d' "$length" "$(( i++ ))" 62 | destination=$dir/$destination$(get_extension "$source") 63 | [[ $source -ef $destination ]] && continue 64 | final_destination=$destination 65 | while [[ -e $destination ]]; do destination+=_; done 66 | rename "$source" "$destination" || exit 67 | if [[ $destination != "$final_destination" ]]; then 68 | pending_source+=("$destination") 69 | pending_destination+=("$final_destination") 70 | fi 71 | done 72 | 73 | for (( i = 0; i < ${#pending_source[@]}; i++ )) do 74 | source=${pending_source[i]} destination=${pending_destination[i]} 75 | if [[ -e $destination ]] || ! rename "$source" "$destination"; then 76 | echo "could not rename $source to $destination" >&2 77 | exit 1 78 | fi 79 | done 80 | -------------------------------------------------------------------------------- /modules/environment/toprc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ncfavier/config/af3b9f8e7d424f98d5f586a2f26b84c73604750a/modules/environment/toprc -------------------------------------------------------------------------------- /modules/environment/upload.sh: -------------------------------------------------------------------------------- 1 | # Functions and variables 2 | 3 | die() { 4 | (( $# > 0 )) && printf '%s\n' "$1" >&2 5 | exit 1 6 | } 7 | 8 | tmpdir() { 9 | tmpdir=$(mktemp -d) || exit 10 | trap "rm -rf ${tmpdir@Q}" exit 11 | } 12 | 13 | build_url() { 14 | local file=$1 15 | jq -nr --arg host "$host" --arg file "$file" '"https://\($host)/\($file | split("/") | map(@uri) | join("/"))"' 16 | } 17 | 18 | extension() { 19 | [[ ${1#.} =~ (\.([[:alpha:]][[:alnum:]]{,9}|7z))+$ ]] && printf '%s\n' "${BASH_REMATCH[0]}" 20 | } 21 | 22 | . config env 23 | 24 | host=f.$domain 25 | uploads_dir=${synced[uploads]} # this should be the server's uploads dir, not the local one 26 | hash_length=6 27 | 28 | # Parse command line arguments 29 | 30 | # If $2 is set, it is used as the destination name unless -u is used. 31 | # If $2 is unset, a random name is used unless -k is used (in which case the source filename is used). 32 | 33 | keep_name=0 random_name=0 remove=0 force=0 interactive=0 34 | while getopts :l:kurf o; do case $o in 35 | :) OPTARG=1 ;& 36 | l) 37 | n=$OPTARG 38 | ssh -q "$host" "find ${uploads_dir@Q} -name .stversions -prune -o -type f -printf '%T@/%P\0'" | 39 | sort -znst / -k 1,1 | tail -zn "$n" | 40 | while IFS=/ read -rd '' time path; do 41 | build_url "$path" 42 | done 43 | exit 44 | ;; 45 | k) keep_name=1;; 46 | u) random_name=1;; 47 | r) remove=1;; 48 | f) force=1;; 49 | esac done 50 | shift "$(( OPTIND - 1 ))" 51 | [[ -t 1 ]] && interactive=1 52 | source=$1 53 | if [[ $2 ]]; then 54 | basename=$2 55 | keep_name=$(( ! random_name )) 56 | else 57 | basename=$source 58 | fi 59 | basename=${basename##*/} 60 | 61 | # Figure out what the source is 62 | 63 | if [[ ! $source || $source == - ]]; then 64 | tmpdir 65 | basename=stdin 66 | printf -v source '%s/stdin-%(%F-%H%M%S)T' "$tmpdir" -1 67 | cat > "$source" 68 | elif [[ $source == +([[:alpha:]])://* ]]; then 69 | tmpdir 70 | (cd "$tmpdir" && curl -fsSLOJ "$source") || exit 71 | source=("$tmpdir"/*) 72 | else 73 | source=$(realpath "$source") 74 | [[ -e $source ]] || die "source not found" 75 | [[ -f $source && -r $source ]] || die "source must be a readable regular file" 76 | fi 77 | 78 | # Figure out what the destination should be 79 | 80 | if (( keep_name )); then 81 | destination=$basename 82 | else 83 | hash=$(openssl dgst -sha1 -binary "$source" | basenc --base64url) 84 | destination=${hash::hash_length}$(extension "$basename") 85 | fi 86 | 87 | # Upload 88 | 89 | rsync_opts=(--progress --protect-args --chmod=D755,F644) 90 | (( remove )) && rsync_opts+=(--remove-source-files) 91 | (( ! force )) && rsync_opts+=(--ignore-existing) 92 | (( isServer )) && rsync_host= || rsync_host=$host: 93 | rsync "${rsync_opts[@]}" "$source" "$rsync_host$uploads_dir/$destination" || exit 94 | 95 | # Report 96 | 97 | url=$(build_url "$destination") 98 | printf '%s\n' "$url" | tee >(clip) 99 | 100 | if (( ! interactive )) && [[ $(dunstify -t 10000 -A open,open -i checkmark "Uploaded $basename" "to $url") == open ]]; then 101 | exec xdg-open "$url" &> /dev/null 102 | fi & 103 | -------------------------------------------------------------------------------- /modules/fonts.nix: -------------------------------------------------------------------------------- 1 | { lib, this, config, pkgs, ... }: with lib; { 2 | config = mkMerge [ 3 | { 4 | fonts.fontconfig.enable = mkDefault false; 5 | } 6 | 7 | (mkIf this.isStation { 8 | nixpkgs.config.allowUnfree = true; 9 | 10 | console.font = pkgs.runCommandLocal "dina.psf" {} '' 11 | cd ${pkgs.bdf2psf}/share/bdf2psf 12 | ${if config.services.xserver.dpi != null && config.services.xserver.dpi >= 100 then 13 | "sed 's/POINT_SIZE 100/AVERAGE_WIDTH 80/' ${pkgs.dina-font.bdf}/share/fonts/misc/Dina_r400-10.bdf" 14 | else 15 | "sed 's/POINT_SIZE 80/AVERAGE_WIDTH 70/' ${pkgs.dina-font.bdf}/share/fonts/misc/Dina_r400-8.bdf" 16 | } | 17 | ${pkgs.bdf2psf}/bin/bdf2psf --fb - standard.equivalents ascii.set+useful.set+linux.set 256 "$out" 18 | ''; 19 | 20 | fonts = { 21 | packages = with pkgs; [ 22 | source-serif 23 | source-sans 24 | source-code-pro 25 | source-han-serif 26 | source-han-sans 27 | source-han-mono 28 | iosevka 29 | julia-mono 30 | jetbrains-mono 31 | roboto 32 | inriafonts 33 | twitter-color-emoji 34 | noto-fonts 35 | noto-fonts-emoji 36 | symbola 37 | dina-font 38 | tewi-font 39 | efont-unicode 40 | siji 41 | babelstone-han 42 | eb-garamond 43 | crimson-pro 44 | alice 45 | libre-baskerville 46 | libertinus 47 | ]; 48 | fontconfig = { 49 | enable = true; 50 | defaultFonts = { 51 | serif = [ "Libertinus Serif" "Source Serif 4" "Source Han Serif" "emoji" ]; 52 | sansSerif = [ "Source Han Sans" "Source Sans 3" "emoji" ]; # Source Han Sans includes most glyphs from Source Sans and is slightly larger 53 | monospace = [ "JuliaMono" "Source Code Pro" "Source Han Mono" "emoji" ]; 54 | emoji = [ "Twitter Color Emoji" "Noto Color Emoji" "Symbola" ]; 55 | }; 56 | # test: ™ ´ ” ☺ 🦢 🪿 π œuf ✓ → ∀ ⬛🟩 57 | localConf = '' 58 | 59 | 60 | 61 | 62 | bitmap 63 | 64 | Dina 65 | tewi 66 | Biwidth 67 | emoji 68 | 69 | 70 | 71 | Helvetica 72 | 73 | sans-serif 74 | 75 | 76 | 77 | Arial 78 | 79 | sans-serif 80 | 81 | 82 | 83 | Liberation Mono 84 | 85 | monospace 86 | 87 | 88 | 89 | 90 | 91 | Biwidth 92 | 10 93 | 94 | 95 | 96 | 97 | Lucida 98 | 99 | 100 | 101 | 102 | ''; 103 | }; 104 | }; 105 | 106 | environment.systemPackages = with pkgs; [ 107 | gucharmap 108 | (writeShellScriptBin "show-siji" '' 109 | exec ${xorg.xfd}/bin/xfd -rows 23 -columns 28 -fn '-*-siji-*-10-*' 110 | '') 111 | ]; 112 | }) 113 | ]; 114 | } 115 | -------------------------------------------------------------------------------- /modules/gpg.nix: -------------------------------------------------------------------------------- 1 | { lib, pkgs, ... }: with lib; { 2 | hm = { 3 | programs.gpg = { 4 | enable = true; 5 | publicKeys = [ 6 | { 7 | text = '' 8 | -----BEGIN PGP PUBLIC KEY BLOCK----- 9 | 10 | mDMEYq3M7RYJKwYBBAHaRw8BAQdAD0qPdmOei7rDi1mfrRIAhi+sWUVCFKIpsvFN 11 | H2xULUi0Gk5hw69tIEZhdmllciA8bkBtb25hZGUubGk+iI4EExYKADYWIQTz60u7 12 | TnGZvCmc1OmVr86CEZCDJQUCYq3M7QIbAwQLCQgHBBUKCQgFFgIDAQACHgUCF4AA 13 | CgkQla/OghGQgyXAEgD+M15YHzHgEbOzhoe05aPUeiYnh+nYQf5S67spAKyHRjsA 14 | /2a7nN52rUH/y4pCJe0Xho6mLNTV7f16ekYjJ63jQg4IuDgEYq3M7RIKKwYBBAGX 15 | VQEFAQEHQCJ2WnPk71SVYHcxyTrlQVDk41D5GDxH+5WMG/FVeN5IAwEIB4h4BBgW 16 | CgAgFiEE8+tLu05xmbwpnNTpla/OghGQgyUFAmKtzO0CGwwACgkQla/OghGQgyVu 17 | LQD+Me/EWhtHQA9wL+fGjIY8MuOgmqWHTGlz4+2jRFMRRz8BAM5v2vLo2nlHZmc/ 18 | tLCXQmUD4h/3EUzWkHhg3X5UjK4D 19 | =M5kE 20 | -----END PGP PUBLIC KEY BLOCK----- 21 | ''; 22 | trust = "ultimate"; 23 | } 24 | { 25 | text = '' 26 | -----BEGIN PGP PUBLIC KEY BLOCK----- 27 | 28 | mQENBF9Ne7UBCAD7KZW1RCBXJY1uDLbmaDUm50eshkv1rT8eK0JJXR3MfuCaJ/Kq 29 | rg547ZjczxED98Qy8A7d1BrIsOiKEoFVou+jCcjU19hlkQiMce3IZmYm0h6MOmZq 30 | B0MR6EGTlAgDfkiDMYqnAUGst4p2xqqmH/gM/UI2d5ZFrxAbK+PC4d7yMxs5QJkJ 31 | 0buXRnbKL/LGRWwyUCV8UDzQ26kYufVyAhS2Iz2SvUSqca5BaJOzAPJ74CFScbIC 32 | FK5nlsc2kHH35ZqK3f1Jxmbpi8ZwXUyxT+pFUClzY/s5H4w8c70ItvOyD3T0B+a8 33 | MF2Ft/c1kLFnHfYJd2FET+RZJQ5P+kXW+iZbABEBAAG0Gk5hw69tIEZhdmllciA8 34 | bkBtb25hZGUubGk+iQFOBBMBCAA4FiEEUaBwXn3SPLxeqrQ+SbBzIlgLfuIFAl9N 35 | e7UCGy8FCwkIBwIGFQoJCAsCBBYCAwECHgECF4AACgkQSbBzIlgLfuK0wwgAn0YY 36 | 2hpWW789Mi5pmCrguPqmG3hingYjzM44XvPMt2pZcximyiX5ZzVfLwdgG4y/1/Qy 37 | 5LIUYHb85aD0SZtPtB0Jm4Luqnm3WntuYfysKxbWg7/IiRbxkyJ8UTkO0/ETAyce 38 | 1pubyF0L2jGcW2xiraBqlHe4ben8f7Fds4ReNwgFgexy+avEASXmlSeE1LCRFckG 39 | +6UMTwlPIdWWlYRBZMVT5e6kSLewBoGXF9Gs/trHwInG5lttKJEnoqMDh/nl/h5i 40 | /5EP6GzSVd9wYUSwEdI+ktDUB7QJdbxTgIWREZzxPG8ZxetXFEYN8K4w2UCwyemF 41 | D+/b5UHVjOB+8SVA4Q== 42 | =8IGY 43 | -----END PGP PUBLIC KEY BLOCK----- 44 | ''; 45 | trust = "ultimate"; 46 | } 47 | ]; 48 | }; 49 | 50 | services.gpg-agent = { 51 | enable = false; 52 | pinentryPackage = pkgs.pinentry-gtk2; 53 | enableSshSupport = true; 54 | sshKeys = [ 55 | "2EBD4F5F9AAAE5BCC8FB0A3CC9BE73CAACA66D33" 56 | "D10BD70AF981C671C8EE4D288F23BAE560675CA3" 57 | ]; 58 | }; 59 | }; 60 | } 61 | -------------------------------------------------------------------------------- /modules/haskell.nix: -------------------------------------------------------------------------------- 1 | {lib, pkgs, ... }: with lib; { 2 | environment.systemPackages = with pkgs; [ 3 | (haskellPackages.ghcWithHoogle (p: with p; [ 4 | adjunctions 5 | aeson 6 | arithmoi 7 | array 8 | comonad 9 | containers 10 | kan-extensions 11 | lens 12 | lens-aeson 13 | linear 14 | megaparsec 15 | MemoTrie 16 | mtl 17 | profunctors 18 | QuickCheck 19 | random 20 | recursion-schemes 21 | split 22 | unordered-containers 23 | vector 24 | ])) 25 | cabal2nix 26 | haskell-language-server 27 | ]; 28 | 29 | hm.programs.haskell.cabal = { 30 | enable = true; 31 | config = '' 32 | repository hackage.haskell.org 33 | url: http://hackage.haskell.org/ 34 | 35 | username: ncfavier 36 | password-command: pass hackage 37 | ''; 38 | }; 39 | 40 | hm.programs.haskell.stack = { 41 | enable = true; 42 | settings = { 43 | nix = { 44 | enable = true; 45 | packages = [ "zlib" ]; 46 | }; 47 | }; 48 | }; 49 | 50 | hm.home.file = { 51 | ".ghc/ghci.conf".text = '' 52 | :set prompt "> " 53 | :set prompt-cont "| " 54 | :def hoogle \x -> pure $ ":!hoogle search \"" ++ x ++ "\"" 55 | :set +t 56 | :set -XArrows 57 | :set -XBangPatterns 58 | :set -XBinaryLiterals 59 | :set -XBlockArguments 60 | :set -XConstraintKinds 61 | :set -XDataKinds 62 | :set -XDeriveTraversable 63 | :set -XDerivingVia 64 | :set -XEmptyCase 65 | :set -XEmptyDataDecls 66 | :set -XFlexibleContexts 67 | :set -XFlexibleInstances 68 | :set -XGADTs 69 | :set -XGeneralisedNewtypeDeriving 70 | :set -XImportQualifiedPost 71 | :set -XLambdaCase 72 | :set -XLiberalTypeSynonyms 73 | :set -XMonadComprehensions 74 | :set -XMultiWayIf 75 | :set -XNamedFieldPuns 76 | :set -XNumericUnderscores 77 | :set -XOverloadedStrings 78 | :set -XPatternSynonyms 79 | :set -XPolyKinds 80 | :set -XRankNTypes 81 | :set -XRecordWildCards 82 | :set -XRecursiveDo 83 | :set -XScopedTypeVariables 84 | :set -XStandaloneDeriving 85 | :set -XTupleSections 86 | :set -XTypeApplications 87 | :set -XTypeOperators 88 | :set -XUnicodeSyntax 89 | :set -XViewPatterns 90 | import Control.Applicative 91 | import Control.Arrow 92 | import Control.Lens 93 | import Control.Monad 94 | import Data.Bifunctor 95 | import Data.Bits 96 | import Data.Char 97 | import Data.Complex 98 | import Data.Either 99 | import Data.Foldable 100 | import Data.Function 101 | import Data.Functor 102 | import Data.Functor.Identity 103 | import Data.Ix 104 | import Data.List 105 | import Data.Map (Map) 106 | import Data.Map qualified as Map 107 | import Data.Maybe 108 | import Data.Monoid 109 | import Data.Ord 110 | import Data.Ratio 111 | import Data.Semigroup 112 | import Data.Set (Set) 113 | import Data.Set qualified as Set 114 | import Data.String 115 | import Data.Traversable 116 | import Data.Tuple 117 | import Data.Void 118 | import System.Environment 119 | import System.Exit 120 | import System.IO 121 | import System.Random 122 | import Test.QuickCheck 123 | import Text.Read 124 | ''; 125 | 126 | ".haskeline".text = '' 127 | bind: up meta-k 128 | bind: down meta-j 129 | maxhistorysize: Just 5000 130 | historyDuplicates: IgnoreAll 131 | ''; 132 | }; 133 | } 134 | -------------------------------------------------------------------------------- /modules/home-manager.nix: -------------------------------------------------------------------------------- 1 | { inputs, lib, config, ... }: with lib; { 2 | imports = [ 3 | inputs.home-manager.nixosModules.default 4 | (mkAliasOptionModule [ "hm" ] [ "home-manager" "users" my.username ]) 5 | ]; 6 | 7 | system.extraDependencies = collectFlakeInputs inputs.home-manager ++ collectFlakeInputs inputs.xhmm; 8 | 9 | home-manager = { 10 | useGlobalPkgs = true; 11 | useUserPackages = true; 12 | verbose = true; 13 | extraSpecialArgs = { inherit inputs; }; 14 | }; 15 | 16 | hm = { 17 | imports = [ 18 | inputs.xhmm.homeManagerModules.languages.haskell 19 | ]; 20 | 21 | home.stateVersion = config.system.stateVersion; 22 | home.enableNixpkgsReleaseCheck = false; 23 | 24 | systemd.user.startServices = "sd-switch"; 25 | 26 | manual.html.enable = true; 27 | }; 28 | } 29 | -------------------------------------------------------------------------------- /modules/jellyfin.nix: -------------------------------------------------------------------------------- 1 | { lib, config, ... }: with lib; mkEnableModule [ "my-services" "jellyfin" ] { 2 | services.jellyfin = { 3 | enable = true; 4 | openFirewall = true; 5 | }; 6 | 7 | systemd.services.jellyfin.wantedBy = mkForce []; 8 | systemd.services.jellyfin.serviceConfig.ProtectHome = "read-only"; 9 | systemd.services.jellyfin.serviceConfig.BindReadOnlyPaths = [ 10 | "${config.hm.xdg.userDirs.videos}:/srv/videos" 11 | ]; 12 | } 13 | -------------------------------------------------------------------------------- /modules/localisation.nix: -------------------------------------------------------------------------------- 1 | { lib, config, ... }: with lib; { 2 | options.keys = { 3 | composeKey = mkOption { 4 | type = types.enum [ "ralt" "lwin" "lwin-altgr" "rwin" "rwin-altgr" "menu" "menu-altgr" "lctrl" "lctrl-altg" "rctrl" "rctrl-altg" "caps" "caps-altgr" "102" "102-altgr" "paus" "prsc" "sclk" ]; 5 | default = "menu"; 6 | }; 7 | 8 | printScreenKey = mkOption { 9 | type = types.str; 10 | default = "Print"; 11 | }; 12 | }; 13 | 14 | config = { 15 | i18n.defaultLocale = "en_GB.UTF-8"; 16 | 17 | services.xserver.xkb = { 18 | layout = "fr-my,us,ru,gr"; 19 | variant = ",,phonetic_azerty,"; 20 | options = "grp:shifts_toggle,compose:${config.keys.composeKey},caps:escape"; 21 | 22 | extraLayouts.fr-my = { 23 | languages = [ "fra" ]; 24 | description = "Modified French layout"; 25 | symbolsFile = builtins.toFile "fr-my" '' 26 | xkb_symbols "fr-my" { 27 | include "fr(oss)" 28 | key { [ grave, twosuperior ] }; 29 | key { [ asterisk, dead_greek, dead_grave, dead_macron ] }; 30 | key { [ semicolon, period, multiply, U00B7 ] }; 31 | }; 32 | ''; 33 | }; 34 | }; 35 | 36 | time.timeZone = "Europe/Stockholm"; 37 | }; 38 | } 39 | -------------------------------------------------------------------------------- /modules/secrets.nix: -------------------------------------------------------------------------------- 1 | { inputs, lib, this, config, pkgs, ... }: with lib; { 2 | imports = [ 3 | inputs.sops-nix.nixosModules.default 4 | (mkAliasOptionModule [ "secrets" ] [ "sops" "secrets" ]) 5 | ]; 6 | 7 | system.extraDependencies = collectFlakeInputs inputs.sops-nix; 8 | 9 | sops = { 10 | gnupg.sshKeyPaths = []; 11 | age.sshKeyPaths = [ "${config.my.home}/.ssh/id_ed25519" ]; 12 | 13 | secrets = let 14 | secretsDir = "${inputs.self}/secrets"; 15 | in mapAttrs' (name: _: let 16 | parts = splitString "." name; 17 | base = head parts; 18 | format = if length parts > 1 then elemAt parts 1 else "binary"; 19 | in nameValuePair base { 20 | sopsFile = "${secretsDir}/${name}"; 21 | inherit format; 22 | key = this.hostname; 23 | }) (builtins.readDir secretsDir); 24 | }; 25 | 26 | my.extraGroups = [ "keys" ]; 27 | 28 | environment = { 29 | systemPackages = [ pkgs.sops pkgs.age ]; 30 | }; 31 | 32 | hm = mkMerge [ 33 | { 34 | home.activation.generateAgeKeyfile = '' 35 | mkdir -p "''${XDG_CONFIG_HOME:-$HOME/.config}/sops/age" 36 | ${getExe pkgs.ssh-to-age} -private-key -i ${head config.sops.age.sshKeyPaths} > "''${XDG_CONFIG_HOME:-$HOME/.config}/sops/age/keys.txt" || true 37 | ''; 38 | } 39 | 40 | (mkIf config.services.syncthing.enable { 41 | programs.password-store = { 42 | enable = true; 43 | package = pkgs.pass.withExtensions (e: [ e.pass-otp ]); 44 | }; 45 | 46 | xdg.dataFile.password-store.source = 47 | config.hm.lib.file.mkOutOfStoreSymlink config.synced.password-store.path; 48 | }) 49 | ]; 50 | 51 | nix.settings = { 52 | substituters = [ "https://mic92.cachix.org" ]; 53 | trusted-public-keys = [ "mic92.cachix.org-1:gi8IhgiT3CYZnJsaW7fxznzTkMUOn1RY4GmXdT/nXYQ=" ]; 54 | }; 55 | } 56 | -------------------------------------------------------------------------------- /modules/server/acme.nix: -------------------------------------------------------------------------------- 1 | { lib, ... }: with lib; { 2 | security.acme = { 3 | acceptTerms = true; 4 | defaults.email = "acme@${my.domain}"; 5 | }; 6 | } 7 | -------------------------------------------------------------------------------- /modules/server/default.nix: -------------------------------------------------------------------------------- 1 | { lib, this, ... }: with lib; optionalAttrs this.isServer { 2 | imports = attrValues (modulesIn ./.); 3 | 4 | networking.firewall.allowedTCPPorts = [ 5 | 25565 # minecraft 6 | ]; 7 | } 8 | -------------------------------------------------------------------------------- /modules/server/dns.nix: -------------------------------------------------------------------------------- 1 | { inputs, lib, this, config, ... }: with lib; let 2 | dns = inputs.dns.lib; 3 | in { 4 | system.extraDependencies = collectFlakeInputs inputs.dns; 5 | 6 | services.nsd = { 7 | enable = true; 8 | interfaces = this.ipv4 ++ this.ipv6; 9 | ipTransparent = true; 10 | ratelimit.enable = true; 11 | 12 | zones = with dns.combinators; let 13 | soa = { 14 | SOA = { 15 | nameServer = "@"; 16 | adminEmail = "dns@${my.domain}"; 17 | serial = 0; 18 | }; 19 | NS = [ "@" ]; 20 | }; 21 | aaaaa = { 22 | A = map a this.ipv4; 23 | AAAA = map aaaa this.ipv6; 24 | }; 25 | in { 26 | # "yoneda.ninja".data = dns.toString "yoneda.ninja" (soa // aaaaa // { 27 | # TTL = 60; 28 | # CAA = letsEncrypt "dns+caa@${my.domain}"; 29 | # subdomains."*" = aaaaa; 30 | # }); 31 | 32 | "grove.monade.li".data = dns.toString "grove.monade.li" (soa // aaaaa // { 33 | TTL = 60; 34 | TXT = [ "across old bark" "in the ancient glade" "it's always dark" "the quiet shade" ]; 35 | }); 36 | 37 | ${my.domain}.data = dns.toString my.domain (soa // aaaaa // { 38 | TTL = 60 * 60; 39 | 40 | MX = [ (mx.mx 10 "@") ]; 41 | DKIM = optional (config.lib ? dkim) { 42 | selector = "mail"; 43 | p = config.lib.dkim.pk; 44 | }; 45 | DMARC = [ { 46 | p = "quarantine"; 47 | sp = "quarantine"; 48 | rua = "mailto:dmarc@${my.domain}"; 49 | } ]; 50 | 51 | TXT = [ 52 | (spf.strict [ "mx" ]) 53 | "google-site-verification=yIwF9ILuYq54P151RraAs06TuJQMLZXKPRXSdn8FJWc" 54 | ]; 55 | 56 | CAA = letsEncrypt "dns+caa@${my.domain}"; 57 | 58 | SRV = [ 59 | { 60 | proto = "tcp"; 61 | service = "imaps"; 62 | target = "@"; 63 | port = 993; 64 | } 65 | { 66 | proto = "tcp"; 67 | service = "submission"; 68 | target = "@"; 69 | port = 465; 70 | } 71 | ]; 72 | 73 | subdomains = rec { 74 | "*" = aaaaa; 75 | 76 | github.CNAME = [ "${my.githubUsername}.github.io." ]; 77 | glam = github; 78 | agda = github; 79 | }; 80 | }); 81 | }; 82 | }; 83 | 84 | services.unbound = mkIf (this ? wireguard) { 85 | enable = true; 86 | localControlSocketPath = "/run/unbound/unbound.ctl"; 87 | settings = { 88 | server = { 89 | tls-system-cert = true; 90 | interface = [ 91 | "127.0.0.1" "::1" 92 | this.wireguard.ipv4 this.wireguard.ipv6 93 | ]; 94 | access-control = [ 95 | "127.0.0.0/8 allow" "::1/128 allow" 96 | "${config.networking.wireguard.subnetv4} allow" "${config.networking.wireguard.subnetv6} allow" 97 | ]; 98 | private-address = with config.networking.wireguard; [ 99 | subnetv4 subnetv6 100 | ]; 101 | local-data = concatLists (mapAttrsToList (n: m: [ 102 | ''"${n}.${config.networking.wireguard.interface}. A ${m.wireguard.ipv4}"'' 103 | ''"${n}.${config.networking.wireguard.interface}. AAAA ${m.wireguard.ipv6}"'' 104 | ]) (my.machinesWith "wireguard")) ++ [ 105 | ''"fu.home. A 192.168.1.2"'' 106 | ''"mo.home. A 192.168.1.3"'' 107 | ''"tsu.home. A 192.168.1.4"'' 108 | ''"no.home. A 192.168.1.5"'' 109 | ''"printer.home. A 192.168.1.63"'' 110 | ]; 111 | local-data-ptr = concatLists (mapAttrsToList (n: m: [ 112 | ''"${m.wireguard.ipv4} ${n}.${config.networking.wireguard.interface}."'' 113 | ''"${m.wireguard.ipv6} ${n}.${config.networking.wireguard.interface}."'' 114 | ]) (my.machinesWith "wireguard")); 115 | }; 116 | forward-zone = [ { 117 | name = "."; 118 | forward-addr = config.services.resolved.fallbackDns; 119 | forward-tls-upstream = true; 120 | } ]; 121 | }; 122 | }; 123 | 124 | my.extraGroups = [ config.services.unbound.group ]; 125 | 126 | networking.nameservers = [ "127.0.0.1" "::1" ]; 127 | 128 | networking.firewall = { 129 | allowedTCPPorts = [ 53 ]; 130 | allowedUDPPorts = [ 53 ]; 131 | }; 132 | } 133 | -------------------------------------------------------------------------------- /modules/server/dwarf-fortress.nix: -------------------------------------------------------------------------------- 1 | { lib, config, pkgs, ... }: with lib; let 2 | wsPort = 4321; 3 | password = "quineapple"; # not meant to be secure 4 | 5 | df = pkgs.dwarf-fortress-packages.dwarf-fortress_0_47_04.override (oldArgs: let 6 | dfplex = pkgs.fetchFromGitHub { 7 | owner = "ncfavier"; 8 | repo = "dfplex"; 9 | rev = "49f850281f1871aa9cf5d7c5e944afe53fb4fe53"; 10 | fetchSubmodules = true; 11 | hash = "sha256-8jufSfbjnIhFfNMAfpRX7KeKUkyfMbViOkX/UfauneI="; 12 | }; 13 | dfhack = oldArgs.dfhack.overrideAttrs (o: { 14 | preConfigure = o.preConfigure or "" + '' 15 | ln -s ${dfplex}/server plugins/dfplex 16 | echo 'add_subdirectory(dfplex)' > plugins/CMakeLists.custom.txt 17 | ''; 18 | nativeBuildInputs = o.nativeBuildInputs or [] ++ [ pkgs.boost16x ]; 19 | }); 20 | in { 21 | enableTextMode = true; 22 | enableTruetype = false; 23 | enableSound = false; 24 | enableIntro = false; 25 | enableFPS = true; 26 | enableDFHack = true; 27 | inherit dfhack; 28 | extraPackages = [ 29 | (pkgs.writeTextDir "dfhack.init" '' 30 | enable title-version 31 | '') 32 | (pkgs.writeTextDir "onLoad.init" '' 33 | enable dfplex 34 | '') 35 | (pkgs.writeTextDir "onUnload.init" '' 36 | disable dfplex 37 | '') 38 | (pkgs.concatTextFile { 39 | name = "config.js"; 40 | destination = "/hack/www/config.js"; 41 | files = [ "${dfhack}/hack/www/config.js" (builtins.toFile "config-custom.js" '' 42 | config.port = ${toString wsPort}; 43 | config.secret = ${builtins.toJSON password}; 44 | config.tiles = "Curses.png"; 45 | config.text = "Curses.png"; 46 | config.overworld = "Curses.png"; 47 | '') ]; 48 | }) 49 | ]; 50 | settings = { 51 | init.FPS_CAP = 60; 52 | d_init.AUTOSAVE = "SEASONAL"; 53 | dfplex = { 54 | PORT = wsPort; 55 | STATICPORT = 0; 56 | AUTH_REQUIRED = 1; 57 | UNIPLEX_READONLY = 1; 58 | MULTIPLEXKEY = 0; 59 | }; 60 | announcements = let 61 | don'tPause = "A_D:D_D"; 62 | in { 63 | BIRTH_CITIZEN = don'tPause; 64 | MOOD_BUILDING_CLAIMED = don'tPause; 65 | ARTIFACT_BEGUN = don'tPause; 66 | }; 67 | }; 68 | }); 69 | 70 | tmuxSocket = "/run/df/tmux.socket"; 71 | tmuxConfig = builtins.toFile "tmux-df.conf" '' 72 | set -g status off 73 | bind -n C-q detach 74 | ''; 75 | dfDir = "/run/df/state"; 76 | in mkEnableModule [ "my-services" "dwarf-fortress" ] { 77 | nixpkgs.config.allowUnfree = true; 78 | 79 | systemd.services.dwarf-fortress = { 80 | description = "Dwarf Fortress + DFPlex in a tmux session"; 81 | wantedBy = [ "multi-user.target" ]; 82 | environment = { 83 | SHELL = getExe pkgs.bashInteractive; # otherwise tmux uses nologin 84 | DF_DIR = dfDir; 85 | DFPLEX_SECRET = password; 86 | }; 87 | serviceConfig = { 88 | DynamicUser = true; 89 | RuntimeDirectory = "df"; 90 | BindPaths = [ "${config.my.home}/df:${dfDir}" ]; 91 | 92 | Type = "forking"; 93 | ExecStart = "${pkgs.tmux}/bin/tmux -S ${tmuxSocket} -f ${tmuxConfig} new-session -d ${df}/bin/dfhack"; 94 | ExecStop = "-${pkgs.tmux}/bin/tmux -S ${tmuxSocket} kill-server"; 95 | 96 | CapabilityBoundingSet = ""; 97 | DevicePolicy = "closed"; 98 | LockPersonality = true; 99 | MemoryDenyWriteExecute = true; 100 | NoNewPrivileges = true; 101 | PrivateDevices = true; 102 | PrivateMounts = true; 103 | PrivateTmp = true; 104 | PrivateUsers = true; 105 | ProcSubset = "pid"; 106 | ProtectClock = true; 107 | ProtectControlGroups = true; 108 | ProtectHome = true; 109 | ProtectHostname = true; 110 | ProtectKernelLogs = true; 111 | ProtectKernelModules = true; 112 | ProtectKernelTunables = true; 113 | ProtectProc = "invisible"; 114 | ProtectSystem = "strict"; 115 | RemoveIPC = true; 116 | RestrictAddressFamilies = [ "AF_INET" "AF_INET6" "AF_UNIX" ]; 117 | RestrictNamespaces = true; 118 | RestrictRealtime = true; 119 | RestrictSUIDSGID = true; 120 | SystemCallArchitectures = "native"; 121 | SystemCallFilter = [ "@system-service" "~@privileged" "~@resources" ]; 122 | }; 123 | restartIfChanged = false; 124 | }; 125 | 126 | networking.firewall.allowedTCPPorts = [ wsPort ]; 127 | 128 | services.nginx.virtualHosts."df.${my.domain}" = { 129 | root = "${df.env}/hack/www"; 130 | basicAuth.df = password; 131 | locations."/".index = "dfplex.html"; 132 | }; 133 | 134 | environment.systemPackages = [ 135 | (pkgs.writeShellScriptBin "df-attach" '' 136 | sudo tmux -S ${tmuxSocket} attach "$@" 137 | '') 138 | ]; 139 | } 140 | -------------------------------------------------------------------------------- /modules/server/lambdabot.nix: -------------------------------------------------------------------------------- 1 | { lib, config, pkgs, ... }: with lib; let 2 | tunnelPort = 6642; 3 | in mkEnableModule [ "my-services" "lambdabot" ] { 4 | secrets.lambdabot-ulminfo = { 5 | owner = config.users.users.lambdabot.name; 6 | group = config.users.users.lambdabot.group; 7 | }; 8 | 9 | services.lambdabot = { 10 | enable = true; 11 | package = let 12 | packages = [ 13 | "adjunctions" 14 | "arithmoi" 15 | "array" 16 | "comonad" 17 | "containers" 18 | "kan-extensions" 19 | "lens" 20 | "linear" 21 | "megaparsec" 22 | "mtl" 23 | "profunctors" 24 | "split" 25 | "unordered-containers" 26 | "vector" 27 | ]; 28 | in pkgs.lambdabot.override { 29 | packages = attrVals packages; 30 | configuration = ''[ 31 | textWidth ==> 380, 32 | commandPrefixes ==> ["@"], 33 | evalPrefixes ==> ["%"], 34 | trustedPackages ==> [ 35 | "base", 36 | "bifunctors", 37 | "bytestring", 38 | "ghc-prim", 39 | "lambdabot-trusted", 40 | "random", 41 | "semigroupoids", 42 | "text", 43 | ${concatMapStringsSep ", " (p: ''"${pkgs.haskellPackages.${p}.pname or p}"'') packages} 44 | ], 45 | languageExts ==> [ 46 | "Arrows", 47 | "BangPatterns", 48 | "BinaryLiterals", 49 | "BlockArguments", 50 | "ConstrainedClassMethods", 51 | "ConstraintKinds", 52 | "DataKinds", 53 | "DeriveDataTypeable", 54 | "DeriveFoldable", 55 | "DeriveFunctor", 56 | "DeriveGeneric", 57 | "DeriveLift", 58 | "DeriveTraversable", 59 | "DerivingVia", 60 | "DoAndIfThenElse", 61 | "EmptyCase", 62 | "EmptyDataDecls", 63 | "EmptyDataDeriving", 64 | "ExistentialQuantification", 65 | "ExplicitForAll", 66 | "ExtendedDefaultRules", 67 | "FlexibleContexts", 68 | "FlexibleInstances", 69 | "GADTs", 70 | "HexFloatLiterals", 71 | "ImplicitPrelude", 72 | "ImportQualifiedPost", 73 | "InstanceSigs", 74 | "KindSignatures", 75 | "LambdaCase", 76 | "LiberalTypeSynonyms", 77 | "LinearTypes", 78 | "MonadComprehensions", 79 | "MultiParamTypeClasses", 80 | "MultiWayIf", 81 | "NamedFieldPuns", 82 | "NamedWildCards", 83 | "NoGeneralisedNewtypeDeriving", 84 | "NoMonomorphismRestriction", 85 | "NumericUnderscores", 86 | "OverloadedStrings", 87 | "PartialTypeSignatures", 88 | "PatternGuards", 89 | "PatternSynonyms", 90 | "PolyKinds", 91 | "PostfixOperators", 92 | "RankNTypes", 93 | "RecordWildCards", 94 | "RecursiveDo", 95 | "RelaxedPolyRec", 96 | "ScopedTypeVariables", 97 | "StandaloneDeriving", 98 | "StandaloneKindSignatures", 99 | "StarIsType", 100 | "TraditionalRecordSyntax", 101 | "TupleSections", 102 | "TypeApplications", 103 | "TypeOperators", 104 | "TypeFamilies", 105 | "TypeSynonymInstances", 106 | "UnicodeSyntax", 107 | "ViewPatterns" 108 | ] 109 | ]''; 110 | }; 111 | script = '' 112 | irc-persist-connect ulminfo localhost ${toString tunnelPort} lambdabot lambdabot 113 | irc-persist-connect libera irc.eu.libera.chat 6667 haskell lambdabot 114 | rc ${config.secrets.lambdabot-ulminfo.path} 115 | admin + ulminfo:ncf 116 | admin + libera:ncf 117 | url-off 118 | join ulminfo:#haskell 119 | join libera:##nf 120 | ''; 121 | }; 122 | 123 | systemd.services.lambdabot = rec { 124 | wants = [ "nss-lookup.target" "stunnel.service" ]; 125 | after = wants; 126 | serviceConfig = { 127 | MemoryMax = "10%"; 128 | Restart = "on-failure"; 129 | }; 130 | }; 131 | 132 | services.stunnel = { 133 | enable = config.services.lambdabot.enable; 134 | clients.ulminfo = { 135 | accept = "localhost:${toString tunnelPort}"; 136 | connect = "ens.wtf:6697"; 137 | }; 138 | }; 139 | 140 | my.extraGroups = [ "lambdabot" ]; 141 | } 142 | -------------------------------------------------------------------------------- /modules/server/mailserver.nix: -------------------------------------------------------------------------------- 1 | { inputs, lib, config, ... }: with lib; mkEnableModule [ "my-services" "mailserver" ] { 2 | imports = [ inputs.simple-nixos-mailserver.nixosModules.default ]; 3 | 4 | system.extraDependencies = collectFlakeInputs inputs.simple-nixos-mailserver; 5 | 6 | secrets.dkim = { 7 | path = "/etc/dkim/${my.domain}.${config.mailserver.dkimSelector}.key"; 8 | owner = config.services.opendkim.user; 9 | group = config.services.opendkim.group; 10 | }; 11 | 12 | lib.dkim.pk = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC/MUKMp4lOoDhaeyIh5hzVNkr5eJ7GMekGRCvVMpSx2DWgUPg8UR68VT1ObmEAQZVDd696XdRNFgFJZuaGSTqcjPfGVq7e+DFVZcRZbISat8mlvOyuDe7J2EwZQxn3gup9hwbesfFPCY6V+ZMwLylT0j974xqJPxEvkebZ+DylUwIDAQAB"; 13 | 14 | mailserver = { 15 | enable = true; 16 | enableImap = false; 17 | enableSubmission = true; 18 | enableImapSsl = true; 19 | enableSubmissionSsl = true; 20 | localDnsResolver = false; 21 | fqdn = my.domain; 22 | domains = [ my.domain ]; 23 | certificateScheme = "acme"; 24 | dkimKeyDirectory = "/etc/dkim"; 25 | loginAccounts.${my.email} = { 26 | hashedPassword = "$2b$05$1YO805N1yn2vVM/c4K8nRuNix1ruHc4SJDDbWREgrcAaamxiqCYKS"; 27 | aliases = [ "@${my.domain}" ]; 28 | }; 29 | lmtpSaveToDetailMailbox = "no"; 30 | messageSizeLimit = 25 * 1024 * 1024; 31 | rejectRecipients = map (x: "${x}@${my.domain}") [ "naim" "znc" ]; 32 | rejectSender = [ "sales@headquarters-biz.cloud" ]; 33 | }; 34 | 35 | my.extraGroups = [ config.mailserver.vmailGroupName ]; 36 | 37 | system.activationScripts.vmailPermissions = { 38 | deps = [ "users" ]; 39 | text = '' 40 | chmod -R g=u ${config.mailserver.mailDirectory} 41 | ''; 42 | }; 43 | 44 | services.rspamd = { 45 | overrides."whitelist.conf".text = '' 46 | whitelist_from { 47 | zulip.com = true; 48 | zulipchat.com = true; 49 | } 50 | ''; 51 | 52 | locals."groups.conf".text = '' 53 | symbols { 54 | "FORGED_RECIPIENTS" { 55 | weight = 10; 56 | } 57 | } 58 | ''; 59 | }; 60 | } 61 | -------------------------------------------------------------------------------- /modules/server/mastodon.nix: -------------------------------------------------------------------------------- 1 | { lib, config, ... }: with lib; let 2 | cfg = config.services.mastodon; 3 | webDomain = "fedi.${my.domain}"; 4 | in mkEnableModule [ "my-services" "mastodon" ] { 5 | services.mastodon = { 6 | enable = true; 7 | localDomain = my.domain; 8 | extraConfig = { 9 | WEB_DOMAIN = webDomain; 10 | SINGLE_USER_MODE = "true"; 11 | SMTP_OPENSSL_VERIFY_MODE = "none"; 12 | }; 13 | smtp.fromAddress = "mastodon@${my.domain}"; 14 | 15 | configureNginx = false; 16 | smtp.createLocally = false; 17 | }; 18 | 19 | services.nginx.virtualHosts = { 20 | ${webDomain} = { 21 | root = "${cfg.package}/public/"; 22 | 23 | locations."/system/".alias = "/var/lib/mastodon/public-system/"; 24 | 25 | locations."/" = { 26 | tryFiles = "$uri @proxy"; 27 | }; 28 | 29 | locations."@proxy" = { 30 | proxyPass = (if cfg.enableUnixSocket then "http://unix:/run/mastodon-web/web.socket" else "http://127.0.0.1:${toString(cfg.webPort)}"); 31 | proxyWebsockets = true; 32 | }; 33 | 34 | locations."/api/v1/streaming/" = { 35 | proxyPass = (if cfg.enableUnixSocket then "http://unix:/run/mastodon-streaming/streaming.socket" else "http://127.0.0.1:${toString(cfg.streamingPort)}/"); 36 | proxyWebsockets = true; 37 | }; 38 | }; 39 | ${my.domain}.locations."/.well-known/webfinger".return = "301 https://${webDomain}$request_uri"; 40 | }; 41 | 42 | users.groups.${cfg.group}.members = [config.services.nginx.user]; 43 | } 44 | -------------------------------------------------------------------------------- /modules/server/miniflux.nix: -------------------------------------------------------------------------------- 1 | { lib, this, config, ... }: with lib; { 2 | secrets.miniflux = {}; 3 | 4 | services.miniflux = { 5 | enable = true; 6 | adminCredentialsFile = config.secrets.miniflux.path; 7 | config = { 8 | LISTEN_ADDR = "${this.wireguard.ipv4}:8072"; 9 | BASE_URL = "https://flux.${my.domain}"; 10 | }; 11 | }; 12 | 13 | services.nginx.virtualHosts."flux.${my.domain}".locations."/".proxyPass = "http://${config.services.miniflux.config.LISTEN_ADDR}"; 14 | } 15 | -------------------------------------------------------------------------------- /modules/server/nix.nix: -------------------------------------------------------------------------------- 1 | { inputs, lib, config, ... }: with lib; let 2 | signature = "nix.monade.li:2Zgy59ai/edDBizXByHMqiGgaHlE04G6Nzuhx1RPFgo="; 3 | in { 4 | secrets.nix-binary-cache = {}; 5 | 6 | services.nix-serve = { 7 | enable = true; 8 | bindAddress = "127.0.0.1"; 9 | openFirewall = false; 10 | secretKeyFile = config.secrets.nix-binary-cache.path; 11 | extraParams = "--priority 41"; 12 | }; 13 | 14 | services.nginx.virtualHosts."nix.${my.domain}" = { 15 | locations."= /" = { 16 | return = ''200 "${signature}\n"''; 17 | extraConfig = '' 18 | add_header Content-Type text/plain; 19 | ''; 20 | }; 21 | locations."/".proxyPass = "http://${config.services.nix-serve.bindAddress}:${toString config.services.nix-serve.port}"; 22 | }; 23 | } 24 | -------------------------------------------------------------------------------- /modules/server/ulmaoc-topic.nix: -------------------------------------------------------------------------------- 1 | { lib, config, pkgs, ... }: with lib; mkEnableModule [ "my-services" "ulmaoc-topic" ] { 2 | secrets.ulmaoc-topic = { 3 | owner = my.username; 4 | inherit (config.my) group; 5 | }; 6 | 7 | systemd.services.ulmaoc-topic = { 8 | serviceConfig.User = my.username; 9 | path = [ pkgs.config-cli ]; 10 | script = '' 11 | . config env 12 | (( day = $(TZ=EST date +%j) - $(date -d "december 1" +%j) + 1 )) || true 13 | read -r board < ${config.secrets.ulmaoc-topic.path} 14 | 15 | topic="Advent of Code $(date +%Y) https://adventofcode.com | Jour $day | Leaderboard $board | Spoilers -> #adventofcode-spoilers" 16 | 17 | echo "irc.server.ulminfo */msg ChanServ TOPIC #adventofcode $topic" > "$weechat_fifo" 18 | ''; 19 | startAt = "12-1..25 00:00:00 EST"; 20 | }; 21 | 22 | systemd.timers.ulmaoc-topic.timerConfig.AccuracySec = "1s"; 23 | } 24 | -------------------------------------------------------------------------------- /modules/server/weechat/alias.conf: -------------------------------------------------------------------------------- 1 | # 2 | # weechat -- alias.conf 3 | # 4 | # WARNING: It is NOT recommended to edit this file by hand, 5 | # especially if WeeChat is running. 6 | # 7 | # Use commands like /set or /fset to change settings in WeeChat. 8 | # 9 | # For more info, see: https://weechat.org/doc/weechat/quickstart/ 10 | # 11 | 12 | config_version = 2 13 | 14 | [cmd] 15 | ? = "eval /ping ${date:%T}" 16 | aaway = "allserv /away" 17 | acl = "buffer clear -all" 18 | akick = "cs akick $channel add" 19 | alis = "msg alis" 20 | anick = "allserv /nick" 21 | b = "buffer" 22 | beep = "print -beep" 23 | black = "say 1,1$*" 24 | bye = "quit" 25 | c = "query candide" 26 | chat = "dcc chat" 27 | cl = "buffer clear" 28 | clear = "buffer clear" 29 | close = "buffer close" 30 | exit = "quit" 31 | filternick = "filter add filter_$1 * nick_$1 *" 32 | g = "go" 33 | gb = "query greybot" 34 | glare = "me glares" 35 | hi = "say hi" 36 | hide = "buffer hide" 37 | highall = "buffer set highlight_regex .*" 38 | ig = "ignore" 39 | j = "join" 40 | k = "query k" 41 | kb = "kickban" 42 | kiss = "me kisses" 43 | lb = "query lambdabot" 44 | leave = "part" 45 | look = "me looks" 46 | m = "msg" 47 | mc = "msg candide" 48 | mock = "exec -o -name mock -stdin -sh while IFS= read -rn 1 c\;do c=${c,,}\;((RANDOM%4&&(u=1-u)))\;((u))&&c=${c^^}\;printf %s "$c"\;done;exec -inclose mock" 49 | ms = "msg MemoServ" 50 | msgbuf = "command -buffer $1 * /input send $2-" 51 | mub = "unban *" 52 | my = "me 's" 53 | no = "say no" 54 | nod = "me nods" 55 | nohighall = "buffer set highlight_regex" 56 | not = "notice $channel" 57 | np = "exec -oc -sh . <(tmux show-environment -s SSH_CONNECTION)\; ssh -q -o BatchMode=yes "${SSH_CONNECTION%% *}" "mpc current -f '/me is now playing %title% by %artist%'"" 58 | np= = "me is now playing" 59 | opme = "cs op $channel" 60 | pat = "me pats" 61 | pet = "me pets" 62 | q = "query" 63 | rc = "reconnect" 64 | rca = "rc -all" 65 | redraw = "window refresh" 66 | say = "msg *" 67 | shb = "query shbot" 68 | shrug = "me shrugs" 69 | signoff = "quit" 70 | slap = "me slaps $* around a bit with a large trout" 71 | squint = "me squints" 72 | stare = "me stares" 73 | t = "topic" 74 | think = "me . o O ( $* )" 75 | ub = "unban" 76 | umode = "mode $nick" 77 | unakick = "cs akick $channel del" 78 | unfilternick = "filter del filter_$1" 79 | unhide = "buffer unhide" 80 | v = "command core version" 81 | w = "who" 82 | wc = "window merge" 83 | wi = "whois" 84 | wide = "exec -o -sh -name wide -stdin sed -r 's/(\x03[0-9]{,2}|\x02)*./& /g';exec -inclose wide" 85 | widerainbow = "exec -o -sh -name widerainbow -stdin sed -r 's/(\x03[0-9]{,2}|\x02)*./& /g' | lolcat -fF 1.5;exec -inclose widerainbow" 86 | wii = "whois $1 $1" 87 | ww = "whowas" 88 | yes = "say yes" 89 | 90 | [completion] 91 | msgbuf = "%(buffers_plugins_names)" 92 | -------------------------------------------------------------------------------- /modules/server/weechat/autosort.conf: -------------------------------------------------------------------------------- 1 | # 2 | # weechat -- autosort.conf 3 | # 4 | # WARNING: It is NOT recommended to edit this file by hand, 5 | # especially if WeeChat is running. 6 | # 7 | # Use commands like /set or /fset to change settings in WeeChat. 8 | # 9 | # For more info, see: https://weechat.org/doc/weechat/quickstart/ 10 | # 11 | 12 | [sorting] 13 | case_sensitive = off 14 | debug_log = off 15 | replacements = "" 16 | rules = "" 17 | signal_delay = 5 18 | signals = "buffer_opened buffer_merged buffer_unmerged buffer_renamed" 19 | sort_limit = 100 20 | sort_on_config_change = on 21 | 22 | [v3] 23 | helpers = "{"core_first": "${if:${buffer.full_name}!=core.weechat}", "irc_raw_last": "${if:${buffer.full_name}==irc.irc_raw}", "irc_last": "${if:${buffer.plugin.name}==irc}", "hashless_name": "${info:autosort_replace,#,,${buffer.name}}", "irc_first": "${if:${buffer.plugin.name}!=irc}", "irc_raw_first": "${if:${buffer.full_name}!=irc.irc_raw}"}" 24 | rules = "["${core_first}", "${irc_last}", "${buffer.plugin.name}", "${irc_raw_first}", "${if:${plugin}==irc?${server}}", "${if:${plugin}==irc?${info:autosort_order,${type},server,*,channel,private}}", "${if:${plugin}==irc?${hashless_name}}", "${buffer.full_name}"]" 25 | -------------------------------------------------------------------------------- /modules/server/weechat/buflist.conf: -------------------------------------------------------------------------------- 1 | # 2 | # weechat -- buflist.conf 3 | # 4 | # WARNING: It is NOT recommended to edit this file by hand, 5 | # especially if WeeChat is running. 6 | # 7 | # Use commands like /set or /fset to change settings in WeeChat. 8 | # 9 | # For more info, see: https://weechat.org/doc/weechat/quickstart/ 10 | # 11 | 12 | [look] 13 | add_newline = on 14 | auto_scroll = 50 15 | display_conditions = "${buffer.hidden} == 0 && ${buffer.active}" 16 | enabled = on 17 | mouse_jump_visited_buffer = off 18 | mouse_move_buffer = off 19 | mouse_wheel = on 20 | nick_prefix = on 21 | nick_prefix_empty = on 22 | signals_refresh = "" 23 | sort = "number,-active" 24 | use_items = 1 25 | 26 | [format] 27 | buffer = "${color_hotlist}${format_number}${indent}${color_hotlist}${format_nick_prefix}${format_name}" 28 | buffer_current = "${color:black,white}${format_number}${indent}${format_nick_prefix}${format_name}" 29 | hotlist = " ${color:green}(${hotlist}${color:green})" 30 | hotlist_highlight = "${color:magenta}" 31 | hotlist_low = "${color:darkgray}" 32 | hotlist_message = "${color:default}" 33 | hotlist_none = "${color:darkgray}" 34 | hotlist_private = "${color:green}" 35 | hotlist_separator = "${color:default}," 36 | indent = " " 37 | lag = " ${color:green}[${color:brown}${lag}${color:green}]" 38 | name = "${name}" 39 | nick_prefix = "${nick_prefix}" 40 | number = "" 41 | tls_version = " ${color:default}(${if:${tls_version}==TLS1.3?${color:green}:${if:${tls_version}==TLS1.2?${color:yellow}:${color:red}}}${translate:${tls_version}}${color:default})" 42 | -------------------------------------------------------------------------------- /modules/server/weechat/charset.conf: -------------------------------------------------------------------------------- 1 | # 2 | # weechat -- charset.conf 3 | # 4 | # WARNING: It is NOT recommended to edit this file by hand, 5 | # especially if WeeChat is running. 6 | # 7 | # Use /set or similar command to change settings in WeeChat. 8 | # 9 | # For more info, see: https://weechat.org/doc/quickstart 10 | # 11 | 12 | [default] 13 | decode = "iso-8859-1" 14 | encode = "" 15 | 16 | [decode] 17 | 18 | [encode] 19 | -------------------------------------------------------------------------------- /modules/server/weechat/colorize_nicks.conf: -------------------------------------------------------------------------------- 1 | # 2 | # weechat -- colorize_nicks.conf 3 | # 4 | # WARNING: It is NOT recommended to edit this file by hand, 5 | # especially if WeeChat is running. 6 | # 7 | # Use commands like /set or /fset to change settings in WeeChat. 8 | # 9 | # For more info, see: https://weechat.org/doc/weechat/quickstart/ 10 | # 11 | 12 | [look] 13 | blacklist_channels = "" 14 | blacklist_nicks = "so,root,zero" 15 | colorize_input = on 16 | greedy_matching = off 17 | ignore_nicks_in_urls = on 18 | ignore_tags = "" 19 | match_limit = 20 20 | min_nick_length = 1 21 | -------------------------------------------------------------------------------- /modules/server/weechat/default.nix: -------------------------------------------------------------------------------- 1 | { lib, this, config, pkgs, ... }: with lib; let 2 | relayPort = 6642; 3 | scripts = [ 4 | "autosort.py" 5 | "color_popup.pl" 6 | "colorize_nicks.py" 7 | "go.py" 8 | "highmon.pl" 9 | "screen_away.py" 10 | ]; 11 | weechat = pkgs.weechat.override { 12 | configure = { availablePlugins, ... }: { 13 | plugins = with availablePlugins; [ python perl ]; 14 | init = "/exec -oc cat ${builtins.toFile "weechat-init" '' 15 | /script update 16 | /script install ${concatStringsSep " " scripts} 17 | /mute set sec.crypt.passphrase_command "cat ${config.secrets.weechat.path}" 18 | /mute set relay.network.bind_address ${this.wireguard.ipv4} 19 | /mute set relay.port.weechat ${toString relayPort} 20 | /mute set logger.file.path ${config.synced.irc-logs.path} 21 | ''}"; 22 | }; 23 | }; 24 | in mkEnableModule [ "my-services" "weechat" ] { 25 | secrets.weechat = { 26 | owner = my.username; 27 | inherit (config.my) group; 28 | }; 29 | 30 | systemd.services."weechat-${my.username}" = rec { 31 | description = "WeeChat in a tmux session"; 32 | # create a user manager so that /run/user/$uid exists and hence SSH_AUTH_SOCK gets set correctly 33 | wants = [ "user@${toString config.my.uid}.service" "network-online.target" ]; 34 | after = wants ++ [ "nss-lookup.target" ]; 35 | wantedBy = [ "multi-user.target" ]; 36 | serviceConfig = { 37 | User = my.username; 38 | Type = "forking"; 39 | ExecStart = "${pkgs.tmux}/bin/tmux -L weechat new-session -s weechat -d ${config.my.shellPath} -lc 'exec ${weechat}/bin/weechat'"; 40 | ExecStartPost = "${pkgs.tmux}/bin/tmux -L weechat set-option status off \\; set-option mouse off"; 41 | }; 42 | restartIfChanged = false; 43 | }; 44 | 45 | hm.xdg.configFile = mapAttrs' (name: _: { 46 | name = "weechat/${name}"; 47 | value.source = config.lib.meta.mkMutableSymlink ./${name}; 48 | }) (filterAttrs (name: _: hasSuffix ".conf" name) (builtins.readDir ./.)); 49 | 50 | networking.firewall.allowedTCPPorts = [ relayPort ]; 51 | 52 | environment.systemPackages = with pkgs; [ lolcat ]; 53 | 54 | lib.shellEnv.weechat_fifo = "${config.hm.xdg.cacheHome}/weechat/weechat_fifo"; 55 | 56 | my-services.ulmaoc-topic.enable = true; 57 | } 58 | -------------------------------------------------------------------------------- /modules/server/weechat/exec.conf: -------------------------------------------------------------------------------- 1 | # 2 | # weechat -- exec.conf 3 | # 4 | # WARNING: It is NOT recommended to edit this file by hand, 5 | # especially if WeeChat is running. 6 | # 7 | # Use commands like /set or /fset to change settings in WeeChat. 8 | # 9 | # For more info, see: https://weechat.org/doc/weechat/quickstart/ 10 | # 11 | 12 | [command] 13 | default_options = "" 14 | purge_delay = 0 15 | shell = "${env:SHELL}" 16 | 17 | [color] 18 | flag_finished = lightred 19 | flag_running = lightgreen 20 | -------------------------------------------------------------------------------- /modules/server/weechat/fifo.conf: -------------------------------------------------------------------------------- 1 | # 2 | # weechat -- fifo.conf 3 | # 4 | # WARNING: It is NOT recommended to edit this file by hand, 5 | # especially if WeeChat is running. 6 | # 7 | # Use commands like /set or /fset to change settings in WeeChat. 8 | # 9 | # For more info, see: https://weechat.org/doc/weechat/quickstart/ 10 | # 11 | 12 | [file] 13 | enabled = on 14 | path = "${weechat_runtime_dir}/weechat_fifo" 15 | -------------------------------------------------------------------------------- /modules/server/weechat/fset.conf: -------------------------------------------------------------------------------- 1 | # 2 | # weechat -- fset.conf 3 | # 4 | # WARNING: It is NOT recommended to edit this file by hand, 5 | # especially if WeeChat is running. 6 | # 7 | # Use commands like /set or /fset to change settings in WeeChat. 8 | # 9 | # For more info, see: https://weechat.org/doc/weechat/quickstart/ 10 | # 11 | 12 | [look] 13 | auto_refresh = "*" 14 | auto_unmark = off 15 | condition_catch_set = "${count} >= 1" 16 | export_help_default = on 17 | format_number = 1 18 | marked_string = "*" 19 | scroll_horizontal = 10 20 | show_plugins_desc = off 21 | sort = "~name" 22 | unmarked_string = " " 23 | use_color_value = off 24 | use_keys = on 25 | use_mute = off 26 | 27 | [format] 28 | export_help = "# ${description2}" 29 | export_option = "/set ${name} ${quoted_value}" 30 | export_option_null = "/unset ${name}" 31 | option1 = "" 32 | option2 = "${marked} ${name} ${type} ${value2}${newline} ${empty_name} ${_default_value}${color:darkgray} -- ${min}..${max}${newline} ${empty_name} ${description}" 33 | 34 | [color] 35 | allowed_values = default 36 | allowed_values_selected = white 37 | color_name = 246 38 | color_name_selected = default 39 | default_value = default 40 | default_value_selected = default 41 | description = default 42 | description_selected = default 43 | file = default 44 | file_changed = brown 45 | file_changed_selected = yellow 46 | file_selected = default 47 | help_default_value = default 48 | help_description = default 49 | help_name = default 50 | help_quotes = darkgray 51 | help_values = default 52 | index = cyan 53 | index_selected = lightcyan 54 | line_marked_bg1 = default 55 | line_marked_bg2 = default 56 | line_selected_bg1 = darkgray 57 | line_selected_bg2 = red 58 | marked = brown 59 | marked_selected = yellow 60 | max = default 61 | max_selected = default 62 | min = default 63 | min_selected = default 64 | name = default 65 | name_changed = brown 66 | name_changed_selected = yellow 67 | name_selected = default 68 | option = default 69 | option_changed = brown 70 | option_changed_selected = yellow 71 | option_selected = default 72 | parent_name = default 73 | parent_name_selected = default 74 | parent_value = cyan 75 | parent_value_selected = lightcyan 76 | quotes = darkgray 77 | quotes_changed = default 78 | quotes_changed_selected = default 79 | quotes_selected = default 80 | section = default 81 | section_changed = brown 82 | section_changed_selected = yellow 83 | section_selected = default 84 | string_values = default 85 | string_values_selected = default 86 | title_count_options = cyan 87 | title_current_option = lightcyan 88 | title_filter = yellow 89 | title_marked_options = lightgreen 90 | title_sort = default 91 | type = green 92 | type_selected = lightgreen 93 | unmarked = default 94 | unmarked_selected = default 95 | value = cyan 96 | value_changed = brown 97 | value_changed_selected = yellow 98 | value_selected = lightcyan 99 | value_undef = magenta 100 | value_undef_selected = lightmagenta 101 | -------------------------------------------------------------------------------- /modules/server/weechat/logger.conf: -------------------------------------------------------------------------------- 1 | # 2 | # weechat -- logger.conf 3 | # 4 | # WARNING: It is NOT recommended to edit this file by hand, 5 | # especially if WeeChat is running. 6 | # 7 | # Use commands like /set or /fset to change settings in WeeChat. 8 | # 9 | # For more info, see: https://weechat.org/doc/weechat/quickstart/ 10 | # 11 | 12 | [look] 13 | backlog = 300 14 | backlog_conditions = "" 15 | 16 | [color] 17 | backlog_end = default 18 | backlog_line = default 19 | 20 | [file] 21 | auto_log = on 22 | color_lines = on 23 | flush_delay = 60 24 | fsync = off 25 | info_lines = off 26 | log_conditions = "" 27 | mask = "$plugin/$name" 28 | name_lower_case = on 29 | nick_prefix = "" 30 | nick_suffix = "" 31 | path = "/home/n/sync/irc-logs" 32 | replacement_char = "_" 33 | rotation_compression_level = 20 34 | rotation_compression_type = none 35 | rotation_size_max = "0" 36 | time_format = "%Y-%m-%d %H:%M:%S" 37 | 38 | [level] 39 | core = 0 40 | irc = 9 41 | 42 | [mask] 43 | irc = "$server/$channel" 44 | -------------------------------------------------------------------------------- /modules/server/weechat/matrix.conf: -------------------------------------------------------------------------------- 1 | # 2 | # weechat -- matrix.conf 3 | # 4 | # WARNING: It is NOT recommended to edit this file by hand, 5 | # especially if WeeChat is running. 6 | # 7 | # Use commands like /set or /fset to change settings in WeeChat. 8 | # 9 | # For more info, see: https://weechat.org/doc/quickstart 10 | # 11 | 12 | [network] 13 | autoreconnect_delay_growing = 2 14 | autoreconnect_delay_max = 600 15 | debug_buffer = off 16 | debug_category = all 17 | debug_level = error 18 | fetch_backlog_on_pgup = on 19 | lag_min_show = 500 20 | lag_reconnect = 90 21 | lazy_load_room_users = off 22 | max_backlog_sync_events = 10 23 | max_initial_sync_events = 30 24 | max_nicklist_users = 5000 25 | print_unconfirmed_messages = on 26 | read_markers_conditions = "${markers_enabled}" 27 | resending_ignores_devices = on 28 | typing_notice_conditions = "${typing_enabled}" 29 | 30 | [look] 31 | bar_item_typing_notice_prefix = "Typing: " 32 | busy_sign = "⏳" 33 | code_block_margin = 2 34 | code_blocks = on 35 | disconnect_sign = "❌" 36 | encrypted_room_sign = "🔐" 37 | encryption_warning_sign = "⚠️ " 38 | human_buffer_names = off 39 | markdown_input = on 40 | max_typing_notice_item_length = 50 41 | new_channel_position = none 42 | pygments_style = "native" 43 | quote_wrap = 67 44 | redactions = strikethrough 45 | server_buffer = independent 46 | 47 | [color] 48 | error_message_bg = default 49 | error_message_fg = darkgray 50 | nick_prefixes = "admin=lightgreen;mod=lightgreen;power=yellow" 51 | quote_bg = default 52 | quote_fg = lightgreen 53 | unconfirmed_message_bg = default 54 | unconfirmed_message_fg = darkgray 55 | untagged_code_bg = default 56 | untagged_code_fg = blue 57 | 58 | [server] 59 | matrix_org.autoconnect = off 60 | matrix_org.address = "matrix.org" 61 | matrix_org.port = 443 62 | matrix_org.proxy = "" 63 | matrix_org.ssl_verify = on 64 | matrix_org.username = "ncfavier" 65 | matrix_org.password = "${sec.data.matrix}" 66 | matrix_org.device_name = "Weechat Matrix" 67 | matrix_org.autoreconnect_delay = 10 68 | matrix_org.sso_helper_listening_port = 0 69 | -------------------------------------------------------------------------------- /modules/server/weechat/perl.conf: -------------------------------------------------------------------------------- 1 | # 2 | # weechat -- perl.conf 3 | # 4 | # WARNING: It is NOT recommended to edit this file by hand, 5 | # especially if WeeChat is running. 6 | # 7 | # Use commands like /set or /fset to change settings in WeeChat. 8 | # 9 | # For more info, see: https://weechat.org/doc/weechat/quickstart/ 10 | # 11 | 12 | [look] 13 | check_license = off 14 | eval_keep_context = on 15 | -------------------------------------------------------------------------------- /modules/server/weechat/python.conf: -------------------------------------------------------------------------------- 1 | # 2 | # weechat -- python.conf 3 | # 4 | # WARNING: It is NOT recommended to edit this file by hand, 5 | # especially if WeeChat is running. 6 | # 7 | # Use commands like /set or /fset to change settings in WeeChat. 8 | # 9 | # For more info, see: https://weechat.org/doc/weechat/quickstart/ 10 | # 11 | 12 | [look] 13 | check_license = off 14 | eval_keep_context = on 15 | -------------------------------------------------------------------------------- /modules/server/weechat/relay.conf: -------------------------------------------------------------------------------- 1 | # 2 | # weechat -- relay.conf 3 | # 4 | # WARNING: It is NOT recommended to edit this file by hand, 5 | # especially if WeeChat is running. 6 | # 7 | # Use commands like /set or /fset to change settings in WeeChat. 8 | # 9 | # For more info, see: https://weechat.org/doc/weechat/quickstart/ 10 | # 11 | 12 | config_version = 2 13 | 14 | [look] 15 | auto_open_buffer = "off" 16 | display_clients = "irc,weechat" 17 | raw_messages = 256 18 | raw_messages_max_length = 4096 19 | 20 | [color] 21 | client = cyan 22 | status_active = lightblue 23 | status_auth_failed = lightred 24 | status_authenticating = yellow 25 | status_connecting = yellow 26 | status_disconnected = lightred 27 | text = default 28 | text_bg = default 29 | text_selected = default 30 | 31 | [network] 32 | allow_empty_password = on 33 | allowed_ips = "^10.42." 34 | auth_timeout = 60 35 | bind_address = "10.42.0.2" 36 | clients_purge_delay = 0 37 | commands = "*,!quit" 38 | compression = 20 39 | ipv6 = off 40 | max_clients = 5 41 | nonce_size = 16 42 | password = "" 43 | password_hash_algo = "*" 44 | password_hash_iterations = 100000 45 | time_window = 5 46 | tls_cert_key = "%h/ssl/relay.pem" 47 | tls_priorities = "NORMAL:-VERS-SSL3.0" 48 | totp_secret = "" 49 | totp_window = 0 50 | websocket_allowed_origins = "" 51 | websocket_permessage_deflate = on 52 | 53 | [irc] 54 | backlog_max_minutes = 1440 55 | backlog_max_number = 256 56 | backlog_since_last_disconnect = on 57 | backlog_since_last_message = off 58 | backlog_tags = "irc_privmsg" 59 | backlog_time_format = "[%H:%M] " 60 | 61 | [api] 62 | remote_autoreconnect_delay_growing = 2 63 | remote_autoreconnect_delay_max = 600 64 | remote_get_lines = 1000 65 | 66 | [port] 67 | weechat = 6642 68 | 69 | [path] 70 | 71 | [remote] 72 | -------------------------------------------------------------------------------- /modules/server/weechat/script.conf: -------------------------------------------------------------------------------- 1 | # 2 | # weechat -- script.conf 3 | # 4 | # WARNING: It is NOT recommended to edit this file by hand, 5 | # especially if WeeChat is running. 6 | # 7 | # Use commands like /set or /fset to change settings in WeeChat. 8 | # 9 | # For more info, see: https://weechat.org/doc/weechat/quickstart/ 10 | # 11 | 12 | [look] 13 | columns = "%s %n %V %v %u | %d | %t" 14 | diff_color = on 15 | diff_command = "auto" 16 | display_source = on 17 | quiet_actions = on 18 | sort = "p,n" 19 | translate_description = on 20 | use_keys = on 21 | 22 | [color] 23 | status_autoloaded = cyan 24 | status_held = default 25 | status_installed = lightcyan 26 | status_obsolete = lightmagenta 27 | status_popular = yellow 28 | status_running = lightgreen 29 | status_unknown = lightred 30 | text = default 31 | text_bg = default 32 | text_bg_selected = darkgray 33 | text_date = default 34 | text_date_selected = default 35 | text_delimiters = default 36 | text_description = default 37 | text_description_selected = default 38 | text_extension = default 39 | text_extension_selected = default 40 | text_name = cyan 41 | text_name_selected = lightcyan 42 | text_selected = default 43 | text_tags = brown 44 | text_tags_selected = yellow 45 | text_version = magenta 46 | text_version_loaded = default 47 | text_version_loaded_selected = default 48 | text_version_selected = lightmagenta 49 | 50 | [scripts] 51 | autoload = on 52 | cache_expire = 1440 53 | download_enabled = on 54 | download_timeout = 30 55 | hold = "" 56 | path = "%h/script" 57 | url = "https://weechat.org/files/plugins.xml.gz" 58 | -------------------------------------------------------------------------------- /modules/server/weechat/sec.conf: -------------------------------------------------------------------------------- 1 | # 2 | # weechat -- sec.conf 3 | # 4 | # WARNING: It is NOT recommended to edit this file by hand, 5 | # especially if WeeChat is running. 6 | # 7 | # Use commands like /set or /fset to change settings in WeeChat. 8 | # 9 | # For more info, see: https://weechat.org/doc/weechat/quickstart/ 10 | # 11 | 12 | [crypt] 13 | cipher = aes256 14 | hash_algo = sha256 15 | passphrase_command = "cat /run/secrets/weechat" 16 | salt = off 17 | 18 | [data] 19 | __passphrase__ = on 20 | ulminfo-oper = "5765654368617421220A5D4DAD53DFDBD4432FBBEDB66D2BC5A9211D4ADBE561E6D3EAA2731777227B01E2FFDCC9907BD49EEA7F03119E876F41D30751A2A6FFAF84" 21 | matrix = "576565436861742176432CA33B99C948C1285940CD5589242D1F5B34597DF164EB6B9F304DA76ACE0764FF746A2E6B04B600600BA5D8B760F9C0B356ED2B57331DB6" 22 | rizon = "57656543686174210400898B35776B673A4CDEB73C8E8653E398EE08C708ED2E50B99B63F52187BE8621B07A792178F4E1C89FBAF42C2B040AABEFE38DA019D95697" 23 | libera = "5765654368617421F2C6118B386071C53DFBD9B592B05D4B09813694FC68F4C30EF266D32E9C9C1461EAB8F162516AF11A257963829413CA65B534CD8DDDEB1C3934" 24 | ulminfo = "5765654368617421204415856FDA649B82949AB11EA64FFDE8EA45EC89A09694BBC54626F807E11D78A814B79164FC47C1C8221F4FFF010CBEC88D5902C683742E5E" 25 | oftc = "5765654368617421042DCE92458ACAAED14AC53B3B5F2BB330DC93E4B4FE8C4D047D86CE73E714AEDC72F197E8D5F2B4B0E98E5A0E9F96A3A342916C19F2970C224F" 26 | -------------------------------------------------------------------------------- /modules/server/weechat/spell.conf: -------------------------------------------------------------------------------- 1 | # 2 | # weechat -- spell.conf 3 | # 4 | # WARNING: It is NOT recommended to edit this file by hand, 5 | # especially if WeeChat is running. 6 | # 7 | # Use /set or similar command to change settings in WeeChat. 8 | # 9 | # For more info, see: https://weechat.org/doc/quickstart 10 | # 11 | 12 | [color] 13 | misspelled = lightred 14 | suggestion = default 15 | suggestion_delimiter_dict = cyan 16 | suggestion_delimiter_word = cyan 17 | 18 | [check] 19 | commands = "ame,amsg,away,command,cycle,kick,kickban,me,msg,notice,part,query,quit,topic" 20 | default_dict = "" 21 | during_search = off 22 | enabled = off 23 | real_time = off 24 | suggestions = -1 25 | word_min_length = 2 26 | 27 | [dict] 28 | 29 | [look] 30 | suggestion_delimiter_dict = " / " 31 | suggestion_delimiter_word = "," 32 | 33 | [option] 34 | -------------------------------------------------------------------------------- /modules/server/weechat/trigger.conf: -------------------------------------------------------------------------------- 1 | # 2 | # weechat -- trigger.conf 3 | # 4 | # WARNING: It is NOT recommended to edit this file by hand, 5 | # especially if WeeChat is running. 6 | # 7 | # Use commands like /set or /fset to change settings in WeeChat. 8 | # 9 | # For more info, see: https://weechat.org/doc/weechat/quickstart/ 10 | # 11 | 12 | [look] 13 | enabled = on 14 | monitor_strip_colors = off 15 | 16 | [color] 17 | flag_command = lightgreen 18 | flag_conditions = yellow 19 | flag_post_action = lightblue 20 | flag_regex = lightcyan 21 | flag_return_code = lightmagenta 22 | identifier = cyan 23 | regex = default 24 | replace = cyan 25 | 26 | [trigger] 27 | autojoin_sort.arguments = "irc.server.*.autojoin" 28 | autojoin_sort.command = "/mute allserv /autojoin sort" 29 | autojoin_sort.conditions = "" 30 | autojoin_sort.enabled = on 31 | autojoin_sort.hook = config 32 | autojoin_sort.post_action = none 33 | autojoin_sort.regex = "" 34 | autojoin_sort.return_code = ok 35 | beep.arguments = "" 36 | beep.command = "/print -beep" 37 | beep.conditions = "${tg_displayed} && (${tg_highlight} || ${tg_msg_pv})" 38 | beep.enabled = on 39 | beep.hook = print 40 | beep.post_action = none 41 | beep.regex = "" 42 | beep.return_code = ok 43 | cmd_pass.arguments = "5000|input_text_display;5000|history_add;5000|irc_command_auth" 44 | cmd_pass.command = "" 45 | cmd_pass.conditions = "" 46 | cmd_pass.enabled = on 47 | cmd_pass.hook = modifier 48 | cmd_pass.post_action = none 49 | cmd_pass.regex = "==^((/(msg|m|quote) +nickserv +(id|identify|ghost +[^ ]+|release +[^ ]+|regain +[^ ]+|recover +[^ ]+) +)|/oper +[^ ]+ +|/quote +pass +|/set +[^ ]*password[^ ]* +|/secure +(passphrase|decrypt|set +[^ ]+) +)(.*)==${re:1}${hide:*,${re:+}}" 50 | cmd_pass.return_code = ok 51 | cmd_pass_register.arguments = "5000|input_text_display;5000|history_add;5000|irc_command_auth" 52 | cmd_pass_register.command = "" 53 | cmd_pass_register.conditions = "" 54 | cmd_pass_register.enabled = on 55 | cmd_pass_register.hook = modifier 56 | cmd_pass_register.post_action = none 57 | cmd_pass_register.regex = "==^(/(msg|m|quote) +nickserv +register +)([^ ]+)(.*)==${re:1}${hide:*,${re:3}}${re:4}" 58 | cmd_pass_register.return_code = ok 59 | copy_message.arguments = "chat_copy_message" 60 | copy_message.command = "/print -stderr \e]52\;c\;${base_encode:64,${_chat_line_message}}\a" 61 | copy_message.conditions = "" 62 | copy_message.enabled = on 63 | copy_message.hook = hsignal 64 | copy_message.post_action = none 65 | copy_message.regex = "" 66 | copy_message.return_code = ok 67 | force_redraw.arguments = "window_switch;buffer_switch" 68 | force_redraw.command = "/wait 1ms /redraw" 69 | force_redraw.conditions = "" 70 | force_redraw.enabled = on 71 | force_redraw.hook = signal 72 | force_redraw.post_action = none 73 | force_redraw.regex = "" 74 | force_redraw.return_code = ok 75 | msg_auth.arguments = "5000|irc_message_auth" 76 | msg_auth.command = "" 77 | msg_auth.conditions = "" 78 | msg_auth.enabled = on 79 | msg_auth.hook = modifier 80 | msg_auth.post_action = none 81 | msg_auth.regex = "==^(.*(id|identify|register|ghost +[^ ]+|release +[^ ]+|regain +[^ ]+|recover +[^ ]+) +)(.*)==${re:1}${hide:*,${re:+}}" 82 | msg_auth.return_code = ok 83 | save_config.arguments = "3600000;0;0" 84 | save_config.command = "/mute save" 85 | save_config.conditions = "" 86 | save_config.enabled = on 87 | save_config.hook = timer 88 | save_config.post_action = none 89 | save_config.regex = "" 90 | save_config.return_code = ok 91 | server_pass.arguments = "5000|input_text_display;5000|history_add" 92 | server_pass.command = "" 93 | server_pass.conditions = "" 94 | server_pass.enabled = on 95 | server_pass.hook = modifier 96 | server_pass.post_action = none 97 | server_pass.regex = "==^(/(server|connect) .*-(sasl_)?password=)([^ ]+)(.*)==${re:1}${hide:*,${re:4}}${re:5}" 98 | server_pass.return_code = ok 99 | unhide_on_activity.arguments = "" 100 | unhide_on_activity.command = "/command -buffer irc.${server}.${channel} core /buffer unhide" 101 | unhide_on_activity.conditions = "${buffer.hidden} == 1 && ${tg_tag_notify} == message" 102 | unhide_on_activity.enabled = on 103 | unhide_on_activity.hook = print 104 | unhide_on_activity.post_action = none 105 | unhide_on_activity.regex = "" 106 | unhide_on_activity.return_code = ok 107 | unhide_on_switch.arguments = "buffer_switch" 108 | unhide_on_switch.command = "/command -buffer ${buffer[${tg_signal_data}].full_name} core /buffer unhide" 109 | unhide_on_switch.conditions = "${buffer[${tg_signal_data}].hidden} == 1" 110 | unhide_on_switch.enabled = on 111 | unhide_on_switch.hook = signal 112 | unhide_on_switch.post_action = none 113 | unhide_on_switch.regex = "" 114 | unhide_on_switch.return_code = ok 115 | -------------------------------------------------------------------------------- /modules/server/weechat/xfer.conf: -------------------------------------------------------------------------------- 1 | # 2 | # weechat -- xfer.conf 3 | # 4 | # WARNING: It is NOT recommended to edit this file by hand, 5 | # especially if WeeChat is running. 6 | # 7 | # Use /set or similar command to change settings in WeeChat. 8 | # 9 | # For more info, see: https://weechat.org/doc/quickstart 10 | # 11 | 12 | [look] 13 | auto_open_buffer = on 14 | progress_bar_size = 20 15 | pv_tags = "notify_private" 16 | 17 | [color] 18 | status_aborted = lightred 19 | status_active = lightblue 20 | status_connecting = yellow 21 | status_done = lightgreen 22 | status_failed = lightred 23 | status_waiting = lightcyan 24 | text = default 25 | text_bg = default 26 | text_selected = default 27 | 28 | [network] 29 | blocksize = 65536 30 | fast_send = on 31 | own_ip = "" 32 | port_range = "" 33 | send_ack = on 34 | speed_limit_recv = 0 35 | speed_limit_send = 0 36 | timeout = 300 37 | 38 | [file] 39 | auto_accept_chats = off 40 | auto_accept_files = off 41 | auto_accept_nicks = "" 42 | auto_check_crc32 = off 43 | auto_rename = on 44 | auto_resume = on 45 | convert_spaces = on 46 | download_path = "%h/xfer" 47 | upload_path = "~" 48 | use_nick_in_filename = on 49 | -------------------------------------------------------------------------------- /modules/shell/default.nix: -------------------------------------------------------------------------------- 1 | { lib, pkgs, ... }: with lib; { 2 | environment.systemPackages = with pkgs; [ 3 | (writeShellScriptBin "functions" (readFile ./functions.bash)) # meant to be sourced 4 | alacritty.terminfo 5 | ghostty.terminfo 6 | ]; 7 | 8 | environment.sessionVariables = rec { 9 | LESS = "ij3FRMK --mouse --wheel-lines=4"; 10 | SYSTEMD_LESS = LESS; 11 | LESSHISTFILE = "-"; 12 | MANOPT = "--no-hyphenation"; 13 | MANPAGER = "less -+F"; 14 | GROFF_SGR = "1"; 15 | GROFF_BIN_PATH = "${pkgs.writeShellScriptBin "grotty" '' 16 | exec ${pkgs.groff}/bin/grotty -i "$@" 17 | ''}/bin"; 18 | }; 19 | 20 | programs.command-not-found.enable = false; 21 | 22 | programs.bash = { 23 | promptInit = readFile ./prompt.bash; 24 | interactiveShellInit = '' 25 | if [[ ! -v SHLVL_BASE ]]; then 26 | export SHLVL_BASE=$SHLVL 27 | fi 28 | ''; 29 | shellAliases = { 30 | # Default flags 31 | comm = "comm --output-delimiter=$'\\t\\t'"; 32 | cp = "cp -i"; 33 | df = "df -h"; 34 | du = "du -h"; 35 | ffmpeg = "ffmpeg -hide_banner"; 36 | ffplay = "ffplay -hide_banner"; 37 | ffprobe = "ffprobe -hide_banner"; 38 | free = "free -h"; 39 | ip = "ip --color=auto"; 40 | ls = "ls -h --color=auto --group-directories-first"; 41 | lsblk = "lsblk -o NAME,TYPE,UUID,PARTLABEL,FSTYPE,LABEL,SIZE,MOUNTPOINT"; 42 | mv = "mv -i"; 43 | nix = "nix -vL"; 44 | 45 | # Shorthands 46 | C = "LC_ALL=C "; 47 | diff = "git diff --no-index --no-prefix"; 48 | dp = "declare -p"; 49 | fc-grep = "fc-list | rg -i"; 50 | serve = "http-server"; 51 | l = "ls -l"; 52 | ll = "ls -la"; 53 | nwd = "nix why-depends"; 54 | o = "xdg-open"; 55 | rgs = "rg --sort path"; 56 | tall = "tail -f -n +1"; 57 | s = "sudo systemctl"; 58 | u = "systemctl --user"; 59 | j = "journalctl"; 60 | top = "htop"; 61 | vim-patch = "vim -c 'au! mangle' --cmd 'let b:EditorConfig_disable = 1'"; 62 | 63 | # Force alias expansion after these commands 64 | exec = "exec "; 65 | rlwrap = "rlwrap "; 66 | sudo = "sudo "; 67 | watch = "watch "; 68 | }; 69 | }; 70 | 71 | hm = { 72 | programs = { 73 | bash = { 74 | enable = true; 75 | historyControl = [ "erasedups" "ignoredups" "ignorespace" ]; 76 | historyIgnore = [ "ls" "l" "ll" "la" ]; 77 | shellOptions = [ "autocd" "extglob" "globstar" "histappend" ]; 78 | initExtra = mkMerge [ 79 | (mkBefore (readFile ./functions.bash)) 80 | '' 81 | stty -ixon 82 | set -b +H 83 | 84 | complete -F _command C cxa cxan 85 | complete -v dp 86 | complete -f diff 87 | complete_alias drv nix derivation show 88 | complete_alias nwd nix why-depends 89 | complete_alias s systemctl 90 | complete_alias u systemctl --user 91 | complete_alias j journalctl 92 | '' 93 | (mkAfter '' 94 | if [[ $BASH_STARTUP ]]; then eval "$BASH_STARTUP"; fi 95 | '') 96 | ]; 97 | logoutExtra = '' 98 | pkill xsel # prevents SSH connections from hanging 99 | ''; 100 | }; 101 | 102 | readline = { 103 | enable = true; 104 | variables = { 105 | colored-completion-prefix = true; 106 | completion-display-width = 0; 107 | mark-symlinked-directories = true; 108 | show-all-if-ambiguous = true; 109 | page-completions = false; 110 | }; 111 | bindings = { 112 | "\\ef" = "shell-forward-word"; 113 | "\\eb" = "shell-backward-word"; 114 | "\\e[A" = "history-search-backward"; 115 | "\\e[B" = "history-search-forward"; 116 | "\\er" = ''"\C-asudo \C-e"''; 117 | "\\ec" = ''"\C-a\ed"''; 118 | "\\ev" = ''"\C-a\edvim"''; 119 | "\\el" = ''"\C-e | less"''; 120 | }; 121 | }; 122 | 123 | dircolors = { 124 | enable = true; 125 | settings = { 126 | TERM = "*"; 127 | DIR = "1"; 128 | LINK = "target"; 129 | ORPHAN = "3;31"; 130 | EXEC = "1;35"; 131 | OTHER_WRITABLE = "37;42"; 132 | }; 133 | }; 134 | }; 135 | }; 136 | } 137 | -------------------------------------------------------------------------------- /modules/shell/prompt.bash: -------------------------------------------------------------------------------- 1 | prompt_git() { 2 | local branch=$(__git_ps1 %s) 3 | if [[ $branch && $branch != @(master|main) ]]; then 4 | printf ' \1\e[34m\2%s\1\e[0m\2' "$branch" 5 | fi 6 | } 7 | 8 | prompt_symbol() { 9 | for (( i = SHLVL_BASE; i < SHLVL; i++ )) do 10 | printf '+' 11 | done 12 | if [[ -v DIRENV_FILE && -v IN_NIX_SHELL ]]; then 13 | printf '\1\e[34m\2$\1\e[0m\2' 14 | else 15 | printf '$' 16 | fi 17 | } 18 | 19 | declare -A kana=([wo]=を [mu]=む [mo]=も [no]=の [fu]=ふ [ki]=き [ku]=く [tsu]=つ) 20 | if [[ $TERM != *linux* && -v 'kana[$HOSTNAME]' ]]; then 21 | hostname_pretty=${kana[$HOSTNAME]} 22 | else 23 | hostname_pretty=$HOSTNAME 24 | fi 25 | 26 | PS1='\['$(tput sgr0)'\]'${SSH_CONNECTION+$hostname_pretty }'\['$(tput bold)'\]\w\['$(tput sgr0)'\]$(prompt_git) $(prompt_symbol) ' 27 | PS2='\['$(tput sgr0)'\]> ' 28 | 29 | pwd_prompt_string='\W' 30 | PROMPT_COMMAND+=${PROMPT_COMMAND:+;}'printf "\\033]0;%s\\007" "${SSH_CONNECTION+$hostname_pretty:}${pwd_prompt_string@P}"' 31 | 32 | if [[ $TERM != *linux* ]]; then 33 | trap '[[ -v AT_PROMPT ]] && (( AT_PROMPT )) && AT_PROMPT=0 SECONDS_LAST=$SECONDS' debug 34 | PROMPT_COMMAND+=${PROMPT_COMMAND:+;}'(( ! NO_ALARM && SECONDS - SECONDS_LAST >= 3 )) && { (( SECONDS_ELAPSED = SECONDS - SECONDS_LAST )); printf \\a; }; AT_PROMPT=1 NO_ALARM=0' 35 | fi 36 | -------------------------------------------------------------------------------- /modules/ssh.nix: -------------------------------------------------------------------------------- 1 | { lib, this, config, pkgs, ... }: with lib; { 2 | services.openssh = { 3 | enable = true; 4 | ports = mkIf (this.sshPort != null) [ this.sshPort ]; 5 | settings.PasswordAuthentication = false; 6 | settings.X11Forwarding = true; 7 | }; 8 | 9 | programs.ssh.extraConfig = '' 10 | StrictHostKeyChecking accept-new 11 | ''; 12 | 13 | environment.systemPackages = with pkgs; [ autossh ]; 14 | 15 | programs.mosh.enable = true; 16 | 17 | environment.variables.SSH_ASKPASS = mkForce ""; 18 | environment.variables.MOSH_TITLE_NOPREFIX = "1"; 19 | 20 | hm.programs.ssh = { 21 | enable = true; 22 | matchBlocks = mkMerge ( 23 | mapAttrsToList (_: m: let 24 | hosts = concatStringsSep " " ( 25 | [ 26 | m.hostname 27 | "${m.hostname}.home" 28 | "${m.hostname}.local" 29 | "${m.hostname}.${config.networking.wireguard.interface}" 30 | ] ++ optionals (m ? wireguard) [ 31 | m.wireguard.ipv4 m.wireguard.ipv6 32 | ] ++ optionals m.isServer [ 33 | my.domain "*.${my.domain}" 34 | ] ++ optionals (m.hostname == this.hostname) [ 35 | "localhost" "127.0.0.1" "::1" 36 | ] ++ m.ipv4 ++ m.ipv6 37 | ); 38 | in { 39 | ${hosts} = optionalAttrs (m.sshPort != null) { 40 | port = m.sshPort; 41 | } // optionalAttrs this.isStation { 42 | forwardX11 = true; 43 | forwardX11Trusted = true; 44 | }; 45 | } // optionalAttrs m.isServer { 46 | "unlock.${m.hostname}" = { 47 | hostname = head m.ipv4; 48 | user = "root"; 49 | extraOptions = { 50 | RemoteCommand = "cryptsetup-askpass"; 51 | RequestTTY = "yes"; 52 | }; 53 | }; 54 | }) (my.machinesWith "hostname") ++ [ { 55 | "ens sas sas.eleves.ens.fr" = { 56 | hostname = "sas.eleves.ens.fr"; 57 | user = "nfavier"; 58 | }; 59 | 60 | "phare phare.normalesup.org" = { 61 | hostname = "phare.normalesup.org"; 62 | user = "nfavier"; 63 | }; 64 | 65 | "zeus zeus.ens.wtf" = { 66 | hostname = "zeus.ens.wtf"; 67 | port = 4022; 68 | user = "nf"; 69 | }; 70 | } ] 71 | ); 72 | }; 73 | } 74 | -------------------------------------------------------------------------------- /modules/station/alacritty.nix: -------------------------------------------------------------------------------- 1 | { lib, config, ... }: with lib; { 2 | hm.programs.alacritty = { 3 | enable = true; 4 | settings = with config.theme; { 5 | window = { 6 | dimensions = { 7 | columns = 80; 8 | lines = 25; 9 | }; 10 | padding = let 11 | inherit (config.services.xserver) dpi; 12 | padding' = if dpi != null then padding * 96 / dpi else padding; # undo DPI scaling 13 | in { 14 | x = padding'; 15 | y = padding'; 16 | }; 17 | decorations = "none"; 18 | }; 19 | font = { 20 | normal.family = font; 21 | size = fontSize; 22 | }; 23 | colors = { 24 | draw_bold_text_with_bright_colors = true; 25 | primary = { 26 | inherit background foreground; 27 | }; 28 | normal = { 29 | black = background; 30 | red = hot; 31 | green = cold; 32 | yellow = hot; 33 | blue = cold; 34 | magenta = hot; 35 | cyan = cold; 36 | white = foregroundAlt; 37 | }; 38 | bright = { 39 | black = backgroundAlt; 40 | red = hot; 41 | green = cold; 42 | yellow = hot; 43 | blue = cold; 44 | magenta = hot; 45 | cyan = cold; 46 | white = foreground; 47 | }; 48 | }; 49 | selection.save_to_clipboard = true; 50 | cursor = { 51 | style.blinking = "Always"; 52 | blink_timeout = 0; 53 | }; 54 | keyboard.bindings = let 55 | # https://github.com/nix-community/home-manager/pull/611#discussion_r1115932168 56 | # https://github.com/nix-community/home-manager/pull/4817#discussion_r1441726084 57 | esc = (builtins.fromTOML ''c = "\u001b"'').c; 58 | in [ 59 | { key = 3; mods = "Alt"; chars = "${esc}2"; } 60 | { key = 8; mods = "Alt"; chars = "${esc}7"; } 61 | { key = 10; mods = "Alt"; chars = "${esc}9"; } 62 | { key = 11; mods = "Alt"; chars = "${esc}0"; } 63 | ]; 64 | }; 65 | }; 66 | } 67 | -------------------------------------------------------------------------------- /modules/station/backlight.nix: -------------------------------------------------------------------------------- 1 | { pkgs, ... }: { 2 | programs.light.enable = true; 3 | 4 | environment.systemPackages = with pkgs; [ 5 | (writeShellScriptBin "backlight" '' 6 | if (( ! $# )); then 7 | light -G 8 | elif [[ $1 =~ ^[+-]([0-9]*)$ ]]; then 9 | n=''${BASH_REMATCH[1]:-1} 10 | if [[ $1 == +* ]]; then 11 | light -A "$n" 12 | else 13 | light -U "$n" 14 | fi 15 | fi 16 | '') 17 | ]; 18 | } 19 | -------------------------------------------------------------------------------- /modules/station/broadcasting.nix: -------------------------------------------------------------------------------- 1 | { lib, config, pkgs, ... }: with lib; mkEnableModule [ "broadcasting" ] { 2 | hm.programs.obs-studio = { 3 | enable = true; 4 | plugins = with pkgs.obs-studio-plugins; [ 5 | obs-vkcapture 6 | ] ++ optional (config.sound-backend == "pipewire") obs-pipewire-audio-capture; 7 | }; 8 | } 9 | -------------------------------------------------------------------------------- /modules/station/default.nix: -------------------------------------------------------------------------------- 1 | { lib, this, pkgs, ... }: with lib; optionalAttrs this.isStation { 2 | imports = attrValues (modulesIn ./.); 3 | 4 | config = { 5 | boot.kernelParams = [ "mitigations=off" ]; 6 | boot.kernel.sysctl."kernel.sysrq" = 1; 7 | 8 | environment.etc."systemd/system-sleep/batenergy".source = pkgs.writeShellScript "batenergy" '' 9 | PATH=${makeBinPath [ pkgs.coreutils pkgs.bc ]} 10 | source ${pkgs.fetchFromGitHub { 11 | owner = "equaeghe"; 12 | repo = "batenergy"; 13 | rev = "13c381f68f198af361c5bd682b32577131fbb60f"; 14 | hash = "sha256-4JQrSD8HuBDPbBGy2b/uzDvrBUZ8+L9lAnK95rLqASk="; 15 | }}/batenergy.sh "$@" 16 | ''; 17 | 18 | programs.adb.enable = true; 19 | 20 | programs.wireshark = { 21 | enable = true; 22 | package = pkgs.wireshark; 23 | }; 24 | 25 | my.extraGroups = [ "audio" "video" "wireshark" "adbusers" ]; 26 | 27 | environment.systemPackages = with pkgs; [ 28 | gparted 29 | gnome-system-monitor 30 | gnome-disk-utility 31 | ]; 32 | 33 | hm.home.packages = with pkgs; [ 34 | chromium 35 | thunderbird 36 | discord 37 | tdesktop 38 | signal-desktop 39 | libreoffice-fresh 40 | xournalpp 41 | pandoc 42 | inlyne 43 | typos 44 | ((pkgs.mine "agda-bump" "sha256-o7ROtc5q4ISWsF+nuENjeITzaZsaXO7OiNOLg9+h88k=").agda.withPackages (p: with p; [ 45 | standard-library 46 | cubical 47 | ])) 48 | coq 49 | elan 50 | ocamlPackages.cooltt 51 | audacity 52 | gimp 53 | evince 54 | inkscape 55 | poppler_utils 56 | transmission_4-gtk 57 | qemu 58 | (writeShellScriptBin "power" '' 59 | actions=(shutdown reboot suspend "lock and suspend" logout) 60 | printf '%s\n' "''${actions[@]}" | 61 | case $(rofi -dmenu -p action -no-fixed-num-lines -theme-str 'window{width:200;}') in 62 | shutdown) sudo poweroff;; 63 | reboot) sudo reboot;; 64 | suspend) sudo systemctl suspend;; 65 | "lock and suspend") wm lock && sudo systemctl suspend;; 66 | logout) wm quit;; 67 | esac 68 | '') 69 | (shellScriptWith "shoot" ./shoot.sh { 70 | deps = [ slop imagemagick ffmpeg-full ffmpegthumbnailer ]; 71 | }) 72 | tmsu 73 | ]; 74 | 75 | hm.programs.texlive = { 76 | enable = true; 77 | extraPackages = tpkgs: { 78 | inherit (tpkgs) 79 | scheme-medium 80 | collection-latexextra 81 | collection-fontsextra 82 | collection-bibtexextra 83 | collection-langcjk 84 | collection-publishers 85 | ; 86 | }; 87 | }; 88 | 89 | hm.xdg.configFile."agda/defaults".text = '' 90 | standard-library 91 | cubical 92 | ''; 93 | 94 | cachix.derivationsToPush = [ pkgs.tmsu ]; 95 | }; 96 | } 97 | -------------------------------------------------------------------------------- /modules/station/discord/default.nix: -------------------------------------------------------------------------------- 1 | { lib, config, pkgs, ... }: with lib; { 2 | hm.home.packages = with pkgs; [ discord ]; 3 | hm.xdg.configFile."discord/settings.json".source = 4 | config.lib.meta.mkMutableSymlink ./settings.json; 5 | } 6 | -------------------------------------------------------------------------------- /modules/station/discord/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "OPEN_ON_STARTUP": false, 3 | "IS_MAXIMIZED": false, 4 | "IS_MINIMIZED": false, 5 | "SKIP_HOST_UPDATE": true, 6 | "WINDOW_BOUNDS": { 7 | "x": 12, 8 | "y": 38, 9 | "width": 1512, 10 | "height": 814 11 | } 12 | } -------------------------------------------------------------------------------- /modules/station/dunst.nix: -------------------------------------------------------------------------------- 1 | { lib, config, pkgs, ... }: with lib; { 2 | hm.home.packages = [ pkgs.libnotify ]; 3 | 4 | hm.services.dunst = { 5 | enable = true; 6 | 7 | settings = with config.theme; rec { 8 | global = { 9 | enable_recursive_icon_lookup = true; 10 | width = "(0, 1200)"; 11 | height = 9999; 12 | offset = "${toString (padding * 2)}x${toString (barHeight + padding * 2)}"; 13 | notification_limit = 6; 14 | shrink = true; 15 | inherit padding; 16 | horizontal_padding = padding; 17 | frame_width = 1; 18 | frame_color = borderColor; 19 | separator_color = "frame"; 20 | font = pangoFont; 21 | markup = "full"; 22 | format = "%s\\n%b %p"; 23 | word_wrap = true; 24 | icon_theme = config.hm.gtk.iconTheme.name; 25 | icon_position = "right"; 26 | min_icon_size = 64; 27 | max_icon_size = 500; 28 | dmenu = "rofi -dmenu -p dunst -no-fixed-num-lines"; 29 | browser = "xdg-open"; 30 | mouse_right_click = "context"; 31 | show_indicators = false; 32 | }; 33 | 34 | urgency_low = urgency_normal; 35 | 36 | urgency_normal = { 37 | inherit background foreground; 38 | timeout = 10; 39 | }; 40 | 41 | urgency_critical = { 42 | background = hot; 43 | inherit foreground; 44 | timeout = 0; 45 | }; 46 | }; 47 | }; 48 | } 49 | -------------------------------------------------------------------------------- /modules/station/feh.nix: -------------------------------------------------------------------------------- 1 | { lib, config, pkgs, ... }: with lib; { 2 | hm = { 3 | programs.feh = { 4 | enable = true; 5 | 6 | buttons = { 7 | prev_img = null; 8 | zoom_in = 4; 9 | next_img = null; 10 | zoom_out = 5; 11 | }; 12 | 13 | keybindings = { 14 | save_filelist = null; 15 | toggle_fullscreen = null; 16 | action_9 = "f"; 17 | action_0 = null; 18 | render = "Return"; 19 | orient_1 = null; 20 | orient_3 = null; 21 | prev_img = [ "Left" "less" ]; 22 | next_img = [ "Right" "greater" "space" ]; 23 | close = [ "Escape" "BackSpace" ]; 24 | quit = "q"; 25 | }; 26 | 27 | themes.feh = [ 28 | "-B" config.theme.background 29 | "-g" "1200x800" 30 | "-Z" "-." 31 | "--action9" ";bspc node -t ~fullscreen" 32 | "--sort" "mtime" "--reverse" 33 | ]; 34 | }; 35 | 36 | home.packages = with pkgs; [ 37 | (writeShellScriptBin "random-wallpaper" '' 38 | pictures=$(xdg-user-dir PICTURES) 39 | wallpaper=$(find "$pictures/horizontal" "$pictures/tileable" -type f | shuf -n 1) 40 | case $wallpaper in 41 | $pictures/horizontal/*) 42 | mode=fill;; 43 | $pictures/tileable/*) 44 | mode=tile;; 45 | esac 46 | feh --bg-"$mode" "$wallpaper" 47 | '') 48 | ]; 49 | }; 50 | } 51 | -------------------------------------------------------------------------------- /modules/station/games/default.nix: -------------------------------------------------------------------------------- 1 | { lib, config, pkgs, ... }: with lib; { 2 | imports = attrValues (modulesIn ./.); 3 | 4 | nixpkgs.config.allowUnfree = true; 5 | 6 | programs.steam = { 7 | enable = true; 8 | extraCompatPackages = with pkgs; [ proton-ge-bin ]; 9 | }; 10 | 11 | programs.gamemode.enable = true; 12 | 13 | services.libinput.mouse.middleEmulation = false; 14 | 15 | hm = { 16 | home.packages = with pkgs; [ 17 | legendary-gl 18 | teeworlds 19 | ddnet 20 | # (writeShellScriptBin "zcatch" '' 21 | # exec ${zcatch}/bin/zcatch_srv -f zcatch.cfg "$@" 22 | # '') 23 | (pkgs.dwarf-fortress.override { 24 | enableTruetype = false; 25 | settings.init.FONT = "Alloy_curses_12x12.png"; 26 | extraPackages = [ 27 | (pkgs.runCommandLocal "alloy-curses" { } '' 28 | mkdir -p "$out/data/art" 29 | ln -s ${pkgs.fetchurl { 30 | url = "https://dwarffortresswiki.org/images/0/03/Alloy_curses_12x12.png"; 31 | hash = "sha256-r+ossYw3BF5rp+Da1Mle0kBKFsA0/IHGD3ENe3JAyHk="; 32 | }} "$out/data/art/Alloy_curses_12x12.png" 33 | '') 34 | ]; 35 | }) 36 | ]; 37 | 38 | xdg.dataFile."ddnet".source = 39 | config.hm.lib.file.mkOutOfStoreSymlink "${config.hm.xdg.dataHome}/teeworlds"; 40 | 41 | xdg.dataFile."teeworlds/zcatch.cfg".text = '' 42 | sv_register 0 43 | sv_port 8303 44 | sv_db_type "" 45 | sv_map ctf5 46 | exec maps/maps.cfg 47 | sv_weapon_mode 3 48 | sv_inactivekick_time 0 49 | ''; 50 | 51 | xdg.dataFile."df_linux/data/save".source = 52 | config.hm.lib.file.mkOutOfStoreSymlink "${config.synced.saves.path}/df"; 53 | }; 54 | 55 | nixpkgs.overlays = [ (pkgs: prev: { 56 | zcatch = with pkgs; stdenv.mkDerivation { 57 | pname = "zcatch"; 58 | version = "0.3.5"; 59 | src = fetchFromGitHub { 60 | owner = "jxsl13"; 61 | repo = "zcatch"; 62 | rev = "e6e87a7fd84f24ef9306c39275ad7003965e55dd"; 63 | sha256 = "sha256-3rFHUWyjGRHgVld8uj7BAaTndpAGWgSMwXUfhNsfbgY="; 64 | fetchSubmodules = true; 65 | }; 66 | postPatch = '' 67 | substituteInPlace src/engine/shared/storage.cpp --replace /usr/share "$out/share" 68 | ''; 69 | nativeBuildInputs = [ cmake ninja python3 ]; 70 | cmakeFlags = [ "-GNinja" "-DCLIENT=OFF" ]; 71 | }; 72 | }) ]; 73 | } 74 | -------------------------------------------------------------------------------- /modules/station/games/minecraft/default.nix: -------------------------------------------------------------------------------- 1 | { config, pkgs, ... }: let 2 | version = "1.18.2"; 3 | minecraftHome = "${config.hm.xdg.dataHome}/PrismLauncher/instances/Simply Optimized/.minecraft"; 4 | in { 5 | hm = { 6 | home.packages = with pkgs; [ 7 | prismlauncher 8 | # minecraft 9 | # (writeShellScriptBin "minecraft-install-fabric" '' 10 | # ${fabric-installer}/bin/fabric-installer client -mcversion ${version} "$@" 11 | # # hide the "player safety disclaimer" 12 | # profile=$(jq -r '.profiles."fabric-loader-${version}" | "\(.lastVersionId)_\(.name)"' ~/.minecraft/launcher_profiles.json) 13 | # state=$(jq --arg profile "$profile" \ 14 | # '.data.UiEvents |= (fromjson | (.hidePlayerSafetyDisclaimer[$profile] = true) | tojson)' ~/.minecraft/launcher_ui_state.json) && 15 | # printf '%s\n' "$state" > ~/.minecraft/launcher_ui_state.json 16 | # '') 17 | ]; 18 | 19 | # home.file.".minecraft/mods".source = pkgs.linkFarmFromDrvs "minecraft-mods" [ 20 | # (pkgs.fetchurl { 21 | # url = "https://github.com/CaffeineMC/sodium-fabric/releases/download/mc1.18.2-0.4.1/sodium-fabric-mc1.18.2-0.4.1+build.15.jar"; 22 | # sha256 = "sha256-d2+zzYyN3uiY6x2dyIpy2JmqqHkvIZFLOa2ZDOolN4Q="; 23 | # }) 24 | # (pkgs.fetchurl { 25 | # url = "https://github.com/CaffeineMC/lithium-fabric/releases/download/mc1.18.2-0.7.9/lithium-fabric-mc1.18.2-0.7.9.jar"; 26 | # sha256 = "sha256-GdV7g/3YhZOiGSEabmk2vY3ZCVf9GeVoH/qFb+Nlv1g="; 27 | # }) 28 | # (pkgs.fetchurl { 29 | # url = "https://github.com/CaffeineMC/phosphor-fabric/releases/download/mc1.18.x-0.8.1/phosphor-fabric-mc1.18.x-0.8.1.jar"; 30 | # sha256 = "sha256-QqE532MANA8jzaRFf8C6fjkSTBusBdiSMrAWe9UeanU="; 31 | # }) 32 | # ]; 33 | 34 | home.file."${minecraftHome}/saves".source = 35 | config.hm.lib.file.mkOutOfStoreSymlink "${config.synced.saves.path}/minecraft"; 36 | home.file."${minecraftHome}/options.txt".source = 37 | config.lib.meta.mkMutableSymlink ./options.txt; 38 | home.file."${minecraftHome}/config/sodium-options.json" = { 39 | source = config.lib.meta.mkMutableSymlink ./sodium.json; 40 | force = true; 41 | }; 42 | }; 43 | } 44 | -------------------------------------------------------------------------------- /modules/station/games/minecraft/options.txt: -------------------------------------------------------------------------------- 1 | version:3700 2 | autoJump:false 3 | operatorItemsTab:false 4 | autoSuggestions:true 5 | chatColors:true 6 | chatLinks:true 7 | chatLinksPrompt:true 8 | enableVsync:false 9 | entityShadows:true 10 | forceUnicodeFont:false 11 | discrete_mouse_scroll:false 12 | invertYMouse:false 13 | realmsNotifications:true 14 | reducedDebugInfo:false 15 | showSubtitles:false 16 | directionalAudio:false 17 | touchscreen:false 18 | fullscreen:true 19 | bobView:false 20 | toggleCrouch:false 21 | toggleSprint:false 22 | darkMojangStudiosBackground:true 23 | hideLightningFlashes:false 24 | hideSplashTexts:false 25 | mouseSensitivity:0.9014084507042254 26 | fov:0.5 27 | screenEffectScale:1.0 28 | fovEffectScale:1.0 29 | darknessEffectScale:1.0 30 | glintSpeed:0.5 31 | glintStrength:0.75 32 | damageTiltStrength:1.0 33 | highContrast:false 34 | narratorHotkey:true 35 | gamma:0.99 36 | renderDistance:12 37 | simulationDistance:5 38 | entityDistanceScaling:1.0 39 | guiScale:0 40 | particles:0 41 | maxFps:260 42 | graphicsMode:0 43 | ao:true 44 | prioritizeChunkUpdates:0 45 | biomeBlendRadius:2 46 | renderClouds:"true" 47 | resourcePacks:["fabric"] 48 | incompatibleResourcePacks:[] 49 | lastServer: 50 | lang:en_us 51 | soundDevice:"" 52 | chatVisibility:0 53 | chatOpacity:1.0 54 | chatLineSpacing:0.0 55 | textBackgroundOpacity:0.5 56 | backgroundForChatOnly:true 57 | hideServerAddress:false 58 | advancedItemTooltips:false 59 | pauseOnLostFocus:true 60 | overrideWidth:0 61 | overrideHeight:0 62 | chatHeightFocused:1.0 63 | chatDelay:0.0 64 | chatHeightUnfocused:0.4375 65 | chatScale:1.0 66 | chatWidth:1.0 67 | notificationDisplayTime:1.0 68 | mipmapLevels:4 69 | useNativeTransport:true 70 | mainHand:"right" 71 | attackIndicator:1 72 | narrator:0 73 | tutorialStep:none 74 | mouseWheelSensitivity:1.0 75 | rawMouseInput:true 76 | glDebugVerbosity:1 77 | skipMultiplayerWarning:true 78 | skipRealms32bitWarning:false 79 | hideMatchedNames:true 80 | joinedFirstServer:true 81 | hideBundleTutorial:false 82 | syncChunkWrites:false 83 | showAutosaveIndicator:true 84 | allowServerListing:true 85 | onlyShowSecureChat:false 86 | panoramaScrollSpeed:1.0 87 | telemetryOptInExtra:false 88 | onboardAccessibility:false 89 | key_key.attack:key.mouse.left 90 | key_key.use:key.mouse.right 91 | key_key.forward:key.keyboard.w 92 | key_key.left:key.keyboard.a 93 | key_key.back:key.keyboard.s 94 | key_key.right:key.keyboard.d 95 | key_key.jump:key.keyboard.space 96 | key_key.sneak:key.keyboard.left.shift 97 | key_key.sprint:key.keyboard.left.control 98 | key_key.drop:key.keyboard.q 99 | key_key.inventory:key.keyboard.e 100 | key_key.chat:key.keyboard.t 101 | key_key.playerlist:key.keyboard.tab 102 | key_key.pickItem:key.mouse.middle 103 | key_key.command:key.keyboard.slash 104 | key_key.socialInteractions:key.keyboard.p 105 | key_key.screenshot:key.keyboard.f2 106 | key_key.togglePerspective:key.keyboard.f5 107 | key_key.smoothCamera:key.keyboard.unknown 108 | key_key.fullscreen:key.keyboard.f11 109 | key_key.spectatorOutlines:key.keyboard.unknown 110 | key_key.swapOffhand:key.keyboard.f 111 | key_key.saveToolbarActivator:key.keyboard.c 112 | key_key.loadToolbarActivator:key.keyboard.x 113 | key_key.advancements:key.keyboard.l 114 | key_key.hotbar.1:key.keyboard.1 115 | key_key.hotbar.2:key.keyboard.2 116 | key_key.hotbar.3:key.keyboard.3 117 | key_key.hotbar.4:key.keyboard.4 118 | key_key.hotbar.5:key.keyboard.5 119 | key_key.hotbar.6:key.keyboard.6 120 | key_key.hotbar.7:key.keyboard.7 121 | key_key.hotbar.8:key.keyboard.8 122 | key_key.hotbar.9:key.keyboard.9 123 | key_key.entityculling.toggle:key.keyboard.unknown 124 | soundCategory_master:0.7367549668874173 125 | soundCategory_music:0.1 126 | soundCategory_record:1.0 127 | soundCategory_weather:1.0 128 | soundCategory_block:1.0 129 | soundCategory_hostile:1.0 130 | soundCategory_neutral:1.0 131 | soundCategory_player:1.0 132 | soundCategory_ambient:1.0 133 | soundCategory_voice:1.0 134 | modelPart_cape:true 135 | modelPart_jacket:true 136 | modelPart_left_sleeve:true 137 | modelPart_right_sleeve:true 138 | modelPart_left_pants_leg:true 139 | modelPart_right_pants_leg:true 140 | modelPart_hat:true 141 | -------------------------------------------------------------------------------- /modules/station/games/minecraft/sodium.json: -------------------------------------------------------------------------------- 1 | { 2 | "quality": { 3 | "cloud_quality": "DEFAULT", 4 | "weather_quality": "DEFAULT", 5 | "enable_vignette": true, 6 | "enable_clouds": true 7 | }, 8 | "advanced": { 9 | "arena_memory_allocator": "ASYNC", 10 | "animate_only_visible_textures": true, 11 | "use_entity_culling": true, 12 | "use_particle_culling": true, 13 | "use_fog_occlusion": true, 14 | "use_block_face_culling": true, 15 | "allow_direct_memory_access": true, 16 | "enable_memory_tracing": false, 17 | "use_advanced_staging_buffers": true, 18 | "max_pre_rendered_frames": 3 19 | }, 20 | "notifications": { 21 | "hide_donation_button": true 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /modules/station/ghostty.nix: -------------------------------------------------------------------------------- 1 | { inputs, lib, config, pkgs, ...}: with lib; { 2 | hm.programs.ghostty = { 3 | enable = true; 4 | package = inputs.ghostty.packages.x86_64-linux.default; 5 | installVimSyntax = true; 6 | 7 | settings = with config.theme; { 8 | gtk-single-instance = true; 9 | window-decoration = false; 10 | font-family = [ "monospace" "emoji" ]; 11 | font-codepoint-map = "U+2600-U+27BF,U+2B00-U+2BFF,U+1F300-U+1F5FF=emoji"; 12 | window-title-font-family = "monospace"; 13 | window-padding-x = 16; 14 | window-padding-y = 16; 15 | theme = "Aura"; 16 | cursor-style = "block"; 17 | cursor-color = "white"; 18 | shell-integration-features = "no-cursor"; 19 | mouse-hide-while-typing = true; 20 | copy-on-select = "clipboard"; 21 | app-notifications = [ "no-clipboard-copy" ]; 22 | keybind = [ 23 | "alt+é=esc:é" 24 | "alt+è=esc:è" 25 | "alt+ç=esc:ç" 26 | "alt+à=esc:à" 27 | ]; 28 | 29 | background = background; 30 | foreground = foreground; 31 | cursor-text = background; 32 | selection-background = hot; 33 | palette = [ 34 | "0=${background}" 35 | "1=${hot}" 36 | "2=${cold}" 37 | "3=${hot}" 38 | "4=${cold}" 39 | "5=${hot}" 40 | "6=${cold}" 41 | "7=${foregroundAlt}" 42 | "8=${backgroundAlt}" 43 | "9=${hot}" 44 | "10=${cold}" 45 | "11=${hot}" 46 | "12=${cold}" 47 | "13=${hot}" 48 | "14=${cold}" 49 | "15=${foreground}" 50 | ]; 51 | 52 | gtk-custom-css = "${pkgs.writeText "ghostty.css" '' 53 | window { 54 | border-radius: 0 0; 55 | } 56 | ''}"; 57 | }; 58 | }; 59 | } 60 | -------------------------------------------------------------------------------- /modules/station/gtk.nix: -------------------------------------------------------------------------------- 1 | { lib, config, pkgs, ... }: with lib; { 2 | hm = { 3 | home.packages = [ pkgs.lxappearance ]; 4 | 5 | gtk = with config.theme; { 6 | enable = true; 7 | gtk2 = { 8 | configLocation = "${config.hm.xdg.configHome}/gtk-2.0/gtkrc"; 9 | }; 10 | gtk3 = { 11 | bookmarks = [ 12 | "file://${config.my.home}/my" 13 | "file://${config.my.home}/git" 14 | "file://${config.hm.xdg.userDirs.pictures}" 15 | "file://${config.hm.xdg.userDirs.music}" 16 | "file://${config.hm.xdg.userDirs.videos}" 17 | ]; 18 | extraConfig = { 19 | gtk-button-images = true; 20 | gtk-menu-images = true; 21 | gtk-xft-antialias = 1; 22 | gtk-xft-hinting = 1; 23 | gtk-recent-files-enabled = false; 24 | }; 25 | }; 26 | font = { 27 | name = gtkFont; 28 | size = 10; 29 | }; 30 | theme = { 31 | package = pkgs.orchis-theme.override { 32 | tweaks = [ "black" "primary" ]; 33 | }; 34 | name = gtkTheme; 35 | }; 36 | iconTheme = { 37 | package = pkgs.tela-icon-theme; 38 | name = iconTheme; 39 | }; 40 | }; 41 | 42 | xdg.configFile."gtk-3.0/settings.ini".force = true; 43 | xdg.configFile."gtk-3.0/bookmarks".force = true; 44 | 45 | home.pointerCursor.gtk.enable = true; 46 | 47 | # live reloading 48 | services.xsettingsd = { 49 | enable = true; 50 | settings = { 51 | "Net/ThemeName" = mkIf (config.hm.gtk.theme != null) config.hm.gtk.theme.name; 52 | "Net/IconThemeName" = mkIf (config.hm.gtk.iconTheme != null) config.hm.gtk.iconTheme.name; 53 | }; 54 | }; 55 | }; 56 | } 57 | -------------------------------------------------------------------------------- /modules/station/im.nix: -------------------------------------------------------------------------------- 1 | { config, pkgs, ... }: { 2 | i18n.inputMethod = { 3 | enable = true; 4 | type = "ibus"; 5 | ibus.engines = with pkgs.ibus-engines; [ 6 | mozc 7 | hangul 8 | typing-booster 9 | table table-others 10 | ]; 11 | }; 12 | 13 | hm = { 14 | xsession.windowManager.bspwm.startupPrograms = [ 15 | "ibus restart || ibus-daemon --daemonize --replace --xim" 16 | ]; 17 | 18 | xdg.configFile."mozc/ibus_config.textproto".text = '' 19 | engines { 20 | name: "mozc-jp" 21 | longname: "Mozc" 22 | layout: "default" 23 | layout_variant: "" 24 | layout_option: "" 25 | rank: 80 26 | } 27 | active_on_launch: True 28 | ''; 29 | 30 | dconf.settings = { 31 | "desktop/ibus/general" = { 32 | use-system-keyboard-layout = true; 33 | preload-engines = [ "xkb:fr:oss:fra" "mozc-jp" "hangul" "typing-booster" "table:latex" ]; 34 | }; 35 | "desktop/ibus/general/hotkey".triggers = [ "i" ]; 36 | "desktop/ibus/panel" = { 37 | show = 0; 38 | lookup-table-orientation = 1; 39 | use-custom-font = true; 40 | custom-font = config.theme.pangoFont; 41 | }; 42 | "org/freedesktop/ibus/engine/hangul" = { 43 | initial-input-mode = "hangul"; 44 | switch-keys = "Hangul,Control+space"; 45 | hangul-keyboard = "ro"; 46 | }; 47 | }; 48 | }; 49 | } 50 | -------------------------------------------------------------------------------- /modules/station/imv.nix: -------------------------------------------------------------------------------- 1 | { lib, config, ... }: with lib; { 2 | hm.programs.imv = { 3 | enable = true; 4 | settings = { 5 | options = { 6 | background = removePrefix "#" config.theme.background; 7 | overlay_font = "monospace:18"; 8 | title_text = "imv - [$imv_current_index/$imv_file_count] $imv_current_file"; 9 | }; 10 | binds = { 11 | i = "overlay"; 12 | "" = "quit"; 13 | "" = "next_frame"; 14 | "" = "prev"; 15 | "" = "next"; 16 | "" = "next"; 17 | "p" = "toggle_playing"; 18 | }; 19 | }; 20 | }; 21 | } 22 | -------------------------------------------------------------------------------- /modules/station/internet-sharing.nix: -------------------------------------------------------------------------------- 1 | { lib, config, pkgs, ... }: with lib; let 2 | cfg = config.networking.sharing; 3 | in { 4 | options.networking.sharing = { 5 | enable = mkEnableOption "Internet sharing (IPv4 only)"; 6 | 7 | internalInterface = mkOption { 8 | type = types.str; 9 | example = "eth0"; 10 | }; 11 | 12 | externalInterface = mkOption { 13 | type = types.str; 14 | example = "wlan0"; 15 | }; 16 | }; 17 | 18 | config = mkIf cfg.enable { 19 | networking = { 20 | interfaces.${cfg.internalInterface} = { 21 | ipv4.addresses = [ 22 | { 23 | address = "192.168.42.1"; 24 | prefixLength = 24; 25 | } 26 | ]; 27 | }; 28 | 29 | firewall.allowedUDPPorts = [ 67 ]; # DHCP 30 | 31 | nat = { 32 | enable = true; 33 | externalInterface = cfg.externalInterface; 34 | internalIPs = [ "192.168.42.0/24" ]; 35 | }; 36 | }; 37 | 38 | systemd.network.networks."40-${cfg.internalInterface}" = { 39 | networkConfig = { 40 | ConfigureWithoutCarrier = true; 41 | DHCPServer = true; 42 | }; 43 | dhcpServerConfig = { 44 | ServerAddress = "192.168.42.1/24"; 45 | EmitDNS = false; 46 | EmitNTP = false; 47 | EmitTimezone = false; 48 | }; 49 | }; 50 | 51 | # Exempt forwarded packets from the WireGuard tunnel 52 | networking.wg-quick.interfaces.${config.networking.wireguard.interface} = let 53 | rule = "PREROUTING -m addrtype ! --dst-type LOCAL -j CONNMARK --set-mark $(wg show ${config.networking.wireguard.interface} fwmark)"; 54 | in { 55 | postUp = [ "iptables -t mangle -I ${rule}" ]; 56 | preDown = [ "iptables -t mangle -D ${rule} || true" ]; 57 | }; 58 | nixpkgs.overlays = [ (self: super: { 59 | wireguard-tools = super.wireguard-tools.overrideAttrs (old: { 60 | patches = old.patches or [] ++ [ (self.fetchpatch { 61 | url = "https://github.com/ncfavier/wireguard-tools/commit/a67eb77a11be418f1a7699bbb28e8674a4d3fe89.patch"; 62 | relative = "src"; 63 | hash = "sha256-bLuSOGHs1toCHyFCaA4qkeyFj5vTjlaPITliMJownt0="; 64 | }) ]; 65 | }); 66 | }) ]; 67 | }; 68 | } 69 | -------------------------------------------------------------------------------- /modules/station/mpv.nix: -------------------------------------------------------------------------------- 1 | { lib, config, pkgs, ... }: with lib; { 2 | hm = { 3 | programs.mpv = { 4 | enable = true; 5 | package = pkgs.mpv.override { 6 | scripts = with pkgs.mpvScripts; [ 7 | autoload 8 | mpris 9 | quality-menu 10 | ]; 11 | }; 12 | 13 | config = { 14 | autofit = "80%x80%"; 15 | force-window = true; 16 | volume-max = 200; 17 | sub-auto = "fuzzy"; 18 | sub-border-size = 1; 19 | 20 | ao = "pulse"; 21 | hwdec = "auto-safe"; 22 | 23 | pulse-latency-hacks = mkIf (config.sound-backend == "pulseaudio") true; 24 | 25 | ytdl-raw-options = "cookies-from-browser=firefox"; 26 | }; 27 | 28 | scriptOpts.autoload = { 29 | directory_mode = "ignore"; 30 | }; 31 | 32 | profiles = { 33 | "loop" = { 34 | loop-file = "inf"; 35 | profile-cond = "p.duration<=30"; 36 | }; 37 | }; 38 | 39 | bindings = { 40 | "Alt+h" = "add video-pan-x 0.01"; 41 | "Alt+l" = "add video-pan-x -0.01"; 42 | "Alt+k" = "add video-pan-y 0.01"; 43 | "Alt+j" = "add video-pan-y -0.01"; 44 | "b" = ''cycle-values background-color "#000000" "#ffffff"''; 45 | "F" = "script-binding quality_menu/video_formats_toggle"; 46 | "Alt+f" = "script-binding quality_menu/audio_formats_toggle"; 47 | "k" = "cycle pause"; 48 | }; 49 | }; 50 | }; 51 | } 52 | -------------------------------------------------------------------------------- /modules/station/music/default.nix: -------------------------------------------------------------------------------- 1 | { config, pkgs, ... }: { 2 | hm = { 3 | services.mpd = { 4 | enable = true; 5 | musicDirectory = config.hm.xdg.userDirs.music; 6 | extraConfig = '' 7 | auto_update "yes" 8 | 9 | ${if config.sound-backend == "pulseaudio" then '' 10 | audio_output { 11 | type "pulse" 12 | name "PulseAudio" 13 | } 14 | '' else '' 15 | audio_output { 16 | type "pipewire" 17 | name "PipeWire" 18 | } 19 | ''} 20 | ''; 21 | }; 22 | 23 | services.mpd-mpris.enable = true; 24 | services.playerctld.enable = true; 25 | 26 | programs.ncmpcpp = { 27 | enable = true; 28 | settings.lyrics_directory = "${config.hm.xdg.dataHome}/lyrics"; # don't pollute ~ 29 | }; 30 | 31 | programs.rofi.extraConfig.modes = [ "music:${ 32 | pkgs.shellScriptWith "music-rofi" ./music-rofi.sh {} 33 | }/bin/music-rofi" ]; 34 | 35 | home.packages = with pkgs; [ 36 | playerctl 37 | mpc_cli 38 | (shellScriptWith "music-play" ./music-play.sh { 39 | deps = [ mpc_cli ]; 40 | }) 41 | (shellScriptWith "music-notify" ./music-notify.sh { 42 | deps = [ mpc_cli playerctl dunst ]; 43 | }) 44 | (shellScriptWith "music-add" ./music-add.sh { 45 | deps = [ imagemagick ffmpeg-full audacity mpc_cli ]; 46 | }) 47 | songrec 48 | ]; 49 | }; 50 | } 51 | -------------------------------------------------------------------------------- /modules/station/music/music-notify.sh: -------------------------------------------------------------------------------- 1 | shopt -s lastpipe 2 | 3 | escape() { 4 | local -n s=$1 o=$1_esc 5 | o=${s//'\'/'\\'} 6 | o=${o//'&'/'&'} 7 | o=${o//'<'/'<'} 8 | o=${o//'>'/'>'} 9 | } 10 | 11 | mpc current -f $'%artist%\n%album%\n%title%' | { 12 | IFS= read -r artist 13 | IFS= read -r album 14 | IFS= read -r title 15 | } 16 | [[ $title ]] || exit 17 | mpc status | { 18 | read -r _ 19 | read -r progress 20 | } 21 | 22 | if artUrl=$(playerctl -p mpd metadata mpris:artUrl 2> /dev/null); then 23 | artUrl=${artUrl#file://} 24 | printf -v artFile '%b' "${artUrl//%//\\x}" 25 | icon=(-I "$artFile") 26 | else 27 | icon=(-i application-audio) 28 | fi 29 | 30 | id=0x1F3B5 # 🎵 31 | escape album; escape title 32 | dunstify "${icon[@]}" -r "$id" "$artist" "$album_esc\n$title_esc\n$progress" 33 | -------------------------------------------------------------------------------- /modules/station/music/music-play.sh: -------------------------------------------------------------------------------- 1 | mpc() { 2 | command mpc -q "$@" 3 | } 4 | 5 | music=$(realpath "$(xdg-user-dir MUSIC)") 6 | files=() 7 | for f do 8 | f=$(realpath "$f") 9 | [[ $f == "$music"/* ]] && files+=("${f#"$music"/}") 10 | done 11 | 12 | if (( ${#files[@]} )); then 13 | mpc clear 14 | mpc add "${files[@]}" 15 | mpc play 16 | fi 17 | -------------------------------------------------------------------------------- /modules/station/music/music-rofi.sh: -------------------------------------------------------------------------------- 1 | shopt -s lastpipe 2 | 3 | music_dir=$(xdg-user-dir MUSIC) 4 | 5 | mpc() { 6 | command mpc -q "$@" 7 | } 8 | 9 | mpc_load() { 10 | mpc clear 11 | if false && [[ $1 == random ]]; then 12 | find -L "$music_dir" -maxdepth 1 -type f -mtime -730 -printf '%f\n' | mpc add 13 | elif [[ $artist || $album ]]; then 14 | mpc findadd ${artist:+artist "$artist"} ${album:+album "$album"} 15 | else 16 | mpc add / 17 | fi 18 | } 19 | 20 | opt() { 21 | printf '\0%s\x1f%s\n' "$1" "$2" 22 | } 23 | 24 | row() { 25 | printf '%s\0info\x1f%s\n' "$1" "${artist@A} ${album@A} ${index@A}" 26 | } 27 | 28 | escape() { 29 | local -n s=$1 o=$1_esc 30 | o=${s//'&'/'&'} 31 | o=${o//'<'/'<'} 32 | o=${o//'>'/'>'} 33 | } 34 | 35 | [[ $ROFI_INFO ]] && eval "$ROFI_INFO" 36 | 37 | if (( ROFI_RETV == 0 )); then # initial invocation 38 | opt prompt artist 39 | opt markup-rows true 40 | opt use-hot-keys true 41 | opt no-custom true 42 | mpc list artist | readarray -t artists 43 | if (( ${#artists[@]} > 1 )); then 44 | artist=; row '*' 45 | fi 46 | for artist in "${artists[@]}"; do 47 | escape artist 48 | row "$artist_esc" 49 | done 50 | elif (( ROFI_RETV >= 10 )); then # custom keybinding: shuffle 51 | if [[ ! -v index ]]; then 52 | mpc_load random 53 | fi 54 | mpc shuffle 55 | mpc play 56 | elif [[ ! -v album ]]; then 57 | opt prompt album 58 | if [[ $artist ]]; then 59 | escape artist 60 | opt message "$artist_esc" 61 | fi 62 | mpc list album ${artist:+artist "$artist"} | readarray -t albums 63 | if (( ${#albums[@]} > 1 )); then 64 | album=; row '*' 65 | fi 66 | for album in "${albums[@]}"; do 67 | escape album 68 | row "$album_esc" 69 | done 70 | elif [[ ! -v index ]]; then 71 | opt prompt track 72 | opt markup-rows false 73 | escape artist; escape album 74 | if [[ $artist && $album ]]; then 75 | opt message "$artist_esc - $album_esc" 76 | elif [[ $artist ]]; then 77 | opt message "$artist_esc" 78 | elif [[ $album ]]; then 79 | opt message "$album_esc" 80 | fi 81 | format='%title%|%file%' 82 | if [[ $album ]]; then 83 | format='[%track% - ]'$format 84 | elif [[ $artist ]]; then 85 | format='[%album% - ]'$format 86 | else 87 | format='[%artist% - ]'$format 88 | fi 89 | mpc_load 90 | mpc playlist -f "$format" | readarray -t tracks 91 | index=1 92 | for track in "${tracks[@]}"; do 93 | row "$track" 94 | (( index++ )) 95 | done 96 | else 97 | mpc play "$index" 98 | fi 99 | -------------------------------------------------------------------------------- /modules/station/openrgb.nix: -------------------------------------------------------------------------------- 1 | { lib, config, pkgs, ... }: with lib; let 2 | loadDefaultProfile = '' 3 | ${getExe config.services.hardware.openrgb.package} --config ${config.hm.xdg.configHome}/OpenRGB -p default || true 4 | ''; 5 | in mkEnableModule [ "my-services" "openrgb" ] { 6 | services.hardware.openrgb = { 7 | enable = true; 8 | 9 | # 1.0rc1 supports my motherboard 10 | package = (pkgs.mine "openrgb" "sha256-aw1L5q1Ac7KlbwVvHMjH+WBrYsMzOvwjftuGGSglLgU=").openrgb; 11 | }; 12 | 13 | # load the default profile on start and after suspend 14 | systemd.services.openrgb.postStart = loadDefaultProfile; 15 | environment.etc."systemd/system-sleep/openrgb".source = pkgs.writeShellScript "openrgb-sleep" '' 16 | if [[ $1 == post ]]; then 17 | ${loadDefaultProfile} 18 | fi 19 | ''; 20 | } 21 | -------------------------------------------------------------------------------- /modules/station/printing.nix: -------------------------------------------------------------------------------- 1 | { pkgs, ... }: { 2 | # nixpkgs.config.allowUnfree = true; 3 | 4 | services.printing = { 5 | enable = true; 6 | }; 7 | 8 | hardware.sane = { 9 | enable = true; 10 | }; 11 | 12 | environment.systemPackages = with pkgs; [ 13 | simple-scan 14 | ]; 15 | 16 | programs.system-config-printer.enable = true; 17 | 18 | services.avahi = { 19 | enable = true; 20 | nssmdns4 = true; 21 | }; 22 | 23 | my.extraGroups = [ "lp" "scanner" ]; 24 | } 25 | -------------------------------------------------------------------------------- /modules/station/qt.nix: -------------------------------------------------------------------------------- 1 | { 2 | qt = { 3 | enable = true; 4 | platformTheme = "gtk2"; 5 | style = "gtk2"; 6 | }; 7 | } 8 | -------------------------------------------------------------------------------- /modules/station/river.nix: -------------------------------------------------------------------------------- 1 | { lib, config, ... }: with lib; mkEnableModule [ "my-programs" "river" ] { 2 | programs.river = { 3 | enable = true; 4 | extraPackages = []; 5 | }; 6 | 7 | hm = { 8 | wayland.windowManager.river = { 9 | enable = true; 10 | package = null; 11 | systemd.enable = false; 12 | 13 | settings = { 14 | map.normal = { 15 | "Super Return" = "spawn ghostty"; 16 | "Super Q" = "close"; 17 | "Super+Shift E" = "exit"; 18 | }; 19 | keyboard-layout = with config.services.xserver.xkb; "-options ${options} -variant ${variant} ${layout}"; 20 | }; 21 | }; 22 | }; 23 | } 24 | -------------------------------------------------------------------------------- /modules/station/rofi.nix: -------------------------------------------------------------------------------- 1 | { lib, config, pkgs, ... }: with lib; { 2 | hm = { 3 | programs.rofi = with config.theme; { 4 | enable = true; 5 | package = with pkgs; rofi.override { 6 | symlink-dmenu = true; 7 | plugins = [ 8 | rofi-calc 9 | ]; 10 | }; 11 | 12 | font = pangoFont; 13 | 14 | theme = with config.hm.lib.formats.rasi; { 15 | "@import" = "default"; 16 | "*" = { 17 | background = mkLiteral background; 18 | lightbg = mkLiteral background; 19 | foreground = mkLiteral foreground; 20 | lightfg = mkLiteral foreground; 21 | red = mkLiteral hot; 22 | blue = mkLiteral cold; 23 | border-color = mkLiteral borderColor; 24 | }; 25 | window = { 26 | inherit padding; 27 | border = borderWidth; 28 | }; 29 | message.border = 0; 30 | listview.border = 0; 31 | inputbar.children = mkLiteral "[ prompt,textbox-prompt-colon,entry,num-filtered-rows,textbox-num-sep,num-rows ]"; 32 | entry.placeholder = ""; 33 | element-icon.size = mkLiteral "2em"; 34 | element-text.vertical-align = mkLiteral "0.5"; 35 | button.horizontal-align = mkLiteral "0.5"; 36 | }; 37 | 38 | cycle = true; 39 | terminal = "ghostty"; 40 | 41 | pass = { 42 | enable = true; 43 | extraConfig = '' 44 | default_do=copyPass 45 | clip=clipboard 46 | ''; 47 | }; 48 | 49 | extraConfig = { 50 | dpi = 0; # auto-detect using X screen size 51 | drun-display-format = "{name}"; 52 | sort = true; 53 | normalize-match = true; 54 | sorting-method = "fzf"; 55 | kb-mode-next = "Super+space,Control+Tab"; 56 | kb-mode-previous = "Super+Shift+space,Control+ISO_Left_Tab"; 57 | }; 58 | }; 59 | 60 | home.packages = with pkgs; [ rofimoji ]; 61 | 62 | xdg.configFile."rofimoji.rc".text = '' 63 | skin-tone = neutral 64 | max-recent = 0 65 | ''; 66 | }; 67 | } 68 | -------------------------------------------------------------------------------- /modules/station/shoot.sh: -------------------------------------------------------------------------------- 1 | shopt -s nullglob 2 | 3 | # If currently recording, stop recording 4 | 5 | pidfile=$XDG_RUNTIME_DIR/shoot.pid 6 | 7 | if { kill "$(< "$pidfile")"; } 2> /dev/null; then # redirect before the command substitution 8 | exit 9 | fi 10 | 11 | # Command line options 12 | 13 | video=0 rename=0 crop=0 upload=0 14 | while getopts :vncu o; do case $o in 15 | v) video=1;; 16 | n) rename=1;; 17 | c) crop=1;; 18 | u) upload=1;; 19 | esac done 20 | shift "$(( OPTIND - 1 ))" 21 | 22 | # Target name 23 | 24 | if (( video )); then 25 | thing=recording 26 | extension=.webm 27 | else 28 | thing=screenshot 29 | extension=.png 30 | fi 31 | 32 | if (( $# )); then 33 | target=$1 34 | elif (( rename )); then 35 | target=~/$(zenity --entry --title shoot --text "Name?") || exit 36 | else 37 | n=1 38 | printf -v date '%(%F-%H%M%S)T' -1 39 | target=~/$thing-$date 40 | while [[ -e $target$extension ]]; do 41 | target=~/$thing-$date-$(( n++ )) 42 | done 43 | fi 44 | 45 | if [[ ${target##*/} != *.* ]]; then 46 | target+=$extension 47 | fi 48 | 49 | # Crop 50 | 51 | offset= geometry= 52 | if (( crop )); then 53 | if (( video )); then 54 | slop=$(slop -f '+%x,%y %wx%h') || exit 1 55 | read -r offset geometry <<< "$slop" 56 | else 57 | geometry=$(slop) || exit 1 58 | fi 59 | fi 60 | 61 | # Shoot 62 | 63 | thumbnail_size=500 64 | thumbnail=$(mktemp --suffix .png) || exit 1 65 | trap 'rm -f "$thumbnail"' exit 66 | 67 | if (( video )); then 68 | # First encoding: lossless, fast, matroska 69 | tmpvideo=$(mktemp -p ~ --suffix .mkv) || exit 1 70 | ffmpeg -hide_banner -y \ 71 | ${geometry:+-video_size "$geometry"} \ 72 | -framerate 30 \ 73 | -f x11grab -i :0.0"$offset" \ 74 | -c:v libx264 -crf 0 -preset ultrafast \ 75 | "$tmpvideo" & 76 | echo "$!" > "$pidfile" 77 | wait 78 | rm -f "$pidfile" 79 | 80 | dunstify -t 10000 -i camera "Encoding..." "to $target" & 81 | 82 | # Second encoding: lossless, slow, webm 83 | ffmpeg -hide_banner -y \ 84 | -i "$tmpvideo" \ 85 | -c:v libvpx-vp9 -lossless 1 \ 86 | -pix_fmt yuv420p `# for compatibility with Safari...` \ 87 | "$target" && 88 | rm -f "$tmpvideo" 89 | 90 | # Thumbnail 91 | ffmpegthumbnailer -i "$target" -o "$thumbnail" -s "$thumbnail_size" 92 | else 93 | # Screenshot 94 | import -silent -window root ${geometry:+-crop "$geometry"} "$target" 95 | 96 | # Thumbnail 97 | magick "$target" -resize "${thumbnail_size}x${thumbnail_size}>" "$thumbnail" 98 | fi 99 | 100 | # Notify 101 | 102 | if [[ $(dunstify -t 10000 -A open,open -I "$thumbnail" "${thing^} saved" "as $target") == open ]]; then 103 | exec xdg-open "$target" &> /dev/null 104 | fi & 105 | 106 | # Upload 107 | 108 | if (( upload )); then 109 | exec upload -r "$target" 110 | fi & 111 | 112 | wait 113 | -------------------------------------------------------------------------------- /modules/station/sound.nix: -------------------------------------------------------------------------------- 1 | { lib, config, pkgs, ... }: with lib; { 2 | options.sound-backend = mkOption { 3 | type = types.enum [ "pulseaudio" "pipewire" ]; 4 | default = "pipewire"; 5 | }; 6 | 7 | config = mkMerge [ 8 | (mkIf (config.sound-backend == "pulseaudio") { 9 | hardware.pulseaudio = { 10 | enable = true; 11 | support32Bit = true; 12 | }; 13 | }) 14 | 15 | (mkIf (config.sound-backend == "pipewire") { 16 | services.pipewire = { 17 | enable = true; 18 | alsa.enable = true; 19 | alsa.support32Bit = true; 20 | pulse.enable = true; 21 | 22 | wireplumber.extraConfig.custom = { 23 | "wireplumber.settings" = { 24 | # https://github.com/marin-m/SongRec/issues/184 25 | "bluetooth.autoswitch-to-headset-profile" = false; 26 | }; 27 | 28 | "monitor.bluez.rules" = [ { 29 | matches = [ { "node.name" = "~bluez_output.*"; } ]; 30 | actions.update-props = { 31 | # https://wiki.archlinux.org/title/PipeWire#Noticeable_audio_delay_or_audible_pop/crack_when_starting_playback 32 | "session.suspend-timeout-seconds" = 0; 33 | }; 34 | } ]; 35 | }; 36 | }; 37 | 38 | security.rtkit.enable = true; 39 | 40 | environment.systemPackages = with pkgs; [ 41 | easyeffects 42 | ]; 43 | }) 44 | 45 | { 46 | environment.systemPackages = with pkgs; [ 47 | alsa-utils 48 | pulseaudio 49 | pavucontrol 50 | (writeShellScriptBin "volume" '' 51 | if (( ! $# )); then 52 | pactl --format=json list sinks | 53 | jq -r --arg def "$(pactl get-default-sink)" '.[] | select(.name == $def) | "\(first(.volume[]) | .value_percent | rtrimstr("%")):\(.mute)"' 54 | else case $1 in 55 | mic) 56 | pactl --format=json list sources | 57 | jq -r --arg def "$(pactl get-default-source)" '.[] | select(.name == $def) | "\(first(.volume[]) | .value_percent | rtrimstr("%")):\(.mute)"' 58 | ;; 59 | mute) pactl set-sink-mute @DEFAULT_SINK@ 1;; 60 | unmute) pactl set-sink-mute @DEFAULT_SINK@ 0;; 61 | toggle) pactl set-sink-mute @DEFAULT_SINK@ toggle;; 62 | toggle-mic) pactl set-source-mute @DEFAULT_SOURCE@ toggle;; 63 | *) pactl set-sink-volume @DEFAULT_SINK@ "$1%";; 64 | esac fi 65 | '') 66 | ]; 67 | } 68 | ]; 69 | } 70 | -------------------------------------------------------------------------------- /modules/station/thunar/dbus-gen-thumbnails.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import sys 3 | import os 4 | from getopt import getopt 5 | from pathlib import Path 6 | from hashlib import md5 7 | from xdg.BaseDirectory import xdg_cache_home 8 | from gi.repository import Gio, GLib 9 | import dbus 10 | from dbus.mainloop.glib import DBusGMainLoop 11 | 12 | sizes = ['normal', 'large'] 13 | recursive = False 14 | hidden = False 15 | delete = False 16 | dry_run = False 17 | xdev = True 18 | files = [] 19 | 20 | def get_thumbnail_path(uri, size): 21 | return f'{xdg_cache_home}/thumbnails/{size}/{md5(uri.encode()).hexdigest()}.png' 22 | 23 | def add_path(p, depth=0): 24 | if not hidden and p.name.startswith('.') and depth > 0: 25 | return 26 | if not xdev and p.is_mount() and depth > 0: 27 | return 28 | if p.is_dir(): 29 | if recursive or depth == 0: 30 | for child in p.iterdir(): 31 | add_path(child, depth+1) 32 | elif p.is_file(): 33 | files.append(p) 34 | 35 | opts, args = getopt(sys.argv[1:], ':rhdns:x') 36 | for o, arg in opts: 37 | if o == '-r': 38 | recursive = True 39 | elif o == '-h': 40 | hidden = True 41 | elif o == '-d': 42 | delete = True 43 | elif o == '-n': 44 | dry_run = True 45 | elif o == '-s': 46 | sizes = [arg] 47 | elif o == '-x': 48 | xdev = False 49 | for p in args: 50 | add_path(Path(p)) 51 | 52 | uris = [] 53 | mimes = [] 54 | 55 | for path in files: 56 | f = Gio.File.new_for_path(str(path)) 57 | uri = f.get_uri() 58 | if delete: 59 | for size in sizes: 60 | Path(get_thumbnail_path(uri, size)).unlink(missing_ok=True) 61 | if not dry_run: 62 | uris.append(uri) 63 | i = f.query_info(Gio.FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE, Gio.FileQueryInfoFlags.NONE) 64 | mimes.append(i.get_content_type()) 65 | 66 | if not dry_run: 67 | bus = dbus.SessionBus(mainloop=DBusGMainLoop()) 68 | thumbnailer = bus.get_object('org.freedesktop.thumbnails.Thumbnailer1', '/org/freedesktop/thumbnails/Thumbnailer1') 69 | interface = dbus.Interface(thumbnailer, 'org.freedesktop.thumbnails.Thumbnailer1') 70 | def on_ready(handle, uris): 71 | for uri in uris: 72 | print(f"{handles[handle]} {uri}") 73 | def on_finished(handle): 74 | del handles[handle] 75 | if not handles: 76 | loop.quit() 77 | interface.connect_to_signal('Ready', on_ready) 78 | interface.connect_to_signal('Finished', on_finished) 79 | handles = {} 80 | for size in sizes: 81 | handles[interface.Queue(uris, mimes, size, 'foreground', 0)] = size 82 | loop = GLib.MainLoop() 83 | loop.run() 84 | -------------------------------------------------------------------------------- /modules/station/thunar/default.nix: -------------------------------------------------------------------------------- 1 | { lib, config, pkgs, ... }: with lib; { 2 | services.gvfs.enable = true; 3 | services.tumbler.enable = true; 4 | programs.dconf.enable = true; 5 | programs.file-roller.enable = true; 6 | 7 | hm = { 8 | home.packages = with pkgs; let 9 | thumbnailerScript = name: script: "${writeShellScript "${name}-script" '' 10 | o=$1 s=$2 i=$3 u=$4 11 | if [[ $i != /* ]]; then 12 | i=$u 13 | [[ $i == trash://* ]] && 14 | i=file://''${XDG_DATA_HOME:-~/.local/share}/Trash/files''${i#trash://} 15 | i=''${i#file://} 16 | printf -v i %b "''${i//%/'\x'}" 17 | fi 18 | ${script} 19 | ''} %o %s %i %u"; 20 | in [ 21 | (xfce.thunar.override { 22 | thunarPlugins = with xfce; [ 23 | thunar-volman 24 | thunar-archive-plugin 25 | thunar-media-tags-plugin 26 | ]; 27 | }) 28 | xfce.xfconf 29 | xfce.exo 30 | glib 31 | zenity 32 | webp-pixbuf-loader 33 | libavif 34 | (writeShellScriptBin "closest-dir" '' 35 | if [[ -d $1 ]]; then 36 | printf '%s\n' "$1" 37 | else 38 | printf '%s\n' "''${1%/*}" 39 | fi 40 | '') 41 | (writeTextDir "share/thumbnailers/ffmpegthumbnailer.thumbnailer" '' 42 | [Thumbnailer Entry] 43 | MimeType=${concatStringsSep ";" config.lib.mimeTypes.media} 44 | Exec=${thumbnailerScript "ffmpeg" ''${ffmpegthumbnailer}/bin/ffmpegthumbnailer -i "$i" -o "$o" -s "$s" -t 30 -m''} 45 | '') 46 | ((pythonScriptWithDeps "dbus-gen-thumbnails" ./dbus-gen-thumbnails.py (ps: 47 | with ps; [ dbus-python pygobject3 pyxdg ])).overrideAttrs { 48 | nativeBuildInputs = [ wrapGAppsNoGuiHook gobject-introspection ]; 49 | }) 50 | ]; 51 | 52 | xfconf.settings.thunar = { 53 | hidden-bookmarks = [ "recent:///" ]; 54 | last-icon-view-zoom-level = "THUNAR_ZOOM_LEVEL_400_PERCENT"; 55 | last-menubar-visible = false; 56 | misc-change-window-icon = true; 57 | misc-date-style = "THUNAR_DATE_STYLE_LONG"; 58 | misc-exec-shell-scripts-by-default = true; 59 | misc-file-size-binary = true; 60 | misc-full-path-in-tab-title = true; 61 | misc-middle-click-in-tab = true; 62 | misc-show-delete-action = true; 63 | misc-single-click = false; 64 | misc-symbolic-icons-in-toolbar = false; 65 | misc-text-beside-icons = false; 66 | misc-thumbnail-draw-frames = false; 67 | misc-thumbnail-mode = "THUNAR_THUMBNAIL_MODE_ALWAYS"; 68 | shortcuts-icon-emblems = false; 69 | shortcuts-icon-size = "THUNAR_ICON_SIZE_48"; 70 | tree-icon-size = "THUNAR_ICON_SIZE_32"; 71 | }; 72 | 73 | xdg.configFile = { 74 | "Thunar/uca.xml".source = config.lib.meta.mkMutableSymlink ./uca.xml; 75 | "Thunar/accels.scm".source = config.lib.meta.mkMutableSymlink ./accels.scm; 76 | "tumbler/tumbler.rc" = { 77 | source = ./tumbler.rc; 78 | onChange = '' 79 | pkill ''${VERBOSE+-e} -f ${pkgs.xfce.tumbler} || true 80 | ''; 81 | }; 82 | }; 83 | }; 84 | } 85 | -------------------------------------------------------------------------------- /modules/station/thunar/tumbler.rc: -------------------------------------------------------------------------------- 1 | ### 2 | # Image Thumbnailers 3 | ### 4 | # Jpeg thumbnailer (from exif data if possible) 5 | [JPEGThumbnailer] 6 | Disabled=true 7 | Priority=3 8 | Locations= 9 | Excludes= 10 | MaxFileSize=209715200 11 | # Supports all type GdkPixbuf supports 12 | [PixbufThumbnailer] 13 | Disabled=false 14 | Priority=2 15 | Locations= 16 | Excludes= 17 | MaxFileSize=209715200 18 | # RAW image files using libopenraw 19 | [RawThumbnailer] 20 | Disabled=false 21 | Priority=1 22 | Locations= 23 | Excludes= 24 | MaxFileSize=209715200 25 | ### 26 | # Video Thumbnailers 27 | ### 28 | # Download cover from omdbapi.com or themoviedb.org if an 29 | # API key is given. This plugin is disabled because it 30 | # sends your (private) movie names over the internet. 31 | [CoverThumbnailer] 32 | Disabled=true 33 | Priority=3 34 | Locations=~/movies 35 | Excludes= 36 | MaxFileSize=0 37 | #APIKey=your-api-key-from-themoviedb.org 38 | # ffmpegthumbnailer plugin 39 | [FfmpegThumbnailer] 40 | Disabled=true 41 | Priority=2 42 | Locations= 43 | Excludes= 44 | MaxFileSize=2147483648 45 | # GStreamer plugin 46 | [GstThumbnailer] 47 | Disabled=true 48 | Priority=1 49 | Locations= 50 | Excludes= 51 | MaxFileSize=2147483648 52 | ### 53 | # Other Thumbnailers 54 | ### 55 | # FreeType thumbnailer 56 | [FontThumbnailer] 57 | Disabled=false 58 | Priority=1 59 | Locations= 60 | Excludes= 61 | MaxFileSize=209715200 62 | # PDF/PS thumbnailer 63 | [PopplerThumbnailer] 64 | Disabled=false 65 | Priority=1 66 | Locations= 67 | Excludes= 68 | MaxFileSize=209715200 69 | # Open document thumbnailer (ODF) 70 | [OdfThumbnailer] 71 | Disabled=false 72 | Priority=1 73 | Locations= 74 | Excludes= 75 | MaxFileSize=209715200 76 | # thumbnailers provided by .thumbnailer desktop files 77 | [DesktopThumbnailer] 78 | Disabled=false 79 | Priority=1 80 | Locations= 81 | Excludes= 82 | MaxFileSize=0 83 | # Epub thumbnailer 84 | [EpubThumbnailer] 85 | Disabled=false 86 | Priority=1 87 | Locations= 88 | Excludes= 89 | MaxFileSize=209715200 90 | -------------------------------------------------------------------------------- /modules/station/thunar/uca.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | utilities-terminal 5 | Open a terminal here 6 | 7 | 1631819606206187-1 8 | ghostty --working-directory="$(closest-dir %f)" 9 | 10 | 11 | * 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | up 21 | Upload 22 | 23 | 1536344904961360-1 24 | name=$(zenity --entry --title upload --text "Name? (leave empty for random)") && upload %f "$name" 25 | 26 | 27 | * 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | code-block 36 | Order 37 | 38 | 1546822886812187-1 39 | order "$(zenity --file-selection --directory)" %F 40 | 41 | 42 | * 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | media-play 51 | Play 52 | 53 | 1623285145974202-1 54 | music-play %F 55 | 56 | 57 | * 58 | 59 | 60 | 61 | mpv 62 | MPV shuffle 63 | 64 | 1614296264720169-1 65 | mpv --shuffle %f 66 | 67 | 68 | * 69 | 70 | 71 | 72 | wallpaper 73 | Set as wallpaper (fill) 74 | 75 | 1548521467613563-1 76 | feh --bg-fill %F 77 | 78 | 79 | * 80 | 81 | 82 | 83 | wallpaper 84 | Set as wallpaper (tile) 85 | 86 | 1548521561824976-2 87 | feh --bg-tile %F 88 | 89 | 90 | * 91 | 92 | 93 | 94 | wallpaper 95 | Set as wallpaper (center) 96 | 97 | 1731433819648815-1 98 | feh --bg-center --image-bg '#ffffff' %F 99 | 100 | * 101 | * 102 | 103 | 104 | 105 | -------------------------------------------------------------------------------- /modules/station/vscode/default.nix: -------------------------------------------------------------------------------- 1 | { lib, config, pkgs, ... }: with lib; let 2 | exts = [ 3 | { 4 | name = "white"; 5 | publisher = "arthurwhite"; 6 | sha256 = "1zybp2px5pqd0hkigvdxf9a8yb2j1fmw20sgi7h7pmsxdxpz7yzl"; 7 | version = "1.3.6"; 8 | } 9 | { 10 | name = "agda-mode"; 11 | publisher = "banacorn"; 12 | sha256 = "sha256-Lif7fvR2fozQDko0G74/+UhTnlbFjGAQj5eb2IIH61I="; 13 | version = "0.5.7"; 14 | } 15 | { 16 | name = "codercoder-dark-theme"; 17 | publisher = "CoderCoder"; 18 | sha256 = "11agag6kh7fg9x3h5pbxx4gr2frqsskkph8niln6a7i0k9cz1wlc"; 19 | version = "1.2.6"; 20 | } 21 | { 22 | name = "night-owl-light-bold"; 23 | publisher = "feego"; 24 | sha256 = "19yrbkp0jpzv0rd0c5bxzkaa2xr76wq70zc9dfl79mb05h1spl69"; 25 | version = "0.0.11"; 26 | } 27 | { 28 | name = "synthax"; 29 | publisher = "foxhoundn"; 30 | sha256 = "029p1grrhaha9q8c5s5jvanbcajisrgpj0cna2pmx1xzsk7fw61z"; 31 | version = "0.1.13"; 32 | } 33 | { 34 | name = "theme-lavender"; 35 | publisher = "gerane"; 36 | sha256 = "0zaqm8x3pgjhsjlppi7ln5l66j150cr7m9m23xgmsix1xaqms0fd"; 37 | version = "0.0.5"; 38 | } 39 | { 40 | name = "white-winter"; 41 | publisher = "jker"; 42 | sha256 = "0g0sb2q7qg53l4xlczrkxl0qdydc2l28wg0xpq9s4bv03751i1pq"; 43 | version = "1.0.1"; 44 | } 45 | { 46 | name = "vscode-theme-1984"; 47 | publisher = "juanmnl"; 48 | sha256 = "0qv1h5cp6jdmay7s17nn5drna5qzmh3jgvx5qc5qp1kdrqr957j5"; 49 | version = "0.3.4"; 50 | } 51 | { 52 | name = "vscode-theme-mr-pink"; 53 | publisher = "juanmnl"; 54 | sha256 = "1i5wh0znhb7shr2lwfby14arlhnqa5l37ncbp2xn8ws45jwm1cvv"; 55 | version = "1.0.1"; 56 | } 57 | { 58 | name = "vscoq"; 59 | publisher = "maximedenes"; 60 | sha256 = "04m1dby6zfzg5nahnricjax28g47mgnja7d9cbll270i7ahnyncm"; 61 | version = "2.2.1"; 62 | } 63 | { 64 | name = "vscode-duotone-dark"; 65 | publisher = "sallar"; 66 | sha256 = "1d7s49j2m4ga590iqnhb0ayafrz9f9lkl3warp8a1898767a1wrq"; 67 | version = "0.3.3"; 68 | } 69 | { 70 | name = "lilac"; 71 | publisher = "shubham-saudolla"; 72 | sha256 = "0bwwhvmm6k3rdlh17cafi9fqhy171j4dwbnipi9gy66s2z0kpz5h"; 73 | version = "1.3.0"; 74 | } 75 | ]; 76 | in { 77 | hm = { 78 | programs.vscode = { 79 | enable = true; 80 | package = pkgs.vscodium; 81 | profiles.default.extensions = with pkgs.vscode-extensions; [ 82 | vscodevim.vim 83 | xadillax.viml 84 | jnoortheen.nix-ide 85 | haskell.haskell 86 | justusadam.language-haskell 87 | elmtooling.elm-ls-vscode 88 | dhall.dhall-lang 89 | eugleo.magic-racket 90 | rust-lang.rust-analyzer 91 | yzhang.markdown-all-in-one 92 | james-yu.latex-workshop 93 | bungcip.better-toml 94 | /*(pkgs.vscode-utils.buildVscodeExtension { 95 | pname = "agda-mode-vscode"; 96 | version = "0.5.1-unstable"; 97 | vscodeExtPublisher = "banacorn"; 98 | vscodeExtName = "agda-mode"; 99 | vscodeExtUniqueId = "banacorn.agda-mode"; 100 | src = pkgs.buildNpmPackage { 101 | name = "agda-mode-vscode.zip"; 102 | src = pkgs.fetchFromGitHub { 103 | owner = my.githubUsername; 104 | repo = "agda-mode-vscode"; 105 | rev = "3b25c9def0d145b0f7aa918bd7f9837d256c2486"; 106 | hash = "sha256-1cHYjWzvnebKnL6zy0gZqcD6q8dLtyYlU1XqVNy8Eiw="; 107 | }; 108 | npmDepsHash = "sha256-q+d4Rof49Xi4PdYaPYUgzSox1M88ReEt6cvCr93KaA4="; 109 | makeCacheWritable = true; 110 | nativeBuildInputs = [ pkgs.vsce ]; 111 | installPhase = '' 112 | vsce package -o "$out" 113 | ''; 114 | }; 115 | })*/ 116 | ] ++ pkgs.vscode-utils.extensionsFromVscodeMarketplace exts; 117 | }; 118 | 119 | xdg.configFile = { 120 | "VSCodium/User/settings.json".source = config.lib.meta.mkMutableSymlink ./settings.json; 121 | "VSCodium/User/keybindings.json".source = config.lib.meta.mkMutableSymlink ./keybindings.json; 122 | }; 123 | 124 | home.packages = [ 125 | pkgs.coqPackages.vscoq-language-server 126 | (pkgs.writeShellScriptBin "update-vscode-extensions" '' 127 | ${pkgs.path}/pkgs/applications/editors/vscode/extensions/update_installed_exts.sh | 128 | nix eval -f - --apply ${escapeShellArg '' 129 | { extensions, ... }: 130 | let 131 | old = builtins.fromJSON (builtins.readFile ${builtins.toFile "exts.json" (builtins.toJSON exts)}); 132 | new = builtins.listToAttrs (map (e: { inherit (e) name; value = e; }) extensions); 133 | in 134 | map (e: e // { inherit (new.''${e.name}) version sha256; }) old 135 | ''} | 136 | ${pkgs.nixfmt-rfc-style}/bin/nixfmt 137 | '') 138 | ]; 139 | }; 140 | } 141 | -------------------------------------------------------------------------------- /modules/station/vscode/keybindings.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "key": "ctrl+t", 4 | "command": "workbench.action.terminal.toggleTerminal", 5 | "when": "terminal.active" 6 | }, 7 | { 8 | "key": "ctrl+`", 9 | "command": "-workbench.action.terminal.toggleTerminal", 10 | "when": "terminal.active" 11 | }, 12 | { 13 | "key": "shift+tab", 14 | "command": "editor.action.outdentLines", 15 | "when": "editorTextFocus && !editorReadonly && vim.mode != 'Insert'" 16 | }, 17 | { 18 | "key": "ctrl+[", 19 | "command": "-editor.action.outdentLines", 20 | "when": "editorTextFocus && !editorReadonly" 21 | }, 22 | { 23 | "key": "tab", 24 | "command": "editor.action.indentLines", 25 | "when": "editorTextFocus && !editorReadonly && vim.mode != 'Insert'" 26 | }, 27 | { 28 | "key": "ctrl+]", 29 | "command": "-editor.action.indentLines", 30 | "when": "editorTextFocus && !editorReadonly" 31 | }, 32 | { 33 | "key": "shift+`", 34 | "command": "editor.action.jumpToBracket", 35 | "when": "editorTextFocus && vim.mode == 'Normal'" 36 | }, 37 | { 38 | "key": "ctrl+shift+\\", 39 | "command": "-editor.action.jumpToBracket", 40 | "when": "editorTextFocus" 41 | }, 42 | { 43 | "key": "shift+`", 44 | "command": "editor.action.selectToBracket", 45 | "when": "vim.mode == 'Visual'" 46 | }, 47 | { 48 | "key": "ctrl+b", 49 | "command": "-markdown.extension.editing.toggleBold", 50 | "when": "editorTextFocus && !editorReadonly && editorLangId == 'markdown'" 51 | }, 52 | { 53 | "key": "ctrl+q", 54 | "command": "workbench.action.closeWindow" 55 | }, 56 | { 57 | "key": "ctrl+shift+w", 58 | "command": "-workbench.action.closeWindow" 59 | }, 60 | { 61 | "key": "ctrl+tab", 62 | "command": "workbench.action.nextEditorInGroup" 63 | }, 64 | { 65 | "key": "ctrl+k ctrl+pagedown", 66 | "command": "-workbench.action.nextEditorInGroup" 67 | }, 68 | { 69 | "key": "ctrl+shift+tab", 70 | "command": "workbench.action.previousEditorInGroup" 71 | }, 72 | { 73 | "key": "ctrl+k ctrl+pageup", 74 | "command": "-workbench.action.previousEditorInGroup" 75 | }, 76 | { 77 | "key": "ctrl+o", 78 | "command": "-workbench.action.files.openFileFolder", 79 | "when": "isMacNative && openFolderWorkspaceSupport" 80 | }, 81 | { 82 | "key": "ctrl+o", 83 | "command": "-workbench.action.files.openFile", 84 | "when": "true" 85 | }, 86 | { 87 | "key": "ctrl+6", 88 | "command": "workbench.action.zoomOut" 89 | }, 90 | { 91 | "key": "ctrl+-", 92 | "command": "-workbench.action.zoomOut" 93 | }, 94 | { 95 | "key": "ctrl+shift+0", 96 | "command": "workbench.action.zoomReset" 97 | }, 98 | { 99 | "key": "ctrl+numpad0", 100 | "command": "-workbench.action.zoomReset" 101 | }, 102 | { 103 | "key": "ctrl+=", 104 | "command": "-workbench.action.zoomIn" 105 | }, 106 | { 107 | "key": "alt+left", 108 | "command": "workbench.action.navigateBack", 109 | "when": "canNavigateBack" 110 | }, 111 | { 112 | "key": "ctrl+alt+-", 113 | "command": "-workbench.action.navigateBack", 114 | "when": "canNavigateBack" 115 | }, 116 | { 117 | "key": "alt+right", 118 | "command": "workbench.action.navigateForward", 119 | "when": "canNavigateForward" 120 | }, 121 | { 122 | "key": "ctrl+shift+-", 123 | "command": "-workbench.action.navigateForward", 124 | "when": "canNavigateForward" 125 | }, 126 | { 127 | "key": "ctrl+8", 128 | "command": "agda-mode.input-symbol[Activate]", 129 | "when": "config.agdaMode.inputMethod.enabled && editorTextFocus && variableLanguage && !agdaModeTyping && editorLangId =~ /.*(l?agda(-(markdown|typst|latex|tex|rst|org|forester))?).*/" 130 | }, 131 | { 132 | "key": "\\", 133 | "command": "-agda-mode.input-symbol[Activate]", 134 | "when": "config.agdaMode.inputMethod.enabled && editorTextFocus && variableLanguage && !agdaModeTyping && editorLangId =~ /.*(l?agda(-(markdown|typst|latex|tex|rst|org|forester))?).*/" 135 | }, 136 | { 137 | "key": "ctrl+8", 138 | "command": "agda-mode.input-symbol[Activate]", 139 | "when": "config.agdaMode.inputMethod.enabled && editorTextFocus && !agdaModeTyping && editorLangId =~ /.*(l?agda(-(markdown|typst|latex|tex|rst|org|forester))?).*/" 140 | }, 141 | { 142 | "key": "\\", 143 | "command": "-agda-mode.input-symbol[Activate]", 144 | "when": "config.agdaMode.inputMethod.enabled && editorTextFocus && !agdaModeTyping && editorLangId =~ /.*(l?agda(-(markdown|typst|latex|tex|rst|org|forester))?).*/" 145 | } 146 | ] 147 | -------------------------------------------------------------------------------- /modules/station/x.nix: -------------------------------------------------------------------------------- 1 | { lib, config, pkgs, ... }: with lib; { 2 | services.xserver = { 3 | enable = true; 4 | autoRepeatDelay = 250; 5 | dpi = mkDefault 96; 6 | enableTearFree = true; 7 | }; 8 | 9 | services.libinput = { 10 | enable = true; 11 | mouse.accelSpeed = "0.5"; 12 | touchpad = { 13 | accelSpeed = "0.5"; 14 | tapping = false; 15 | }; 16 | }; 17 | 18 | lib.shellEnv = { 19 | inherit (config.services.xserver) dpi; 20 | }; 21 | 22 | services.autorandr.enable = true; 23 | hm.xdg.configFile."autorandr/settings.ini".text = '' 24 | [config] 25 | skip-options=gamma 26 | ''; 27 | 28 | my-programs.bspwm.enable = mkDefault true; 29 | 30 | hm = { 31 | home.keyboard = with config.services.xserver.xkb; { 32 | inherit layout variant; 33 | options = splitString "," options; 34 | }; 35 | 36 | home.pointerCursor = { 37 | package = pkgs.adwaita-icon-theme; 38 | name = "Adwaita"; 39 | size = 16; 40 | x11.enable = true; 41 | }; 42 | 43 | home.packages = with pkgs; [ 44 | xorg.xev 45 | arandr 46 | xdotool 47 | ]; 48 | 49 | xresources.properties = with config.theme; { 50 | "*color0" = black; 51 | "*color1" = hot; 52 | "*color2" = cold; 53 | "*color3" = hot; 54 | "*color4" = cold; 55 | "*color5" = hot; 56 | "*color6" = cold; 57 | "*color7" = foregroundAlt; 58 | "*color8" = backgroundAlt; 59 | "*color9" = hot; 60 | "*color10" = cold; 61 | "*color11" = hot; 62 | "*color12" = cold; 63 | "*color13" = hot; 64 | "*color14" = cold; 65 | "*color15" = white; 66 | "*background" = background; 67 | "*foreground" = foreground; 68 | "*cursorColor" = foreground; 69 | }; 70 | 71 | services.picom = { 72 | enable = mkDefault true; 73 | backend = mkDefault "glx"; 74 | vSync = ! config.services.xserver.enableTearFree; 75 | 76 | settings.unredir-if-possible = true; # reduces lag in fullscreen games 77 | 78 | # workaround for https://github.com/yshui/picom/issues/16#issuecomment-792739119 79 | fade = true; 80 | fadeSteps = [ 1 1 ]; 81 | fadeDelta = 30; 82 | }; 83 | 84 | services.redshift = { 85 | enable = true; 86 | latitude = 48.0; 87 | longitude = 2.0; 88 | }; 89 | systemd.user.services.redshift.Service.ExecStop = 90 | let terminate = "${pkgs.util-linux}/bin/kill $MAINPID"; 91 | in "${terminate} ; ${terminate}"; # don't wait for the fade-out 92 | }; 93 | } 94 | -------------------------------------------------------------------------------- /modules/syncthing.nix: -------------------------------------------------------------------------------- 1 | { lib, this, config, ... }: with lib; let 2 | devices = my.machinesWith "syncthing"; 3 | enable = this ? wireguard && this ? syncthing; 4 | in { 5 | imports = [ 6 | (mkAliasOptionModule [ "synced" ] [ "services" "syncthing" "settings" "folders" ]) 7 | ]; 8 | 9 | lib.shellEnv.synced = mapAttrs (_: v: v.path) config.synced; 10 | 11 | services.syncthing = { 12 | inherit enable; 13 | user = my.username; 14 | inherit (config.my) group; 15 | dataDir = config.my.home; 16 | overrideDevices = true; 17 | overrideFolders = true; 18 | 19 | guiAddress = "${this.wireguard.ipv4}:8384"; 20 | openDefaultPorts = true; 21 | 22 | key = mkIf (this ? syncthing) config.secrets.syncthing.path; 23 | 24 | settings = { 25 | gui = { 26 | theme = "default"; 27 | insecureAdminAccess = true; 28 | }; 29 | options.urAccepted = -1; 30 | 31 | devices = mapAttrs (_: m: { 32 | inherit (m.syncthing) id; 33 | introducer = m.hostname == my.server.hostname; 34 | }) devices; 35 | 36 | folders = let 37 | trashcan = { 38 | type = "trashcan"; 39 | params.cleanoutDays = "0"; 40 | }; 41 | simple = { 42 | type = "simple"; 43 | params = { 44 | keep = "5"; 45 | cleanoutDays = "0"; 46 | }; 47 | }; 48 | allDevices = attrNames devices; 49 | allDevicesExceptPhone = attrNames (filterAttrs (_: m: !m.isPhone) devices); 50 | in mapAttrs (_: f: { 51 | enable = builtins.elem this.hostname f.devices; 52 | } // f) { 53 | my = { 54 | path = "${config.my.home}/sync/my"; 55 | devices = allDevices; 56 | versioning = simple; 57 | }; 58 | pictures = { 59 | path = "${config.my.home}/sync/pictures"; 60 | devices = allDevices; 61 | versioning = trashcan; 62 | }; 63 | music = { 64 | path = "${config.my.home}/sync/music"; 65 | devices = allDevices; 66 | versioning = trashcan; 67 | }; 68 | camera = { 69 | path = "${config.my.home}/sync/camera"; 70 | devices = allDevices; 71 | versioning = trashcan; 72 | }; 73 | saves = { 74 | path = "${config.my.home}/sync/saves"; 75 | devices = allDevicesExceptPhone; 76 | fsWatcherEnabled = false; 77 | versioning = trashcan; 78 | }; 79 | irc-logs = { 80 | path = "${config.my.home}/sync/irc-logs"; 81 | type = if config.my-services.weechat.enable or false then "sendonly" else "receiveonly"; 82 | devices = allDevicesExceptPhone; 83 | fsWatcherEnabled = false; 84 | versioning = trashcan; 85 | }; 86 | uploads = { 87 | path = "${config.my.home}/sync/uploads"; 88 | devices = allDevicesExceptPhone; 89 | versioning = trashcan; 90 | }; 91 | password-store = { 92 | path = "${config.my.home}/sync/password-store"; 93 | devices = allDevices; 94 | versioning = simple; 95 | }; 96 | firefox = { 97 | path = "${config.my.home}/${config.hm.programs.firefox.configPath}/default"; 98 | type = if config.hm.programs.firefox.enable then "sendonly" else "receiveonly"; 99 | devices = [ my.server.hostname "no" ]; 100 | versioning = simple; 101 | maxConflicts = 0; 102 | }; 103 | mail = { 104 | path = if this.isServer 105 | then config.mailserver.mailDirectory 106 | else "${config.my.home}/sync/mail"; 107 | type = if config.mailserver.enable or false then "sendonly" else "receiveonly"; 108 | devices = allDevicesExceptPhone; 109 | fsWatcherEnabled = false; 110 | versioning = simple; 111 | }; 112 | }; 113 | }; 114 | }; 115 | 116 | systemd.services.syncthing = { 117 | after = [ "home-manager-${my.username}.service" ]; # ensure ~/.config is created 118 | environment.STNODEFAULTFOLDER = "yes"; 119 | serviceConfig.StartLimitIntervalSec = "1min"; 120 | serviceConfig.StartLimitBurst = 5; 121 | }; 122 | 123 | environment.systemPackages = [ config.services.syncthing.package ]; 124 | 125 | hm.home.file = { 126 | "${config.synced.my.path}/.stignore".text = '' 127 | .git 128 | ''; 129 | "${config.synced.saves.path}/.stignore".text = '' 130 | /df/current 131 | ''; 132 | "${config.synced.uploads.path}/.stignore".text = '' 133 | /local 134 | ''; 135 | "${config.synced.firefox.path}/.stignore".text = '' 136 | storage 137 | ''; 138 | }; 139 | } 140 | -------------------------------------------------------------------------------- /modules/systemd.nix: -------------------------------------------------------------------------------- 1 | { 2 | systemd.extraConfig = '' 3 | DefaultTimeoutStartSec=30s 4 | DefaultTimeoutStopSec=15s 5 | ''; 6 | systemd.user.extraConfig = '' 7 | DefaultTimeoutStartSec=30s 8 | DefaultTimeoutStopSec=15s 9 | ''; 10 | } 11 | -------------------------------------------------------------------------------- /modules/theme.nix: -------------------------------------------------------------------------------- 1 | { lib, config, ... }: with lib; { 2 | options.theme = mkOption { 3 | type = with types; submodule { 4 | freeformType = attrs; 5 | options.dark = mkOption { 6 | type = bool; 7 | default = true; 8 | }; 9 | }; 10 | }; 11 | 12 | config = { 13 | theme = with config.theme; { 14 | black = "#000000"; 15 | darkGrey = if dark then "#666666" else "#aaaaaa"; 16 | lightGrey = if dark then "#444444" else "#cccccc"; 17 | white = "#ffffff"; 18 | hot = "#d13cff"; 19 | cold = if dark then "#4befdb" else "#33aacc"; 20 | background = if dark then black else white; 21 | foreground = if dark then white else black; 22 | backgroundAlt = darkGrey; 23 | foregroundAlt = lightGrey; 24 | borderWidth = 0; 25 | borderColor = foreground; 26 | padding = 16; 27 | barHeight = if config.services.xserver.dpi != null && config.services.xserver.dpi > 100 then 32 else 28; 28 | gtkTheme = "Orchis-Purple" + (if dark then "-Dark" else ""); 29 | iconTheme = "Tela-dracula" + (if dark then "-dark" else ""); 30 | gtkFont = "sans-serif"; 31 | font = "bitmap"; 32 | fontSize = 8; 33 | pangoFont = "${font} ${toString fontSize}"; 34 | trayWidth = 3 * barHeight; 35 | }; 36 | 37 | lib.shellEnv.theme = config.theme; 38 | }; 39 | } 40 | -------------------------------------------------------------------------------- /modules/tmux.nix: -------------------------------------------------------------------------------- 1 | { config, pkgs, ... }: { 2 | nixpkgs.overlays = [ (self: super: { 3 | tmux = super.tmux.overrideAttrs (old: { 4 | patches = old.patches or [] ++ [ 5 | # https://github.com/tmux/tmux/issues/3923 6 | (builtins.toFile "tmux.patch" '' 7 | diff --git a/screen-write.c b/screen-write.c 8 | index 6892d041..1174cb15 100644 9 | --- a/screen-write.c 10 | +++ b/screen-write.c 11 | @@ -2088,7 +2088,7 @@ screen_write_combine(struct screen_write_ctx *ctx, const struct grid_cell *gc) 12 | if (utf8_is_zwj(ud)) 13 | zero_width = 1; 14 | else if (utf8_is_vs(ud)) 15 | - zero_width = force_wide = 1; 16 | + zero_width = 1; 17 | else if (ud->width == 0) 18 | zero_width = 1; 19 | 20 | '') 21 | ]; 22 | }); 23 | }) ]; 24 | 25 | hm.programs.tmux = { 26 | enable = true; 27 | 28 | # Having the socket in /tmp is fine; piping TMUX_TMPDIR to the weechat service is annoying. 29 | secureSocket = false; 30 | 31 | shortcut = "a"; 32 | terminal = "tmux-256color"; 33 | escapeTime = 100; 34 | baseIndex = 1; 35 | clock24 = true; 36 | sensibleOnTop = false; 37 | 38 | extraConfig = '' 39 | set -g history-limit 100000 40 | set -g mouse on 41 | set -g renumber-windows on 42 | set -g set-clipboard on 43 | set -g set-titles on 44 | set -g set-titles-string '#T' 45 | set -g status-left "" 46 | set -g status-right "#S" 47 | set -g status-style "" 48 | set -ga terminal-overrides ",xterm-256color:Ms=\\E]52;c;%p2%s\\7" # get mosh to behave https://gist.github.com/yudai/95b20e3da66df1b066531997f982b57b 49 | set -g window-status-current-format "#W" 50 | set -g window-status-current-style "bold fg=terminal" 51 | set -g window-status-format "#W" 52 | set -g window-status-separator " " 53 | 54 | set-hook -g client-active attach 55 | 56 | bind r source $XDG_DATA_HOME/tmux/tmux.conf 57 | bind -n C-q detach 58 | bind -n C-Left previous-window 59 | bind -n C-Right next-window 60 | bind -n WheelUpPane if -t = -F '#{==:#{pane_current_command},info}' 'send -N 2 Up' 'if -t = -F "#{||:#{mouse_any_flag},#{pane_in_mode}}" "send -M" "copy-mode -e -t ="' 61 | bind -n WheelDownPane if -t = -F '#{==:#{pane_current_command},info}' 'send -N 2 Down' 'send -M' 62 | ''; 63 | }; 64 | } 65 | -------------------------------------------------------------------------------- /modules/users.nix: -------------------------------------------------------------------------------- 1 | { inputs, lib, config, utils, ... }: with lib; { 2 | imports = [ (mkAliasOptionModule [ "my" ] [ "users" "users" my.username ]) ]; 3 | 4 | options.users.users = mkOption { 5 | type = with types; attrsOf (submodule ({ config, ... }: { 6 | options.shellPath = mkOption { 7 | type = str; 8 | default = utils.toShellPath config.shell; 9 | defaultText = literalExpression "utils.toShellPath shell"; 10 | readOnly = true; 11 | }; 12 | })); 13 | }; 14 | 15 | config = { 16 | users = { 17 | users = { 18 | ${my.username} = { 19 | uid = 1000; 20 | isNormalUser = true; 21 | description = my.realName; 22 | extraGroups = [ "wheel" ]; 23 | openssh.authorizedKeys.keys = my.sshKeys; 24 | }; 25 | 26 | root = { 27 | inherit (config.my) hashedPassword; 28 | openssh.authorizedKeys.keys = config.my.openssh.authorizedKeys.keys; 29 | }; 30 | }; 31 | }; 32 | 33 | hm.home.file.".hushlogin".text = ""; 34 | 35 | security = { 36 | sudo = { 37 | wheelNeedsPassword = false; 38 | extraConfig = '' 39 | Defaults env_keep+="EDITOR" 40 | Defaults env_keep+="SSH_CONNECTION SSH_CLIENT SSH_TTY" 41 | ''; 42 | }; 43 | 44 | polkit.extraConfig = '' 45 | polkit.addRule(function (action, subject) { 46 | if (subject.isInGroup('wheel')) 47 | return polkit.Result.YES; 48 | }); 49 | ''; 50 | }; 51 | 52 | boot.kernel.sysctl."kernel.dmesg_restrict" = false; 53 | }; 54 | } 55 | -------------------------------------------------------------------------------- /modules/vim/default.nix: -------------------------------------------------------------------------------- 1 | { lib, pkgs, ... }: with lib; { 2 | environment.variables.EDITOR = "vim"; 3 | 4 | hm = { 5 | programs.neovim = { 6 | enable = true; 7 | vimAlias = true; 8 | viAlias = true; 9 | extraConfig = readFile ./init.vim; 10 | plugins = with pkgs.vimPlugins; [ 11 | ctrlp 12 | nvim-lastplace 13 | editorconfig-vim 14 | vim-sleuth 15 | nvim-lspconfig 16 | nerdtree 17 | nerdcommenter 18 | vim-surround 19 | vim-easy-align 20 | vim-nix 21 | vim-nixhash 22 | vim-markdown 23 | haskell-vim 24 | agda-vim 25 | coq-vim 26 | kotlin-vim 27 | colorbuddy-nvim 28 | # more minimalist themes: https://github.com/mcchrish/vim-no-color-collections 29 | ]; 30 | }; 31 | 32 | editorconfig = { 33 | enable = true; 34 | settings."*" = { 35 | trim_trailing_whitespace = true; 36 | insert_final_newline = true; 37 | }; 38 | }; 39 | }; 40 | } 41 | -------------------------------------------------------------------------------- /secrets/bothendieck: -------------------------------------------------------------------------------- 1 | { 2 | "data": "ENC[AES256_GCM,data:vVLu35zbI6XVr09JkeT8oYBTWRNxkmBbYqUhpFeCfRVxcAYWF6NqXj7bHETfe/iLDK+mGkF8sn9iPExq0ya3Qjnd/Cx06Aan0Tq5YkGonMwrddNpG1LaHi1XC5L9FeP1qOjpZ4zKmPXnnY5JTBiBVuyji5tY0dtiPp2B84XKbpJW2jvlj7Z3WQ==,iv:g9nTxIomAaW/x8gc8xSxiOk3wMxtWUSoIZ8ByH0u2s4=,tag:YAcZw/Jh8gYjUAzfiXNjVA==,type:str]", 3 | "sops": { 4 | "kms": null, 5 | "gcp_kms": null, 6 | "azure_kv": null, 7 | "hc_vault": null, 8 | "age": [ 9 | { 10 | "recipient": "age1nklm02ejf57n0x208mjrww9s8np44kkmwfl7wdn78dtmh7x2g5wqh987h9", 11 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB5L3ZneDVTRWsvdXJ3WjZG\nOFNBU0g4MkdFeWcvY2Flem0vNWVmdDg3RlFVCnlqczgvMTArQlF6MlN6bjVPSm1n\naEZyY3hYa3N5Z0dVN2JabXJPNVhEZ28KLS0tIHlUMzU1ZVcrcHZ2UFU5YStGTnpZ\nOVFKREVoMzFlcFBnTDA5dXVCMnJIaW8KyWhzRLFRugVwP06mpKK4zK4flQOV0apO\nuOEIbdLnLBJGq9X56HqZrNylxB/1Qe6xhV5gI70fdzW9UAZBj7DGfw==\n-----END AGE ENCRYPTED FILE-----\n" 12 | } 13 | ], 14 | "lastmodified": "2025-01-01T12:52:58Z", 15 | "mac": "ENC[AES256_GCM,data:5nvdgtrnOx/pl3ZFwHppnIm1NoO48XOl60iPKrkEA6dDUbe/bBnycp1eZQi9ThyOPuW60ELQ8gSJ+r8HvdiVLKM0ZCeYujIo2xd1RRYDRldD5kVwMdWqec0HAB0mfKdwbiv1xINqaORMSyPQYr8p9tQhWD5RLghWlcz464J8S7o=,iv:j8qg6Qe9XbHxxXEt1/+HxXrYAG/F7mGDh9JUuo1qGKE=,tag:+DwbnN/648M096bjaWgSxQ==,type:str]", 16 | "pgp": null, 17 | "unencrypted_suffix": "_unencrypted", 18 | "version": "3.9.2" 19 | } 20 | } -------------------------------------------------------------------------------- /secrets/cachix: -------------------------------------------------------------------------------- 1 | { 2 | "data": "ENC[AES256_GCM,data:F7QdwwWfRc/4zXFzuS/rCYi52d89owqmlkIrsisCgi+Zg5DD5Mhkz8A+0rNRMSfUPPOuXDdeQhYBAt4ZNFX6gIX6/+BDeuw4O1UhSXhEY1IvI9LZ8ruVueZX820OP2mSpxwlyJUBMNBOKfVEhguQinrGrATY1uhZ/OyuAgjLERe+2L/vWWU4h8FExPy+CUGpAZe+40qDNB5nI2rBHkLnQPihjXUReMxoIv+N1yNwhaklwKfR8zR7GEWjcIirXsNYFpTl4WRF2byQUm3v2SJ7KaM2O74mNVL735qYfXUAno7DKf8XWrMMpjkm4yeuKhtbXijmjlWdpm0=,iv:QfBVOYadzey8JY/+P+EOg5xkJtnmlSbkpGMQnbnEgu0=,tag:m9Na/Z4AL0njqk8mH0gHWg==,type:str]", 3 | "sops": { 4 | "kms": null, 5 | "gcp_kms": null, 6 | "azure_kv": null, 7 | "hc_vault": null, 8 | "age": [ 9 | { 10 | "recipient": "age1nklm02ejf57n0x208mjrww9s8np44kkmwfl7wdn78dtmh7x2g5wqh987h9", 11 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBMYWN4djBjbHByWVB3cVE4\nc3ozUi9JaFJBbW1LVC9uTXF0MzBtUVBFZ2w4Cnl2ek1IUk9TTjV5cGR4ejFibVpl\nK3FVL09GODc5MmN5TVdVMHNVWkxBSFkKLS0tIGNhcjlmbGhNYm9WYmFIdmRKWWpk\nWHN3YlBlVlpmRHVCQm9oTE5JKzRYc1UKAubx2Ku/gLJHbxFPlcXafMIqGOpjorCk\neOLNaudncvpoWKH92p7bEtB0kw2vpOAkeHG8hyyN6zd/Al/lyKIwtw==\n-----END AGE ENCRYPTED FILE-----\n" 12 | } 13 | ], 14 | "lastmodified": "2025-01-01T12:52:58Z", 15 | "mac": "ENC[AES256_GCM,data:dxiF/zqReAF8eq847cIWTJSZe68iKpZ1noGLwMuNMLjfuWI/9TmTSdQBA5ZLCPA+zYQxRZCSA5QSMbOlTigTOkUxmxMTdajbwQHspQfa0W9OelllLA1sj+PRsuxnxTxQDJi500pMVr+Is4cgu2Eg6VVyYHAaDt4WyGCw0iT4FcQ=,iv:z00WVegM0ma0AVV+Xl3m2wl/vnfnXvgCCBctRSZv3hQ=,tag:2E7bOkSlFnd3eu7f/UJ3DQ==,type:str]", 16 | "pgp": null, 17 | "unencrypted_suffix": "_unencrypted", 18 | "version": "3.9.2" 19 | } 20 | } -------------------------------------------------------------------------------- /secrets/dkim: -------------------------------------------------------------------------------- 1 | { 2 | "data": "ENC[AES256_GCM,data:Wl/87RwPFzDsndKuArAXkmZG2MNlfzck8GQEx9bbXa+K8vkTyDpLuIFgwn2zAKBqmXJFTI1AIZ3q6Oh8JF7rIkCHR/IFu2DJ4RzB8SUZjQ1XEowj17lOVmbyhfFihSRwjLvalHLawDKERTM1rtSC8QpCcvP2tMj9xopMgqFSgojKs4AkX+IcvkylxS578yzfkhLAtpzScwq1viKHRR4c/DTnu5QdsPcN0OZXCV0vdHPZr9ozvpgQdSzPqpCjZbAwBV3e7nR1aB+cO64gbFpdCgjz4g73n0uSOoTxh+P7NmN2FURw/ANKYr+0OUGR1pD+nod5rNtnTMSs5GJ/gqkR0/dLPG1NV2It8kEv9kIe48K0rqZduaVUxBj5CIy0HiLNlWntzJN89YqvPRrGGB8BO25LlvueTWYbawrABB4cjib8ClvTguyfdvQdX7E2T1SK18tA5zj2vpt20nTRCh0h4qrGwDPC7EaqQqOyBy7b8JkXhbbSq2D06oFwGTXNAoBGVyKnyE9BGsduQnqC5dAxaY+aMLXRSM7BrwAp695KkE7QpmDbQvqgbxC30NC7oSByq03fhBb0WpDQlqnQxEvzhoZBB0RB5QDYedlvwbzFfpRDeoYXm7Slc0Haoen/IszjPPX2oljwPCv7gvqjLFZmqIXwAEAGGFRdzJNHxqFFRsf6GJK42a7tODMtj7t7NpDtbl8e3jTY6/Fac2OM+xdGdnb2fn5WxBncsHLLI3+wS2Xz/ux+mRA2FPT/z+8WInbcgZj4UgH0RE+eE96xkI1cgNHW9TpWi3TvxG5Mta9mb27F5cxpLmGodCJ8sZIQ9+lS/Qie+MKBB/XGYqHab38ZsyF2k/zH7hccyq/ToMr0PrVrVnluYvLdQk98SSvahOIANag1dWn/Dtp7PslJR94VbSGEVZB4wizbxJ1E3UpR9lqxdfn82hSJAq+yvsj+sKmep9ziVdHtD3dwYjR/HNC6CtFuvhQVH2L6dfEkXMxwmNbxmyDru54tJ3Hf5L67btVdJiZmd0pexYWabg1T1qZQpDytuIZs8vmC7jFqhl7mRMX7ullCersqS6MOeWoffqZ86DtswOKGbdJsiD7YL7JAu9keXNkHLkm52u3GWFzsQbsnQtgA70W2PA4533XUSSlyUBE2BaADkHbJoA6HOFXRNGoYNA12OJo=,iv:rjw+ITMW+f6iAj/17EhjiHNpBvmz5PFBRTKsivLkaQU=,tag:8vLQ4Xim6VOuUAj4qvU00Q==,type:str]", 3 | "sops": { 4 | "kms": null, 5 | "gcp_kms": null, 6 | "azure_kv": null, 7 | "hc_vault": null, 8 | "age": [ 9 | { 10 | "recipient": "age1nklm02ejf57n0x208mjrww9s8np44kkmwfl7wdn78dtmh7x2g5wqh987h9", 11 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA5WEJXdHNQQXgzR012ZWlO\nTm4rdEhBR2hXRlRNajIvbTdEMllOQnlpN2lzCko1czUwYzJ4VW1ITDZTSEZYVmtP\nYWxQN2txRlJGczZUOVF4QkovQmVBZmcKLS0tIEdMb0ZtcFhTQWZtUDFmNG00QXYv\nUC83RUpLNktnTlBpVDQzOGROblAvbXMKmbyU/cUR98fh3ByBs3YzTGjb2Fe8oogo\nV3olpLs29fTYx22wvxV+Bk/tRerp9Tq+ik6nbCzgr902D8GUvfCqDQ==\n-----END AGE ENCRYPTED FILE-----\n" 12 | } 13 | ], 14 | "lastmodified": "2025-01-01T12:52:58Z", 15 | "mac": "ENC[AES256_GCM,data:2s3epTz9OiWygUwZPryvwF9nzDJkjIEENcYUOpGBLFcrCAeIztZCmJW65Sjb2zDoBLNZ85GVE9jL67VRPUKU8hEJAeTlIHPzNWt0jFhQ5Ngq3fqCVDLju9dN/g/iDh44z9/NmLEp+TuDT/2++oThRZQkvOXvuELGdmqDM0fpPeM=,iv:hwMdwnM4rKLPTVP/YB66Nz2UW3d/VeTi/sJs01g9skk=,tag:5v9YCpr0el1UFURg9tSY+g==,type:str]", 16 | "pgp": null, 17 | "unencrypted_suffix": "_unencrypted", 18 | "version": "3.9.2" 19 | } 20 | } -------------------------------------------------------------------------------- /secrets/lambdabot-ulminfo: -------------------------------------------------------------------------------- 1 | { 2 | "data": "ENC[AES256_GCM,data:vh2858d1RSkqWra3o+VuMH3fjwEkE2G1/dxg9AwUkDWgUDNx/gAeU1XF6vS9a2o=,iv:dmFSLPU/4U0ZI3lzBR8MT1h3bA0AbWU6bZYoZ2lHm1g=,tag:8XRimDENtT+MmW8jJU+1qw==,type:str]", 3 | "sops": { 4 | "kms": null, 5 | "gcp_kms": null, 6 | "azure_kv": null, 7 | "hc_vault": null, 8 | "age": [ 9 | { 10 | "recipient": "age1nklm02ejf57n0x208mjrww9s8np44kkmwfl7wdn78dtmh7x2g5wqh987h9", 11 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBVZWxsdFdFVU5KMFRiTVVS\nOGtFdDBWZWxML0d1MnV0aVJsdm5SSHZPVzE4CkZlam1NS3VXMWJjWVBUSVpXVWtM\nMTg5cTV4b2JtQkxOMW5pbTdhWDRjNkkKLS0tIDNmOEl3ajVTcUtFQjQrUHI5eWhS\nMlZWQ3NxQzgzU05ZRGI5WjFtdXJJUU0KKPT+Gt3NoUyd49+qKxY+0THQFsfafeh4\nLjjD2cC6TGB9QT/m8qw4bwyGVo7qPxEV8eFkKASqq3gCOQZknRMVzA==\n-----END AGE ENCRYPTED FILE-----\n" 12 | } 13 | ], 14 | "lastmodified": "2025-01-01T12:52:58Z", 15 | "mac": "ENC[AES256_GCM,data:g4M9yL8KxYLztAIloMK6LYv1UrsEu0xU3Gm3swi48oUNqH+WeU40bPHTwgJYGMvMjm//+aLI82aE3gfjb4A1pwIbd/naskO9u1TkisK7CzECmLXjm3upUyNJ+pftr+V8GWcND52Pmzb3WQ7gE62TEFp3Xvo03uhxN01NJ5yyNMI=,iv:8QyNLGNZYTrZkntCgPu6XLD6PdKJxkGYDgBhsEEOpCY=,tag:kH8iRWZn9e4P8ZictF20Kg==,type:str]", 16 | "pgp": null, 17 | "unencrypted_suffix": "_unencrypted", 18 | "version": "3.9.2" 19 | } 20 | } -------------------------------------------------------------------------------- /secrets/miniflux: -------------------------------------------------------------------------------- 1 | { 2 | "data": "ENC[AES256_GCM,data:9Y27DEE5X4/ti5lB3xHkpvYip2iSjBAGxv7GXIhXGzjwlb5+luJYDa3JGW8cYwQR2eJk2q1G9pW5t5IW,iv:QmyRmLBZODyYUpl/fnQBlfP1aW3TqECULyum8N1msAg=,tag:TksZJbytXJRPS4d1Ky2HdQ==,type:str]", 3 | "sops": { 4 | "kms": null, 5 | "gcp_kms": null, 6 | "azure_kv": null, 7 | "hc_vault": null, 8 | "age": [ 9 | { 10 | "recipient": "age1nklm02ejf57n0x208mjrww9s8np44kkmwfl7wdn78dtmh7x2g5wqh987h9", 11 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB5Qk5kallUY3BtMU1aS1hz\nc24rWkRkdURjUURwMmsvMkloT0ZZeDZiTkg4Cm91c3N1TXo1R0JaK0NSaEhBM3lN\ncUlCM0ZLZ2JwK3JBV29MWkExVmE0WG8KLS0tIDFKVk5PRGNlelVTRWl6Nm80MTJp\nTWNrMmVqYTlVRjRlZmJKaUVTQVIzQTgKDikF/KecK+VqHwFgWsXmRdNkaFcPSp7W\neWpwcKb2v5IgZDQJ5BPEfQ2LD7ft9Arg17PqJDauKb0GH+qOoioINA==\n-----END AGE ENCRYPTED FILE-----\n" 12 | } 13 | ], 14 | "lastmodified": "2025-01-01T12:52:58Z", 15 | "mac": "ENC[AES256_GCM,data:2Pqg5B04n48LBhK2VBjLfU/mKlOCREZEr46nDXT71kJSj3gs34BDTjdnuhhn5M+LiuumjEB8oYcIjC+ptZMLoUXXZbTAWceI1eZT11s95qIwHeR3yf98ph4ZbbeLH+F3vSO0zFBhFrlvKzXHEIsKKfAW9xp1W/zjshxNGhpGooo=,iv:QwL66lAgn41k2lmauxMrVaFk0NrIETCZbcW1bwtuonU=,tag:vuLMZZZv1woH4oaGiXrkxQ==,type:str]", 16 | "pgp": null, 17 | "unencrypted_suffix": "_unencrypted", 18 | "version": "3.9.2" 19 | } 20 | } -------------------------------------------------------------------------------- /secrets/networkmanager: -------------------------------------------------------------------------------- 1 | { 2 | "data": "ENC[AES256_GCM,data:1ZbgvYlC0RfojWRBXvBDabYwQQqvvRamqupcO+uGUFOb3EFWDcLXI1CGN3t/R3lZbQ52ZLWH2wxisMehXlgATcBcBeKIIyMy+tnOvSsgcbDdR1biY8UzZ/dBvVD3ydcAbDYrkOVVq3R8UbI/PMRN4Y7ntH3Z0SaxdHigMcWv0sxmtpdk+L18ug==,iv:Vh/WlE/WzzqUFxckGFju/rQKmdyXbubeOyGsgXT2Vh0=,tag:jSuD5b4E/inXOtYUEyhf6g==,type:str]", 3 | "sops": { 4 | "kms": null, 5 | "gcp_kms": null, 6 | "azure_kv": null, 7 | "hc_vault": null, 8 | "age": [ 9 | { 10 | "recipient": "age1nklm02ejf57n0x208mjrww9s8np44kkmwfl7wdn78dtmh7x2g5wqh987h9", 11 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBveTU2bWh5WEtqTjN2OUcx\nTisxVnJYSGlCTVdvZFpKYXhFNHVVVjVmVWw0Ck5iUDFSY2FsTlIvZ3Vxcmdva05U\nRU1mYzJOZVZTNW1qVWk2UnF3ZXQ0cjAKLS0tIHdia2dvVlZobU9QbnFmbC9Oa2xw\nWG5SSENxd1hkbHR1Wm5RZkVWbWhRKzgKfUtjR6YhXSfTqpgQgDc0N/PcLqjv0wUD\navG6fyMK4pyjNgpavA5qoMnlKcUwOzxDbrblT52YoAr2S1JvffEnlw==\n-----END AGE ENCRYPTED FILE-----\n" 12 | } 13 | ], 14 | "lastmodified": "2025-01-01T12:52:58Z", 15 | "mac": "ENC[AES256_GCM,data:KbxHMy7zWdIFDaKm42wDchf1Z9C4Uuhx/9SFbBNg2UkgB2mPDP36msaK8GcwcCx0oGUpRFJCJb26rwQSvkCYMOWz3eIGtji4jPpjX+lUPRb6Vx4uld+erjvVw5qd4GzGJ1Y+mkDxzoX5P5MLhsMUOSMa6GpObWc1ZtG2ALG5/3U=,iv:m4Yphim+cHhzAhXjwHe7irpH0290vsnc3ehcAneTV3s=,tag:MoGA37RS3HyDoYrl1uka+g==,type:str]", 16 | "pgp": null, 17 | "unencrypted_suffix": "_unencrypted", 18 | "version": "3.9.2" 19 | } 20 | } -------------------------------------------------------------------------------- /secrets/nix-access-tokens: -------------------------------------------------------------------------------- 1 | { 2 | "data": "ENC[AES256_GCM,data:0pTyn2+J0ZF6KH8WDyZX7AQtMh3ZHM7xfpIX8SAUXX4YnN31ASqi0BzYdRw1qqxu4/EC/4gtebnnEr1UMYuNxxndGH/4KsCdUeoydxR04/1/Othn10el05USI5zuQO32lGpxgjBmgkkuFSg6C0k=,iv:BJ3A93Zi6+aR5IjRo/erqlLplNB2eMEI8lPKo9y5IPE=,tag:TAh5X5mbs0JZnZfC1XXmqg==,type:str]", 3 | "sops": { 4 | "kms": null, 5 | "gcp_kms": null, 6 | "azure_kv": null, 7 | "hc_vault": null, 8 | "age": [ 9 | { 10 | "recipient": "age1nklm02ejf57n0x208mjrww9s8np44kkmwfl7wdn78dtmh7x2g5wqh987h9", 11 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBUSCs4TFlwRFdCMkp3NkJL\nN0J6TnU4WVpEYmdkVEZtcXhPUktmd3lWSmlVCnd0RXpMR0UyT0xpL2xOM25qSS85\nSXlmNjh2UVZnNUNjMkp3U2o5cmJtR00KLS0tIGJkNnVFRGhDa2ZRRG5pLzRDSDdo\neFZNcVZTMDJhV1ppcmRYN2dPYVBLNFUK88jQ33/3UXkR0RdMcJ6zA47mo42MXIfV\nMFLR4ciAJGpaHKsDERcto8cJEfzWuruE/lk1YjaC84lRwaYcE3QKjw==\n-----END AGE ENCRYPTED FILE-----\n" 12 | } 13 | ], 14 | "lastmodified": "2025-01-01T12:52:58Z", 15 | "mac": "ENC[AES256_GCM,data:zMzD3xjm8X4pM6VV27mAo+d9bov4DYs0WSOi52hKHExlI7pmnuK5GDyyX08Ni9WZ9j+mgnGAE1+dlScCume5U1L5YZ89kxLC4j1T2N94J9i/xsBKlhDs76lUEFHXq4x2J6ZRPpN3WhCNTrMjSXVhOJSUzwEVb9trGXHZpWPPugQ=,iv:pslkjTZXWbgxjKgB8GbuGWi2jj6YhoVLkpo+hInMGxo=,tag:qGQIxgo/iSxQuYjubWvd7A==,type:str]", 16 | "pgp": null, 17 | "unencrypted_suffix": "_unencrypted", 18 | "version": "3.9.2" 19 | } 20 | } -------------------------------------------------------------------------------- /secrets/nix-binary-cache: -------------------------------------------------------------------------------- 1 | { 2 | "data": "ENC[AES256_GCM,data:4uCjm9q1gpKOJokRZSqKLahl/Bja/WQYnSipG8AyRJKYM6dnLIPoXlJrwbK843iF63MarD6HtlfyxJ5Ks2ySm8jfd1KC/bzQ2HUUADWSIiFr1tqfjLIaphX6Xmh2JvSuBUQDVqfr,iv:XPyl90U+kcmcjvPzBcXug7BEXZ1pmj1vOMKrgNy8UB0=,tag:+1OvBl83+f77ISaJZkN0iw==,type:str]", 3 | "sops": { 4 | "kms": null, 5 | "gcp_kms": null, 6 | "azure_kv": null, 7 | "hc_vault": null, 8 | "age": [ 9 | { 10 | "recipient": "age1nklm02ejf57n0x208mjrww9s8np44kkmwfl7wdn78dtmh7x2g5wqh987h9", 11 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBpWHNwVXIreFJkdktMMUtx\ndTg1cDhoMTc4dUJ2b2FIWURaM1FTQlAvNTFvCktKdXBTVmVjRStrWTdNbDc4dmlm\nSEx5dmdrSWFGd2RxcGI0eWxYK0g0S1kKLS0tIGd6aEdsbTJpUldpYXlnaDhISko4\nQTl0NXI2SlprejRBbzFsZHVhMU5GU3cK1Bi8HPrmHlO3b2T9w+vzZlKOz3GnkDMd\n9juuPKg7ViOB7Xj2jACNtcUXVMgVru24ecJo4fTu9rrhG5eIQS9Lww==\n-----END AGE ENCRYPTED FILE-----\n" 12 | } 13 | ], 14 | "lastmodified": "2025-01-01T12:52:58Z", 15 | "mac": "ENC[AES256_GCM,data:nrEWdSbMwnRzlCgfGQQL9PCJaJVEjPMzkpkTo/1heAuz/iydfvHCZrVYbKWVhA5Ck+ABZHG9WS6orV6lm4TTsv1zJ3VlbF/BGriz7kD3kQnxPaWB0eU6StsMGEz8TPlAOBIwVpM7mmFvmqFFejVm0co6y2dV4IQaDGw5WwmMVLM=,iv:71ENAJWvXs2vmfHp6eIu10PaHg9aOAjNRH+Mg7wdwGs=,tag:hPEBHpYIIx/M7HoZOqsFzw==,type:str]", 16 | "pgp": null, 17 | "unencrypted_suffix": "_unencrypted", 18 | "version": "3.9.2" 19 | } 20 | } -------------------------------------------------------------------------------- /secrets/syncthing.yaml: -------------------------------------------------------------------------------- 1 | fu: ENC[AES256_GCM,data:Te5Jo+OzNR+MBKxpVO41gGfMFiy7RCsQUzv5xDK074/pUfYj+wBSjy53lOHKvrqUQiqxhLLNc1MZPaWZUapgtF5dcXkUZlWoE0eKdaF3eVFV3kIQ0RkItV7D6cJhYmrQPgYctPGKgIS/9sySJG1/TQgkD0J0M0upMmgkGS/dUqDi0Vr9TritFu9K8GwUEMlhSjucYGvXlIKipbjHrQ/UzSFo7+BQswtV/ER7/JpaVEZQKYDWXuW1fh5OuPK7kvNHEUhLc5XoNjb8NFvrWEjGVjIs3RUoFbbZM5w0u/Cv1JAkXvwUe8QdxcEg3QhZMK6shXaw60P20JhTnTkxWaO4iSNdBGX2+DML0fnyldYhTqnLnTn+G4nPkfy5tjZDbamH,iv:BgFP85OxS1TNi1N0+x5zut5oBZ+jWhvO08PzP9HTBJ4=,tag:E9KNxNcSlIbRQbsjoZcRpg==,type:str] 2 | mo: ENC[AES256_GCM,data:QGbwUVTc5xPo2HIN3XY7Rb1uTY7OyU83+fxXCyQhnN2GTx+yrF0keNH80d0lf8SYGRqpM/Sn5J/uolhDIedX9BXe9OQVm6AybE35fLGOB+wb/nH7rYsbSdmDzIT43OZIf+k13qvbY32Ui3M6AaiJDccK/PbJA39x62YgEMTtzJvYh91dTF/r+Jj0ETqiolWekduFH4ZJRm1WQB160bMP1gUBbxvxD1HbY+p5JSegRvrNkND/7X8qL1C/VyAaj9WH0Nw95y/kpd24C1UQJ54wMkgoXrADr82arN6gMj+Vtt3IVBth+usoO3U53KvxS2TEHhyE4ugxWQvtAlx7Zqsyzkinu7YPTKE5FvL3d74IJPG2xUEAhjB9itDMRvbxR6By,iv:SpGnaN8S3bqUfdSixVgPv+PsolEGIJ3ILWt0vt27IMM=,tag:tiWuIgtKPrAw/+IkBxNEUg==,type:str] 3 | "no": ENC[AES256_GCM,data:7opAr/mWn0PRk0nu1qnw/SRqVXwTHaAwsYvqZTrUkhALuo38QflNUpV4Si9IxgfZO1uNXTnUXmEO2Jk7FkYDnOJxJAiuoa48lUTrzc9rjZvrMqdGUUyHaXTxlvyy0dUvhmPTkHWH7z7lkIM1NVhPllTn6QhyvtFsXgF99cHda2KJ5uO6N5Fzo3sc/n64QKdQfjVNjys47lfIXW5sr4sXh6KUYlmo5N3qzL2nNZ67Sh6WmAPfrKXTeNn2Qfco8RzmZeTIbQTMjGORdYUmr1ebhh5UiYK6xVnyUOr1tMXcKIwFXcMgwEfRqitCSoI6KxJPU+3PuLJo0TwazDcSmlanrHQ2iY2ejTcb/6ILW5VTQBtBIRHVQzBpJaHErjbCsfkU,iv:qeXC5ZPTKcGeskwrYDyp6uyUmaODaJZX3d5+e9VPDow=,tag:Qx/KGVSHhXyP7vOm9pxKSQ==,type:str] 4 | ki: ENC[AES256_GCM,data:UqorjBHBePB2toV6+9splzI+CnpbXg/Y6Q2ybiO7bLnekW8vu/8AWEsRIfOmfIA4+lMyYLONk0NngFbD20+haibzdlXauTimhmMQBFBZ0s7qbjzuwQSBJEVsc1bkkHPUYyKtrJkzSsSDfI/fudPu3c8ToeeOaQIYVX9tv8yzMJG3i5pOkbKq2FEh8HPdTmdh+KpmggUrnm/uvglOyG6C/lL+PYgAg00XzZCtNRHQaMB5wiOPzUot8WDttMYdpSUzdxvedqhnJdgCPm5pv/81lOHbWOIAOatfs+yXX2mRHvRPBmWIQRa63z2v6wLD6zh90T5vWqLbQ5N1BqAfuco6/HxIYIJKES1MXqufU/TE752siFgaGbE0FMd0bvAQvGdT,iv:o025Cza8f46uw8A1IM3GZD9jl07JyuTmpal/LHnIArE=,tag:Ls5Ggf4eHHoY/visMfTqlw==,type:str] 5 | ku: ENC[AES256_GCM,data:YDHe1rD1wn/555zooArAOvXZWR2GOxxZZgP3Ft78qxx60NuMvG8AA/wTZqxLMpkzv9dgOuzJIgOFzbUX3xyUk3BlgycmmfEyv4KVwfOcee2M98+LqKjIqJAgZ1kJ8KzJrkfWXu6IunU67JTs9aesHbEosOWCJvWPVA5VEANsMkhgZBZ9oi7NqOPy8lakbCzxiXFSwBW/QJklzTEUIz6jvp3YqhShz3GQzVCSDnVaPeCi+e+2PCLy/gobNxD5laFJ3kxGFD/rDSOR4ObB97xTcmMFryy4Pr9SPhwdFY6UOq1K9OU0Rwbs2Sh7XiS0S7qZW1a1qdKzg2ch0w7Tj7oujjtm6zbL6uKuimosw/YhkyAdGcN6nnQ6NWfpSvJEy8jx,iv:83RH23dmI1q1mubOmiClvSu8Sa4i1Ci4NPfbwKqZkNs=,tag:pUg8TddkMzvx5rnWDpMgag==,type:str] 6 | sops: 7 | kms: [] 8 | gcp_kms: [] 9 | azure_kv: [] 10 | hc_vault: [] 11 | age: 12 | - recipient: age1nklm02ejf57n0x208mjrww9s8np44kkmwfl7wdn78dtmh7x2g5wqh987h9 13 | enc: | 14 | -----BEGIN AGE ENCRYPTED FILE----- 15 | YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBacmIxcTdsbUY2TCtGNjVK 16 | V0ljSm1hZmIyK2Y5UVdrdjBTSDlZdUhuK0hrClY0dVBYVWJtZ2hoYzZQR3FZTGtk 17 | NTBhazhnYTBvNlpWQkxtQ2NlOWl0VTAKLS0tIFJENDJpTDFIQXMzeEtNSTBNRjRU 18 | UkF4WDlVY0szWlpQNElxazRQelc5MU0KgnXMDgoJ5EsojHTBzt5lKwKXXSo7RQnJ 19 | rUiX7qkwsmtGczYQ3qWcknNld8UKjrk4TozWFTHXLwjM/PcJnWjFUw== 20 | -----END AGE ENCRYPTED FILE----- 21 | lastmodified: "2025-03-16T20:31:40Z" 22 | mac: ENC[AES256_GCM,data:NZMQI8fRSRltQr5IwUqnHWSr9lplwgPBEmFwL1ndF87QJF7aDwbsiB/svVN2SP0x8zH8rcDiB4Qm+sA4WOXWOtAesaTuCaQKv+5k7WoAEoT5vyqczZlCqylQ4iKuTb/tKFAI39larSNDforIKq0XOpoRshrzi2grBUVAJLJ3GKI=,iv:Di7wDuTeyPPRF9aNL39589uUQpfJ1qZWGCrgoyBvShU=,tag:Ls5fwQqrfLDUIRwmMiwyDQ==,type:str] 23 | pgp: [] 24 | unencrypted_suffix: _unencrypted 25 | version: 3.9.4 26 | -------------------------------------------------------------------------------- /secrets/ulmaoc-topic: -------------------------------------------------------------------------------- 1 | { 2 | "data": "ENC[AES256_GCM,data:t8kDWSfDV5/Dh6BWTLk2eA==,iv:jD+m4mqijJsgrGQAtRxl5bUlNbtpO5lSbTO3ldeClhY=,tag:/WIxdbgYs5DFArove239vg==,type:str]", 3 | "sops": { 4 | "kms": null, 5 | "gcp_kms": null, 6 | "azure_kv": null, 7 | "hc_vault": null, 8 | "age": [ 9 | { 10 | "recipient": "age1nklm02ejf57n0x208mjrww9s8np44kkmwfl7wdn78dtmh7x2g5wqh987h9", 11 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA2RDZBdm9kYjdHUkcwUkUw\nV3lqcUUvUU8rbjZwOS8vbEdZOTBVZklNdnpNClNnTk1CVTY1d0h0SWdmK3gyaVhP\ndituWTc4YnV3VFUvNnJmNUt3QWM2L1UKLS0tIFVwZUt0QzNKSGdjZklBUEtqM1NL\nbVNnVzdvWFN2aVhEbjZsRW0xU0Z0bk0K0JUNr2Jg+wdYbEox1Fw50l9VpGLXCLEo\ncfNhADxTi5glcZPbmOhvsIny/kM9bl11l3sFvj3moNdLf1AzVaepEA==\n-----END AGE ENCRYPTED FILE-----\n" 12 | } 13 | ], 14 | "lastmodified": "2025-01-01T12:52:58Z", 15 | "mac": "ENC[AES256_GCM,data:k9U/deS86m8Zt51As+P0HP79Ug4yxspYSTPSafjlgm/c7DGYncAa2jqw7r9ARxcLa5z26IguAHvfCt6heRymW8JIm6VoD4oNT8wyubaz0KPWUH0GSZq2+ssWM1DN5eCqEBcPZLNsq84G3lY1dTUYuvGSHtXbyAXnBFgiwZj648E=,iv:xlMWaxZX66HJWw8FmALAKF7PhXM2q/9qloOExO0gJaM=,tag:JggHHNu/4tcDrRufZ+tmdA==,type:str]", 16 | "pgp": null, 17 | "unencrypted_suffix": "_unencrypted", 18 | "version": "3.9.2" 19 | } 20 | } -------------------------------------------------------------------------------- /secrets/weechat: -------------------------------------------------------------------------------- 1 | { 2 | "data": "ENC[AES256_GCM,data:Sv6Z/VnRfr2m/46N0fEwuxU=,iv:Won+/RfUoYcPklTlyQ2sg3Xgjt2o6WZNODcBSI7t4VE=,tag:GyYER7l14euv1dWq0NJGiQ==,type:str]", 3 | "sops": { 4 | "kms": null, 5 | "gcp_kms": null, 6 | "azure_kv": null, 7 | "hc_vault": null, 8 | "age": [ 9 | { 10 | "recipient": "age1nklm02ejf57n0x208mjrww9s8np44kkmwfl7wdn78dtmh7x2g5wqh987h9", 11 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAvWlRCaVZjcmR1YnF6UlA2\nS3BRY29YRHpDdWQ4VFhZOGhYNEUrNkIwQ1U0CkxQZ0pERVRmbVh1N0Y1dUxWcG41\nVWF0Y3R0cmxmajRleG1HNFJadWJVM0UKLS0tIG1UR2Z0blZqbFAxYzJuU09UL1pC\nM1F4aTRqZ1BJZkZCQkhaK2UxNFFKMU0Ku3Rrzs641qqUq6F4zFbBwIBRNPVDoVii\nSAX6ae8fJ/q/AvHtGgrINEIJwI1IS6BZYqZJfU4krU4HiWMrhilmnw==\n-----END AGE ENCRYPTED FILE-----\n" 12 | } 13 | ], 14 | "lastmodified": "2025-01-01T12:52:58Z", 15 | "mac": "ENC[AES256_GCM,data:HtnBT67K7fSalMMQtbOz6z/HSDHQdvVxUr3y3CW/QWAZ99+Ava5DuRhTrRBe7zHak5N6aTQtdRpNbOUtbv3+m2XX/zqQBIBuijncE2KiYit4XP4dvuEl/2CPDFJiesYgiU+XqLEpj15TUHQVMKk7WquiwtPIUlF4boc4KS6guKk=,iv:jPVUiu0o7fT5VfDB2UYDXbl2njI81qcPyhNhFsrSBTg=,tag:qet5Op9YiYLUM2Uv45668Q==,type:str]", 16 | "pgp": null, 17 | "unencrypted_suffix": "_unencrypted", 18 | "version": "3.9.2" 19 | } 20 | } -------------------------------------------------------------------------------- /secrets/wireguard.yaml: -------------------------------------------------------------------------------- 1 | fu: ENC[AES256_GCM,data:1AHNdjDm6bnvviIPyZVBJNLmkqULY90BwIT1LUUJn5evFF0b2Xh7sYZ5r0Q=,iv:EHMLAoGkA/0G2C7psf40qFfvSZpM7Kt9PxtTzF/NNWo=,tag:B45YSbcamP4TvCh+LJl8ng==,type:str] 2 | mo: ENC[AES256_GCM,data:oIUXUjnrYey8FpXKO6fhXJsn4nLy/9fD+T+b9ZVG1ao3FjfwX5gYckUOZKM=,iv:1wv1ZvfhWmbciRDgjAj88B5z3g1UOEcMRpBXQshXCA4=,tag:utqpB3Rw7y1JUColAU9N3w==,type:str] 3 | "no": ENC[AES256_GCM,data:l0FZ8l4vkSUg9LUnHwXujTI2+dPWpG1u3bKL0nSkAbbb6vd0m7RVkkT0i3s=,iv:Dka5B8hztlISv7LzHoPtRTHowDyC+GT5KdQhUmsVNbo=,tag:EX3fP8BkjOMNKLAlFuUTbw==,type:str] 4 | ki: ENC[AES256_GCM,data:4x3cDi7vDSuCsKGhn3X7V8aOXACXqA7Ktm9GL0E4OeLkOR9ZYPS63GUPS3A=,iv:0pmSosWthpVrMUboBKO5G1QQgblehIaW7s35WIst9ts=,tag:Yrmoz5xAfymU8a8qHnjVaQ==,type:str] 5 | ku: ENC[AES256_GCM,data:8e75LErMq4Hi4JtYxDuyF3mNtWImsuGhD5ixBOv8Z7F/bQGv0htfb0q3aZo=,iv:JzmC4/nCAuFSHT/ZE4AeTLN0B2VU7ZzCtwlLvyEQ0RY=,tag:uTqYmO8sMQ2Cq3eiLUSOvA==,type:str] 6 | sops: 7 | kms: [] 8 | gcp_kms: [] 9 | azure_kv: [] 10 | hc_vault: [] 11 | age: 12 | - recipient: age1nklm02ejf57n0x208mjrww9s8np44kkmwfl7wdn78dtmh7x2g5wqh987h9 13 | enc: | 14 | -----BEGIN AGE ENCRYPTED FILE----- 15 | YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBDLzVpNTdDYTVWS1c4S0t3 16 | WDFQZ2djTVFpQUhjeGZSUzlWcHVOK2V3c0h3ClY1eTdMUWQvQ3ZrVCtaWDJjakZy 17 | ejVjbzVDczh0WmJlTkxYRHAyMzNuR0EKLS0tIEJ4dC9DK0tJTnBMWm1EWlhYczNk 18 | VnZFekVOaVZ2NVc5UjkwYk9DS2I2THMKNREQnuL6V3eILq9foPiC329zM1a6kqAB 19 | WxIrORhnA67eqwvp2ho0u70UzoFJe92OovZkIAsNJlollWhcGRU2Hg== 20 | -----END AGE ENCRYPTED FILE----- 21 | lastmodified: "2025-03-16T20:31:46Z" 22 | mac: ENC[AES256_GCM,data:0jDUz19YJo4bOpmhtf6e/CDAltDAXy7tDvbXQfuJovtmvuIzx4FdqKSmEA3lgkSlqaQMJcJGYhL+0JOWZMzlXULpkW4p0TxnOIxmKI2XG7iI9MgnpIVeoclgX1mA77/gP1BtLJ0fAkWrnuzP5jbBh3txKw30zV10hgAz5khWZ38=,iv:1oMmJENeaSARSzOCaOkDj8Bs+djxSzehJkOYJ/KfrLk=,tag:szhTOmRki3NDbOj9Ccfo+Q==,type:str] 23 | pgp: [] 24 | unencrypted_suffix: _unencrypted 25 | version: 3.9.4 26 | --------------------------------------------------------------------------------