├── .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 |
--------------------------------------------------------------------------------