Repository: budimanjojo/dotfiles Branch: main Commit: 994d428683ed Files: 266 Total size: 478.4 KB Directory structure: gitextract_4wydarc6/ ├── .chezmoiroot ├── .editorconfig ├── .github/ │ ├── FUNDING.yml │ ├── renovate.json │ └── workflows/ │ ├── broken-link-check.yaml │ ├── build-push-cache.yaml │ ├── check-flakes.yaml │ ├── renovate.yaml │ ├── update-nvfetcher.yaml │ └── update-sops-keys.yaml ├── .gitignore ├── .justfile ├── .sops.yaml ├── LICENSE ├── README.md ├── chezmoi/ │ ├── .chezmoi.yaml.tmpl │ ├── .chezmoiignore │ ├── .chezmoiscripts/ │ │ ├── run_once_after_90-cleanup.sh.tmpl │ │ ├── run_once_before_10-setup-fish.sh.tmpl │ │ ├── run_once_before_20-install-packages-archlinux.sh.tmpl │ │ ├── run_once_before_20-install-packages-ubuntu.sh.tmpl │ │ └── run_onchange_after_80-setup-terminal.sh.tmpl │ └── dot_config/ │ ├── fontconfig/ │ │ └── fonts.conf.tmpl │ └── termite/ │ └── config ├── flake.nix ├── flakeLib.nix ├── home/ │ ├── _modules/ │ │ ├── _default/ │ │ │ ├── default.nix │ │ │ ├── nix.nix │ │ │ └── sops.nix │ │ ├── browser/ │ │ │ └── firefox/ │ │ │ └── default.nix │ │ ├── default.nix │ │ ├── editor/ │ │ │ └── neovim/ │ │ │ └── default.nix │ │ ├── homelab/ │ │ │ └── kubernetes/ │ │ │ ├── default.nix │ │ │ └── secret.sops.yaml │ │ ├── multiplexer/ │ │ │ ├── tmux/ │ │ │ │ ├── config/ │ │ │ │ │ └── tmux.conf │ │ │ │ └── default.nix │ │ │ └── zellij/ │ │ │ ├── config/ │ │ │ │ ├── config.kdl │ │ │ │ └── layouts/ │ │ │ │ └── default.kdl │ │ │ └── default.nix │ │ ├── programs/ │ │ │ ├── beeaccounting/ │ │ │ │ └── default.nix │ │ │ ├── chezmoi/ │ │ │ │ └── default.nix │ │ │ ├── fontconfig/ │ │ │ │ └── default.nix │ │ │ ├── go/ │ │ │ │ └── default.nix │ │ │ ├── obs-studio/ │ │ │ │ └── default.nix │ │ │ ├── qmk/ │ │ │ │ ├── default.nix │ │ │ │ └── qmk.ini │ │ │ └── yamllint/ │ │ │ ├── config.yaml │ │ │ └── default.nix │ │ ├── services/ │ │ │ └── opencloud-client/ │ │ │ └── default.nix │ │ ├── shell/ │ │ │ ├── dircolors/ │ │ │ │ └── default.nix │ │ │ ├── fish/ │ │ │ │ └── default.nix │ │ │ ├── git/ │ │ │ │ └── default.nix │ │ │ ├── lf/ │ │ │ │ ├── configs/ │ │ │ │ │ ├── colors │ │ │ │ │ └── icons │ │ │ │ └── default.nix │ │ │ ├── nix-direnv/ │ │ │ │ └── default.nix │ │ │ └── starship/ │ │ │ └── default.nix │ │ ├── terminal-emulator/ │ │ │ ├── alacritty/ │ │ │ │ └── default.nix │ │ │ ├── contour/ │ │ │ │ ├── config/ │ │ │ │ │ └── contour.yml │ │ │ │ └── default.nix │ │ │ ├── kitty/ │ │ │ │ └── default.nix │ │ │ └── wezterm/ │ │ │ ├── config/ │ │ │ │ └── wezterm.lua │ │ │ └── default.nix │ │ └── windowmanager/ │ │ ├── add-on/ │ │ │ ├── blueman-applet/ │ │ │ │ └── default.nix │ │ │ ├── dunst/ │ │ │ │ └── default.nix │ │ │ ├── gtk-theme/ │ │ │ │ └── default.nix │ │ │ ├── nm-applet/ │ │ │ │ └── default.nix │ │ │ ├── nwg-bar/ │ │ │ │ └── default.nix │ │ │ ├── pasystray/ │ │ │ │ └── default.nix │ │ │ ├── picom/ │ │ │ │ └── default.nix │ │ │ ├── py3status/ │ │ │ │ ├── config │ │ │ │ └── default.nix │ │ │ ├── rofi/ │ │ │ │ ├── default.nix │ │ │ │ └── style.rasi │ │ │ ├── screenshotter/ │ │ │ │ ├── camera-shutter.oga │ │ │ │ └── default.nix │ │ │ ├── swayidle/ │ │ │ │ └── default.nix │ │ │ ├── swaylock/ │ │ │ │ └── default.nix │ │ │ ├── terminal-emulator/ │ │ │ │ └── default.nix │ │ │ ├── theme/ │ │ │ │ └── tokyonight/ │ │ │ │ └── default.nix │ │ │ ├── waybar/ │ │ │ │ └── default.nix │ │ │ └── xdg/ │ │ │ └── default.nix │ │ ├── hyprland/ │ │ │ ├── config/ │ │ │ │ ├── appearances.nix │ │ │ │ ├── default.nix │ │ │ │ ├── keybindings.nix │ │ │ │ ├── window-rules.nix │ │ │ │ └── ws-outputs.nix │ │ │ └── default.nix │ │ ├── i3/ │ │ │ ├── config/ │ │ │ │ ├── commands.nix │ │ │ │ ├── default.nix │ │ │ │ ├── keybindings.nix │ │ │ │ ├── modes.nix │ │ │ │ └── startups.nix │ │ │ └── default.nix │ │ └── sway/ │ │ ├── config/ │ │ │ ├── commands.nix │ │ │ ├── default.nix │ │ │ ├── keybindings.nix │ │ │ ├── modes.nix │ │ │ └── startups.nix │ │ └── default.nix │ └── budiman/ │ ├── config/ │ │ ├── gitcommit-message │ │ └── neovim/ │ │ ├── appearance.nix │ │ ├── autocmds.nix │ │ ├── default.nix │ │ ├── diagnostic.nix │ │ ├── general.nix │ │ ├── keymaps.nix │ │ ├── lsp.nix │ │ ├── lua/ │ │ │ └── utils.lua │ │ └── plugins/ │ │ ├── blink-cmp/ │ │ │ └── default.nix │ │ ├── chezmoi-vim/ │ │ │ └── default.nix │ │ ├── cord/ │ │ │ └── default.nix │ │ ├── default.nix │ │ ├── endec/ │ │ │ └── default.nix │ │ ├── gitsigns/ │ │ │ └── default.nix │ │ ├── grug-far/ │ │ │ └── default.nix │ │ ├── lualine/ │ │ │ └── default.nix │ │ ├── luasnip/ │ │ │ ├── default.nix │ │ │ └── lua-snippets/ │ │ │ └── gitcommit.lua │ │ ├── mini/ │ │ │ ├── comment.nix │ │ │ ├── default.nix │ │ │ ├── icons.nix │ │ │ ├── indentscope.nix │ │ │ ├── surround.nix │ │ │ └── trailspace.nix │ │ ├── noice/ │ │ │ └── default.nix │ │ ├── none-ls/ │ │ │ └── default.nix │ │ ├── nvim-autopairs/ │ │ │ ├── _rules.lua │ │ │ └── default.nix │ │ ├── oil/ │ │ │ └── default.nix │ │ ├── snacks/ │ │ │ ├── default.nix │ │ │ ├── input.nix │ │ │ └── picker.nix │ │ ├── toggleterm/ │ │ │ └── default.nix │ │ └── treesitter/ │ │ └── default.nix │ ├── default.nix │ ├── hosts/ │ │ ├── budimanjojo-firewall.nix │ │ ├── budimanjojo-main.nix │ │ ├── budimanjojo-nas.nix │ │ ├── budimanjojo-oracle.nix │ │ └── budimanjojo-ubuntu.nix │ └── profiles/ │ ├── extra-gaming.nix │ ├── extra-graphics.nix │ ├── extra-utilities.nix │ ├── server.nix │ ├── workstation-common.nix │ ├── workstation-hyprland.nix │ ├── workstation-i3.nix │ └── workstation-sway.nix ├── lib/ │ └── default.nix ├── overlays/ │ └── default.nix ├── packages/ │ ├── _sources/ │ │ ├── generated.json │ │ └── generated.nix │ ├── configure-gtk/ │ │ └── default.nix │ ├── default.nix │ ├── fish-plugins/ │ │ └── default.nix │ ├── krr/ │ │ ├── about-time.nix │ │ ├── alive-progress.nix │ │ ├── default.nix │ │ └── prometheus-api-client.nix │ ├── kubectl-rook-ceph/ │ │ └── default.nix │ ├── nvfetcher.toml │ ├── nvim-plugins/ │ │ └── default.nix │ ├── tokyonight-gtk-theme/ │ │ └── default.nix │ └── tokyonight-icon-theme/ │ └── default.nix ├── shell.nix └── system/ ├── _modules/ │ ├── _default/ │ │ ├── default.nix │ │ ├── nix.nix │ │ ├── secret.sops.yaml │ │ ├── sops.nix │ │ └── users.nix │ ├── containers/ │ │ └── beeaccounting/ │ │ ├── default.nix │ │ └── secret.sops.yaml │ ├── default.nix │ ├── displaymanager/ │ │ └── sddm/ │ │ └── default.nix │ ├── monitoring/ │ │ ├── node-exporter/ │ │ │ └── default.nix │ │ └── smartctl-exporter/ │ │ └── default.nix │ ├── myHardware.nix │ ├── programs/ │ │ ├── adb/ │ │ │ └── default.nix │ │ ├── hugo/ │ │ │ └── default.nix │ │ ├── msmtp/ │ │ │ ├── default.nix │ │ │ └── secret.sops.yaml │ │ ├── nh/ │ │ │ └── default.nix │ │ └── qmk/ │ │ └── default.nix │ ├── services/ │ │ ├── btrfs-autoscrub/ │ │ │ └── default.nix │ │ ├── grafana/ │ │ │ ├── default.nix │ │ │ └── secret.sops.yaml │ │ ├── openssh/ │ │ │ └── default.nix │ │ ├── prometheus/ │ │ │ ├── default.nix │ │ │ └── rules/ │ │ │ ├── embedded-exporter.yaml │ │ │ └── node-exporter.yaml │ │ └── restic-backup/ │ │ ├── default.nix │ │ └── secret.sops.yaml │ ├── system/ │ │ ├── autoupgrade/ │ │ │ └── default.nix │ │ ├── bootloader/ │ │ │ └── default.nix │ │ ├── cpu/ │ │ │ └── default.nix │ │ ├── font/ │ │ │ └── default.nix │ │ ├── sound/ │ │ │ └── default.nix │ │ └── video/ │ │ └── default.nix │ └── windowmanager/ │ ├── add-on/ │ │ ├── blueman/ │ │ │ └── default.nix │ │ ├── gnome-keyring/ │ │ │ └── default.nix │ │ ├── networkmanager/ │ │ │ └── default.nix │ │ ├── polkit-gnome/ │ │ │ └── default.nix │ │ └── thunar/ │ │ └── default.nix │ ├── hyprland/ │ │ └── default.nix │ ├── i3/ │ │ └── default.nix │ └── sway/ │ └── default.nix ├── hosts/ │ ├── budimanjojo-firewall/ │ │ ├── _modules/ │ │ │ ├── default.nix │ │ │ ├── firewall/ │ │ │ │ ├── config/ │ │ │ │ │ ├── sets.nft │ │ │ │ │ ├── zone-directions.nft │ │ │ │ │ └── zone-rules.nft │ │ │ │ └── default.nix │ │ │ ├── network.nix │ │ │ ├── podman.nix │ │ │ ├── secret.sops.yaml │ │ │ ├── services/ │ │ │ │ ├── adguardhome/ │ │ │ │ │ ├── default.nix │ │ │ │ │ └── secret.sops.yaml │ │ │ │ ├── chrony/ │ │ │ │ │ └── default.nix │ │ │ │ ├── fireqos/ │ │ │ │ │ └── default.nix │ │ │ │ ├── frr/ │ │ │ │ │ └── default.nix │ │ │ │ ├── kea/ │ │ │ │ │ ├── ddns.nix │ │ │ │ │ ├── default.nix │ │ │ │ │ ├── dhcp.nix │ │ │ │ │ └── secret.sops.yaml │ │ │ │ ├── omada-controller/ │ │ │ │ │ ├── default.nix │ │ │ │ │ ├── secret.sops.yaml │ │ │ │ │ └── ssl.crt │ │ │ │ ├── powerdns/ │ │ │ │ │ ├── default.nix │ │ │ │ │ └── secret.sops.yaml │ │ │ │ ├── rsyslogd/ │ │ │ │ │ └── default.nix │ │ │ │ └── tdarr/ │ │ │ │ └── default.nix │ │ │ └── wireguard.nix │ │ ├── default.nix │ │ ├── disk-config.nix │ │ └── hardware-configuration.nix │ ├── budimanjojo-main/ │ │ ├── default.nix │ │ └── hardware-configuration.nix │ ├── budimanjojo-nas/ │ │ ├── _modules/ │ │ │ ├── default.nix │ │ │ ├── incus.nix │ │ │ ├── network.nix │ │ │ ├── nfs.nix │ │ │ └── secret.sops.yaml │ │ ├── default.nix │ │ ├── disk-config.nix │ │ └── hardware-configuration.nix │ ├── budimanjojo-oracle/ │ │ ├── _modules/ │ │ │ ├── default.nix │ │ │ ├── firewall.nix │ │ │ ├── network.nix │ │ │ ├── secret.sops.yaml │ │ │ ├── services/ │ │ │ │ ├── blocky/ │ │ │ │ │ ├── config.yaml │ │ │ │ │ ├── default.nix │ │ │ │ │ └── secret.sops.yaml │ │ │ │ └── qbittorrent/ │ │ │ │ └── default.nix │ │ │ └── wireguard.nix │ │ ├── default.nix │ │ └── hardware-configuration.nix │ ├── default.nix │ └── nixos-livecd/ │ └── default.nix ├── profiles/ │ ├── gaming.nix │ ├── server.nix │ ├── work.nix │ ├── workstation-common.nix │ ├── workstation-hyprland.nix │ ├── workstation-i3.nix │ └── workstation-sway.nix └── pubkeys/ └── default.nix ================================================ FILE CONTENTS ================================================ ================================================ FILE: .chezmoiroot ================================================ chezmoi ================================================ FILE: .editorconfig ================================================ ; https://editorconfig.org/ ; top-most EditorConfig file root = true [*] indent_style = space indent_size = 2 end_of_line = lf charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true ; my custom property that trim trailing empty last lines trim_trailing_lastline = true [{*.just,*.justfile}] indent_size = 4 ================================================ FILE: .github/FUNDING.yml ================================================ github: budimanjojo ================================================ FILE: .github/renovate.json ================================================ { "extends": [ "github>budimanjojo/renovate-config:default.json5" ], "username": "budimanjojo-bot[bot]", "gitAuthor": "budimanjojo-bot <111944664+budimanjojo-bot[bot]@users.noreply.github.com>", "repositories": ["budimanjojo/nix-config"], "automerge": true, "ignoreTests": true, "lockFileMaintenance": { "enabled": true, "schedule": [ "after 1am and before 3am" ] }, "nix": { "enabled": true }, "customManagers": [ { "customType": "regex", "fileMatch": ["chezmoi\\/\\.chezmoiscripts\\/.+\\.sh\\.tmpl$"], "matchStrings": [ "# renovate: depName=(?.*) datasource=(?.*)\\n\\s*current_\\w+_version=(?.+)\\b" ] }, { "customType": "regex", "fileMatch": [ "^system/.*\\.nix$" ], "matchStrings": [ "image *= *\"(?.*?):(?.*?)(@(?sha256:[a-f0-9]+))?\"" ], "datasourceTemplate": "docker" } ], "packageRules": [ { "matchDepNames": "wez/wezterm", "versioning": "regex:^(?\\d{4})(?\\d{2})(?\\d{2})-(?\\d+)-(?.+)$" } ] } ================================================ FILE: .github/workflows/broken-link-check.yaml ================================================ --- name: Broken link check on: workflow_dispatch: schedule: - cron: "0 0 * * 0" jobs: check: name: Check runs-on: ubuntu-latest steps: - name: Generate token uses: tibdex/github-app-token@v2 id: generate-token with: app_id: "${{ secrets.BOT_APP_ID }}" private_key: "${{ secrets.BOT_APP_PRIVATE_KEY }}" - name: Checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 with: token: "${{ steps.generate-token.outputs.token }}" - name: Scan for broken links uses: lycheeverse/lychee-action@v2 id: lychee env: GITHUB_TOKEN: "${{ steps.generate-token.outputs.token }}" with: args: --verbose --no-progress --exclude-mail './**/*.md' - name: Find link checker issue id: broken-link-check-issue uses: micalevisk/last-issue-action@v2 with: state: open labels: | broken-links - name: Update issue uses: peter-evans/create-issue-from-file@v6 with: title: Broken links detected 🔗 issue-number: "${{ steps.broken-link-check-issue.outputs.issue-number }}" content-filepath: ./lychee/out.md token: "${{ steps.generate-token.outputs.token }}" labels: | broken-links ================================================ FILE: .github/workflows/build-push-cache.yaml ================================================ --- name: Build Push Cache on: workflow_dispatch: {} push: paths: - "system/**" - "home/**" - "packages/**" - "overlays/**" - "flake.lock" - "*nix" jobs: define-matrix: runs-on: ubuntu-latest outputs: matrix: ${{ steps.set-matrix.outputs.matrix }} steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 id: cache with: path: /tmp/nix-cache key: nix-flake-inputs-${{ hashFiles('**/flake.lock') }} restore-keys: | nix-flake-inputs- - uses: cachix/install-nix-action@ab739621df7a23f52766f9ccc97f38da6b7af14f # v31.10.5 - name: Import Nix store cache if: steps.cache.outputs.cache-hit == 'true' run: nix copy --all --no-check-sigs --from /tmp/nix-cache - name: Build and Export .#gc-keep to Nix store cache run: | # remove everything before copying so the cache doesn't grow over time sudo rm -rf /tmp/nix-cache nix copy --no-check-sigs --to /tmp/nix-cache .#gc-keep - id: set-matrix run: | set -Eeu matrix="$(nix eval --json '.#ghActions.matrix')" echo "matrix=$matrix" >> "$GITHUB_OUTPUT" build-push: name: ${{ matrix.attrset }} needs: define-matrix runs-on: ${{ matrix.runner }} strategy: fail-fast: false matrix: ${{ fromJSON(needs.define-matrix.outputs.matrix) }} steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 id: cache with: path: /tmp/nix-cache key: nix-flake-inputs-${{ hashFiles('**/flake.lock') }} - name: Brutally add more storage to our runner uses: wimpysworld/nothing-but-nix@687c797a730352432950c707ab493fcc951818d7 # main with: hatchet-protocol: carve - uses: cachix/install-nix-action@ab739621df7a23f52766f9ccc97f38da6b7af14f # v31.10.5 - name: Import Nix store cache if: steps.cache.outputs.cache-hit == 'true' run: nix copy --no-check-sigs --all --from /tmp/nix-cache - name: Login to Cachix uses: cachix/cachix-action@1eb2ef646ac0255473d23a5907ad7b04ce94065c # v17 with: name: budimanjojo authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' skipPush: true - name: Build id: build run: | result=$(nix build .#${{ matrix.attrset }} --print-out-paths) echo "result=$result" >> $GITHUB_OUTPUT - name: Push run: cachix push budimanjojo ${{ steps.build.outputs.result }} ================================================ FILE: .github/workflows/check-flakes.yaml ================================================ --- name: Check Flakes on: pull_request_target: types: - opened - auto_merge_enabled push: paths: - '**.nix' - flake.lock jobs: check_flakes: runs-on: ubuntu-latest steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - uses: cachix/install-nix-action@ab739621df7a23f52766f9ccc97f38da6b7af14f # v31 with: nix_path: nixpkgs=channel:nixos-unstable extra_nix_config: | access-tokens = github.com=${{ secrets.GITHUB_TOKEN }} - run: nix flake check ================================================ FILE: .github/workflows/renovate.yaml ================================================ --- name: Renovate on: workflow_dispatch: inputs: dryRun: description: Dry-Run default: "false" required: false logLevel: description: Log-level default: debug required: false schedule: - cron: 0 * * * * push: paths: - .github/renovate.json env: LOG_LEVEL: debug RENOVATE_DRY_RUN: false RENOVATE_CONFIG_FILE: .github/renovate.json jobs: renovate: runs-on: ubuntu-latest steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - name: Generate Token uses: tibdex/github-app-token@v2 id: generate-token with: app_id: ${{ secrets.BOT_APP_ID }} private_key: ${{ secrets.BOT_APP_PRIVATE_KEY }} - name: Override default config from dispatch variables run: | echo "RENOVATE_DRY_RUN=${{ github.event.inputs.dryRun || env.RENOVATE_DRY_RUN }}" >> "${GITHUB_ENV}" echo "LOG_LEVEL=${{ github.event.inputs.logLevel || env.LOG_LEVEL }}" >> "${GITHUB_ENV}" - name: Renovate uses: renovatebot/github-action@v46.1.12 with: configurationFile: "${{ env.RENOVATE_CONFIG_FILE }}" token: "${{ steps.generate-token.outputs.token }}" ================================================ FILE: .github/workflows/update-nvfetcher.yaml ================================================ --- name: Update nvfetcher on: workflow_dispatch: {} schedule: - cron: 0 * * * * push: paths: - packages/nvfetcher.toml jobs: update-nvfetcher: runs-on: ubuntu-latest steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 # TODO: https://github.com/peter-evans/create-pull-request/issues/4228 with: persist-credentials: false - uses: nixbuild/nix-quick-install-action@2c9db80fb984ceb1bcaa77cdda3fdf8cfba92035 # v34 with: nix_conf: | keep-env-derivations = true keep-outputs = true - uses: nix-community/cache-nix-action@7df957e333c1e5da7721f60227dbba6d06080569 # v7.0.2 with: primary-key: nvfetcher-${{ runner.os }}-${{ hashFiles('**/*.nix', '**/flake.lock') }} restore-prefixes-first-match: nvfetcher-${{ runner.os }}- gc-max-store-size-linux: 1G purge: true purge-prefixes: nvfetcher-${{ runner.os }}- purge-created: 0 purge-last-accessed: 0 purge-primary-key: never - run: nix profile install github:berberman/nvfetcher - id: update_nvfetcher run: | cd packages echo "OUTPUT<> $GITHUB_OUTPUT nvfetcher | sed -n '/Changes/,/$!d/p' >> $GITHUB_OUTPUT echo "EOF" >> $GITHUB_OUTPUT - name: Generate token uses: tibdex/github-app-token@v2 id: generate-token with: app_id: ${{ secrets.BOT_APP_ID }} private_key: ${{ secrets.BOT_APP_PRIVATE_KEY }} - name: Create Pull Request id: cpr uses: peter-evans/create-pull-request@v8 with: token: ${{ steps.generate-token.outputs.token }} title: "chore(deps): update packages managed by nvfetcher" commit-message: "chore(deps): update packages managed by nvfetcher" delete-branch: true labels: nvfetcher committer: budimanjojo-bot <111944664+budimanjojo-bot[bot]@users.noreply.github.com> author: budimanjojo-bot <111944664+budimanjojo-bot[bot]@users.noreply.github.com> body: | ${{ steps.update_nvfetcher.outputs.OUTPUT }} - name: Automerge if: steps.cpr.outputs.pull-request-operation == 'created' uses: peter-evans/enable-pull-request-automerge@v3 with: token: ${{ steps.generate-token.outputs.token }} pull-request-number: ${{ steps.cpr.outputs.pull-request-number }} merge-method: squash ================================================ FILE: .github/workflows/update-sops-keys.yaml ================================================ --- name: Update SOPS keys on: workflow_dispatch: {} schedule: - cron: 0 * * * * push: paths: - .sops.yaml jobs: update-sops-keys: runs-on: ubuntu-latest steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 # TODO: https://github.com/peter-evans/create-pull-request/issues/4228 with: persist-credentials: false - uses: cachix/install-nix-action@ab739621df7a23f52766f9ccc97f38da6b7af14f # v31 with: nix_path: nixpkgs=channel:nixos-unstable extra_nix_config: | access-tokens = github.com=${{ secrets.GITHUB_TOKEN }} - uses: workflow/nix-shell-action@v4.0.0 with: packages: sops,findutils script: | export SOPS_AGE_KEY=${{ secrets.SOPS_AGE_KEY }} find . -type f -name \*.sops.yaml ! -name .sops.yaml -exec sops updatekeys --yes {} \; - name: Generate token uses: tibdex/github-app-token@v2 id: generate-token with: app_id: ${{ secrets.BOT_APP_ID }} private_key: ${{ secrets.BOT_APP_PRIVATE_KEY }} - name: Create Pull Request id: cpr uses: peter-evans/create-pull-request@v8 with: token: ${{ steps.generate-token.outputs.token }} title: "chore(sops): update sops secrets with new public keys" commit-message: "chore(deps): update sops secrets with new public keys" delete-branch: true committer: budimanjojo-bot <111944664+budimanjojo-bot[bot]@users.noreply.github.com> author: budimanjojo-bot <111944664+budimanjojo-bot[bot]@users.noreply.github.com> - name: Automerge if: steps.cpr.outputs.pull-request-operation == 'created' uses: peter-evans/enable-pull-request-automerge@v3 with: token: ${{ steps.generate-token.outputs.token }} pull-request-number: ${{ steps.cpr.outputs.pull-request-number }} merge-method: squash ================================================ FILE: .gitignore ================================================ .luarc.json result ================================================ FILE: .justfile ================================================ set unstable := true set shell := ['bash', '-euo', 'pipefail', '-c'] # `nix` is required requireNixToRun := require('nix') # the user running this needs to be a nix trusted-user for the extra-substituters to work export NIX_CONFIG := ''' extra-experimental-features = nix-command flakes extra-substituters = https://viperml.cachix.org https://budimanjojo.cachix.org extra-trusted-public-keys = viperml.cachix.org-1:qZhKBMTfmcLL+OG6fj/hzsMEedgKvZVFRRAhq7j8Vh8= budimanjojo.cachix.org-1:S0gy6IKTFXis9fFqEbVAS2zsvnZw/30NV2bWvGiN1YQ= ''' # we use nix shebang to get into a nix shell with packages that's needed for each recipe nixShebang := '/usr/bin/env -S nix shell --inputs-from ' + justfile_directory() realShebang := '/usr/bin/env bash -euo pipefail' [private] default: @just -l # update packages managed by `nvfetcher` [group('repository')] update-packages: #! {{ nixShebang }} nixpkgs#nvfetcher -c {{ realShebang }} cd ./packages nvfetcher # update SOPS keys [group('repository')] update-sops-keys: #! {{ nixShebang }} nixpkgs#sops nixpkgs#findutils -c {{ realShebang }} find . -type f -name \*.sops.yaml ! -name .sops.yaml -exec sops updatekeys --yes {} \; # do `nixos-rebuild switch` for current system [group('nix')] nixos-switch: sudo nixos-rebuild switch --flake .#{{ shell('hostname') }} # do `home-manager switch` for current system [group('nix')] home-switch: home-manager switch --flake .#{{ shell('whoami') }}@{{ shell('hostname') }} # create NixOS ISO [group('nix')] make-iso: nix build .#nixosConfigurations.nixos-livecd.config.system.build.isoImage # garbage collect all unused store entries [group('nix')] gc day='7': # garbage collect all unused nix store entries (system-wide) sudo nix-collect-garbage --delete-older-than {{ day }}d # garbage collect all unused nix store entries (user-wide) # ref: https://github.com/NixOS/nix/issues/8508 nix-collect-garbage --delete-older-than {{ day }}d # verify all the store entries # nix store can contains corrupted entries if the nix store object has been modified unexpectedly # we need to fix the corrupted entries manually via `sudo nix store delete ...` [group('nix')] verify-store: nix store verify --all # repair nix store objects [group('nix')] repair-store *paths: nix store repair {{ paths }} # show all the auto gc root in the nix store [group('nix')] gcroot: #! {{ nixShebang }} nixpkgs#eza -c {{ realShebang }} eza -lag /nix/var/nix/gcroots/auto/ ================================================ FILE: .sops.yaml ================================================ --- creation_rules: - path_regex: .*secret\.sops\.ya?ml$ key_groups: - age: - age1zeqkpfz7e3s207ynea0z0auc0mrct0pc7w4sh6j3d0c4qac3dahqj9ufdg - age1tdwlq9y4jgejkhasqwynw5uaen9xwatcvr52l70trsdxkeyvlesqjnh7l8 - age16p3zls5n0jks6amszwcuaqgl5dyuyf8k8wgeyrw562s5s88xtq3qq046fh - age1v52mx8gs4ephprep0wcc4j3fvvprppvs9vktf2p24yv52sqsf33sd5crk9 - age1k8ufac2s0gs6nh0xsfavafz062vd36petmyv6nwmg00z4a7s4gnsjtd837 - age1dcsm5awz8ekzchk7gsvndkc4kq65n2wzgavxtqe53vxdsfk9k9pqh3whru ================================================ FILE: LICENSE ================================================ This is free and unencumbered software released into the public domain. Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means. In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. For more information, please refer to ================================================ FILE: README.md ================================================
### My Infrastructure IaC ...Managed with NixOS :snowflake:  and chezmoi :robot: 
## :book:  Overview This repository contains my machine configurations in Infrastructure as Code style. This is possible thanks to combination of [NixOS modules](https://nixos.wiki/NixOS_modules), [home-manager](https://github.com/nix-community/home-manager/), and [chezmoi](https://www.chezmoi.io/). This repo is a [Nix Flake](https://nixos.wiki/wiki/Flakes) that you can import and use yourself but I don't recommend doing that. You should take it as an inspiration for your infrastructure instead. Please note that this is a Work In Progress, so stuffs may change without further notice and this README can get outdated. My goal is to have everything as reproducible as possible. For NixOS machines, everything is managed by NixOS modules and `home-manager`. As for non NixOS machines, I use combination of `home-manager` and `chezmoi`. I use `chezmoi` mainly to configure the system using [chezmoi scripts](https://www.chezmoi.io/user-guide/use-scripts-to-perform-actions/) while `home-manager` to configure applications in the userspace. ## :camera:  Screenshots **Sway** ![sway](https://user-images.githubusercontent.com/13085918/155890643-aa0adb3c-695f-497d-9d5d-d4bb61907cd0.png) **Tmux** ![tmux](https://user-images.githubusercontent.com/13085918/155890745-5fc83821-8008-4f91-8ebc-5852badeca22.png) **Fish prompt** ![fish](https://user-images.githubusercontent.com/13085918/155890660-57039ad8-d769-4044-ad38-a4b821c634e9.png) **Neovim** ![nvim1](https://user-images.githubusercontent.com/13085918/155890675-a3b8b3f8-479c-4fd0-9ed6-c74e0fc4de0a.png) ![nvim2](https://user-images.githubusercontent.com/13085918/155890676-38b95046-c35d-4db5-aa01-b9d8ee44f9d0.png) ## :package:  Modules - [Flake Parts](https://github.com/hercules-ci/flake-parts) to manage `flakes` outputs. - [home-manager](https://github.com/nix-community/home-manager) to manage my home directory. - [sops-nix](https://github.com/Mic92/sops-nix) is used to encrypt/decrypt my secrets safely. - [Disko](https://github.com/nix-community/disko) to declaratively manage disks. - [NUR](https://github.com/nix-community/NUR) for packages not available in the official NixOS repository. - [nixvim](https://github.com/nix-community/nixvim) to create reproducible Neovim package. ## :open_file_folder:  Directory structure The structure of this repository is highly opinionated. I shamelessly took the pieces I believe is the best from people and modified it. - [./flake.nix](./flake.nix) is the entrypoint for `nixos-rebuild` and `home-manager` commands. - [./flake.lock](./flake.lock) is the lock file for this flake, updated daily by [budimanjojo-bot](https://github.com/apps/budimanjojo-bot) powered by [Renovate](https://github.com/renovatebot/renovate). - [./flakeLib.nix](./flakeLib.nix) is where I put helper functions used in `flake.nix` file, this is where the magic happens. - [./lib](./lib) is where I put helper functions used in NixOS and `home-manager` modules. - [./packages](./packages) is where I put my own packages, updated daily by [budimanjojo-bot](https://github.com/apps/budimanjojo-bot) powered by [nvfetcher](https://github.com/berberman/nvfetcher). - [./overlays](./overlays) contains overlays for packages used in NixOS and `home-manager` modules. - [./shell.nix](./shell.nix) accessible via `nix develop` to have tools needed available in current shell. - [./system](./system) contains my own NixOS modules and per machine system configurations. - [./home](./home) contains my own `home-manager` modules and per user configurations. - [./chezmoi](./chezmoi) contains files used by `chezmoi`. ## :inbox_tray:  How do I bootstrap a new machine ### NixOS - Clone this repository in a directory inside the machine. - Edit `./flake.nix` file and add the new machine specs inside `outputs.flake.nixosConfigurations` schema. - Create `./system/hosts//default.nix` for the new machine and configure it. - Create `./home/budiman/hosts/.nix` for the new machine and configure it. - Run `git add .` then `sudo nixos-rebuild switch --flake .#` and I'm done. ### Non NixOS - Install Nix and enable Flake. - Edit `./flake.nix` file and add the new machine specs inside `outputs.flake.homeConfigurations` schema. - Create `./home/budiman/hosts/.nix` for the new machine and configure it. - Run `git add .` then `nix run nixpkgs.home-manager -c home-manager switch --flake .#budiman@` and I'm done. ## :pencil:  Neovim My `neovim` setup is packaged with [nixvim](https://github.com/nix-community/nixvim) and exposed at `legacyPackages.neovim` from this flake. You can run it directly if you have `nix` installed and `flakes` enabled with: `nix run github:budimanjojo/nix-config#neovim`. ## :fish:  Fish Fish is enabled using the `home-manager` module. On my non NixOS machines, `fish` is patched to work without system intervention and I have a `chezmoi` script that will switch the default shell for the user to `~/.nix-profile/bin/fish`. These are the plugins I'm using: - [Starship](https://starship.rs/) - [FZF Fish](https://github.com/patrickF1/fzf.fish) - [Puffer Fish](https://github.com/nickeb96/puffer-fish) - [autopair.fish](https://github.com/jorgebucaran/autopair.fish) - [fish-abbreviation-tips](https://github.com/gazorby/fish-abbreviation-tips) ## :abcd:  Fonts Starship requires powerline fonts to work. I suggest [Nerd-fonts](https://github.com/ryanoasis/nerd-fonts). The font in the screenshot above is using UbuntuMono Nerd Font Regular. Unifont is also for some glyphs to work. On non NixOS machines, `home-manager` will install these fonts automatically. ## :scroll:  Cheatsheet ### Sway/i3/Hyprland keybindings I use `Super` key for Sway/i3. `hjkl` keys are mapped to `left`, `down`, `up`, `right` arrow keys. `S` means Super key, [0-9] means number key 0 to 9. | Keypress | Description | | :-------------: | :----------------------------------------- | | `S+t` | Open terminal app | | `S+w` | Open browser | | `S+f` | Open file manager | | `S+grave` | Open rofi apps menu | | `S+Esc` | Open rofi apps menu | | `S+F4` | Close window | | `S+k` | Change focus to window above | | `S+j` | Change focus to window below | | `S+h` | Change focus to left side window | | `S+l` | Change focus to right side window | | `S+Shift+k` | Move focused window up | | `S+Shift+j` | Move focused window down | | `S+Shift+h` | Move focused window left | | `S+Shift+l` | Move focused window right | | `S+Ctrl+h` | Split opened windows horizontally | | `S+Ctrl+v` | Split opened windows vertically | | `S+Ctrl+q` | Toggle opened windows split | | `S+Tab` | Go to next workspace | | `S+Shift+Tab` | Go to previous workspace | | `S+Ctrl+t` | Toggle window border on/off | | `S+Ctrl+g` | Toggle gaps on/off | | `S+Ctrl+f` | Toggle fullscreen mode on/off | | `S+Ctrl+s` | Change container layout to stacking | | `S+Ctrl+w` | Change container layout to tabbed | | `S+Ctrl+e` | Toggle split layout to horizontal/vertical | | `S+Shift+Space` | Toggle window floating on/off | | `S+Space` | Swap focus between tiling/floating window | | `S+Shift+p` | Move current focused window to scratchpad | | `S+p` | Show/hide scratchpad window | | `S+[0-9]` | Go to workspace #[0-9] | | `S+Shift+[0-9]` | Move focused window to workspace #[0-9] | | `S+Shift+r` | Go to resize container mode | | `S+Shift+g` | Go to resize gaps mode | | `S+Ctrl+Del` | Go to logout mode | | `Printscreen` | Go to screenshot mode | | `S+Shift+c` | Reload configuration | | `S+Shift+e` | Exit | ### Neovim keybindings The prefix key is `Space`. You can override this using your custom `.vimrc.local` file. `` means you need to press prefix key first. If they are not in the table, that means it's using the default Vim keybindings. | Mode | Keypress | Description | | :-------------: | :----------: | :-------------------------------------- | | `Normal` | `w` | Save file | | `Normal` | `x` | Save file and quit | | `Normal` | `qq` | Quit | | `Normal` | `qa` | Force quit without saving | | `Normal` | `wq` | Save file and quit | | `Normal` | `Y` | Yank from cursor to end of file | | `Normal` | `Control+k` | Move to the split window above | | `Normal` | `Control+j` | Move to the split window below | | `Normal` | `Control+h` | Move to the left split window | | `Normal` | `Control+l` | Move to the right split window | | `Normal` | `s` | Open new horizontal split window | | `Normal` | `v` | Open new vertical split window | | `Insert` | `Control+k` | Move cursor Up | | `Insert` | `Control+j` | Move cursor Down | | `Insert` | `Control+h` | Move cursor Left | | `Insert` | `Control+l` | Move cursor Right | | `Normal` | `tn` | Open new tab | | `Normal` | `td` | Close tab | | `Normal` | `th` | Go to previous tab | | `Normal` | `tl` | Go to next tab | | `Normal` | `te` | Open new tab with current buffer's path | | `Normal` | `hh` | Jump back to older cursor position | | `Normal` | `ll` | Jump forward to newer cursor position | | `Normal/Visual` | `Tab` | Indent current line or selection | | `Normal/Visual` | `Shift+Tab` | De-indent current line or selection | | `Normal` | `lr` | Restart LSP client | | `Normal` | `fz` | Open Picker | | `Normal` | `ff` | Open Picker to find files | | `Normal` | `fg` | Open Picker to live grep | | `Normal` | `fc` | Open Picker to see git log | | `Normal` | `fb` | Open Picker to see opened buffers | | `Normal` | `fh` | Open Picker to find help | | `Normal` | `fk` | Open Picker to see keymappings | | `Normal` | `fe` | Open Picker to find lsp diagnostics | | `Normal` | `xx` | Toggle Trouble | | `Normal` | `xr` | Toggle Trouble to find lsp references | | `Normal` | `Control+f` | Toggle oil.nvim file manager | | `Normal/Term` | `Control+t` | Toggle floating terminal | | `Normal` | `pp` | Format buffer with null-ls | | `Visual` | `pp` | Range format buffer with null-ls | | `Normal` | `rn` | Do LSP buffer rename | | `Normal` | `gd` | Do LSP buffer get definition | | `Normal` | `gD` | Do LSP buffer get declaration | | `Normal` | `gh` | Do LSP buffer get hover | | `Normal` | `gr` | Do LSP buffer get references | | `Normal` | `gi` | Do LSP buffer get implementation | ### Tmux keybindings I override the default keybindings for Tmux to be more reasonable. Prefix key is `Alt+a` for local session and `Alt+z` for nested session. `` means you need to press prefix key first, `` means you don't need to press prefix key again after triggering it (I use unconventional way instead of `-r` flag so it will stay forever unless I press `Esc` key), `` means you must be in copy-mode first. The table below lists all the keybindings set. | Keypress | Description | | :-------------------: | :-------------------------------------------------------------- | |`h` | Move selection to left pane | |`j` | Move selection to pane below | |`k` | Move selection to pane above | |`l` | Move selection to right pane | |`H` | Resize current pane to the left by 2 columns | |`J` | Resize current pane downwards by 2 lines | |`K` | Resize current pane upwards by 2 lines | |`L` | Resize current pane to the right by 2 columns | |`Alt+n`| Move selection to next window | |`Alt+p`| Move selection to previous window | |`>` | Swap to next pane | |`<` | Swap to previous pane | |`Alt+s` | Split window horizontally with current pane path | |`Alt+v` | Split window vertically with current pane path | |`c` | Open a new window with current pane path | |`Esc` | Exit prefix key table | |`Space` | Exit prefix key table | |`a` | Enter copy mode | |`:` | Enter tmux command prompt | |`x` | Close current pane | |`X` | Close current window | |`b` | Move cursor to word beginning | |`e` | Move cursor to word ending | |`Home` | Move cursor to start of line | |`0` | Move cursor to start of line | |`End` | Move cursor to end of line | |`$` | Move cursor to end of line | |`PageUp` | Move cursor one page up | |`PageDn` | Move cursor one page down | |`v` | Begin selection | |`Space` | Begin selection | |`V` | Select a line | |`Alt+v` | Toggle box selection | |`y` | Copy selection to clipboard | |`Y` | Copy from cursor to end of line to clipboard | |`Esc` | Exit copy mode | |`q` | Exit copy mode | ### Zellij keybindings I'm migrating my tmux to [Zellij](https://zellij.dev/). I mimicked my tmux configuration to work in zellij but not everything works the same. Prefix key is `Alt+a`, I use the "switch to normal mode" in zellij to achieve this. `` means you need to be in normal mode, `` means pane mode, and so on. The table below lists all the keybindings set. | Keypress | Description | | :--------------------: | :--------------------------------------------------------- | | `Alt+a` | Swith to normal mode (act like prefix key in tmux) | | `Alt+s` | Create new horizontal split window and back to locked mode | | `Alt+v` | Create new vertical split window and back to locked mode | | `r` | Switch to renametab mode | | `h` | Move selection to left pane | | `j` | Move selection to pane below | | `k` | Move selection to pane above | | `l` | Move selection to right pane | | `>` | Move pane around | | `H` | Resize current pane upwards | | `J` | Resize current pane downwards | | `H` | Resize current pane to the left | | `L` | Resize current pane to the right | | `Alt+n` | Go to next window | | `Alt+p` | Go to previous window | | `c` | Open new tab and back to locked mode | | `x` | Close current pane and back to locked mode | | `a` | Open pane with $EDITOR and back to locked mode | | `Esc/' '/"\n"` | Switch back to locked mode | | `Alt+a` | Switch to normal mode | | `"\n"` | Switch to locked mode | | `Esc` | Confirm tab name and back to locked mode | ## :coffee:  Acknowledgements I wrote most of the codes by myself, but there are a lot of stuffs inspired by these repositories. - [MatthiasBenaets/nixos-config](https://github.com/MatthiasBenaets/nixos-config) for the awesome YouTube video introducing NixOS to me. - [viperML/dotfiles](https://github.com/viperML/dotfiles) for answering stupid questions on Discord. - [bjw-s/nix-config](https://github.com/bjw-s/nix-config) for being inspiration for my repo structure. - [truxnell/nix-config](https://github.com/truxnell/nix-config) for being inspiration for my flakeLib functions. ================================================ FILE: chezmoi/.chezmoi.yaml.tmpl ================================================ {{- /* Checks if running interactively */ -}} {{- $interactive := stdinIsATTY -}} {{- /* Template file for chezmoi config file */ -}} {{- $headless := false -}}{{/* true if this machine does not have a screen and keyboard */}} {{- $agekey := false -}}{{/* true if this machine has agekey to decrypt secret */}} {{- $hostname := .chezmoi.hostname -}} {{- if eq $hostname "budimanjojo-main" -}} {{- $headless = false -}} {{- $agekey = true -}} {{- end -}} {{- if eq $hostname "budimanjojo-ubuntu" -}} {{- $headless = false -}} {{- $agekey = true -}} {{- end -}} {{- if hasKey . "headless" -}} {{- $headless = .headless -}} {{- else if $interactive -}} {{- $headless = promptBool "headless" -}} {{- end -}} {{- if hasKey . "agekey" -}} {{- $agekey = .agekey -}} {{- else if $interactive -}} {{- $agekey = promptBool "agekey" -}} {{- end -}} {{- if $interactive -}} {{- writeToStdout "💡 Tip: you can re-enter your data with `chezmoi init --data=false`.\n" -}} {{- end -}} data: headless: {{ $headless }} agekey: {{ $agekey }} format: yaml encryption: age {{- if $agekey }} age: identity: /home/budiman/.config/sops/age/keys.txt recipient: age1zeqkpfz7e3s207ynea0z0auc0mrct0pc7w4sh6j3d0c4qac3dahqj9ufdg {{- end }} merge: command: nvim args: - -d - "{{ "{{ .Destination }}" }}" - "{{ "{{ .Source }}" }}" - "{{ "{{ .Target }}" }}" git: autoAdd: true ================================================ FILE: chezmoi/.chezmoiignore ================================================ {{- if .headless }} .config/alacritty/ .config/kitty/ .config/termite/ .config/wezterm/ {{- end }} ================================================ FILE: chezmoi/.chezmoiscripts/run_once_after_90-cleanup.sh.tmpl ================================================ #!/bin/bash echo -e "\033[0;32m>>>>> Begin Executing Cleanup Job <<<<<\033[0m" # This script will ensure that I fixes changes I have made in the past # For example, I used to install stuffs in /usr/local/bin but now I'm not anymore # So this script will make sure I cleaned up my old unused stuffs set -eufo pipefail function not_needed_executable () { local dir=$2 local path=$dir/$1 local current_path current_path=$(command -v "$1" || echo "notfound") if [ "$current_path" == "notfound" ]; then echo "\"$1\" not found, skip deleting" return fi if [ "$current_path" != "$path" ] && [ -f "$path" ]; then if [ "$EUID" -ne 0 ]; then sudo rm "$path" echo "deleted $path, not needed anymore" else rm "$path" echo "deleted $path, not needed anymore" fi fi } function not_needed_directory () { local dir=$1 if [ -d "$dir" ]; then if [ "$EUID" -ne 0 ]; then sudo rm -rf "$dir" echo "deleted $dir directory, not needed anymore" else rm -rf "$dir" echo "deleted $dir directory, not needed anymore" fi fi } function not_needed_file() { local file=$1 if [ -f "$file" ]; then if [ "$EUID" -ne 0 ]; then sudo rm "$file" echo "deleted $file, not needed anymore" else rm "$file" echo "deleted $file, not needed anymore" fi fi } not_needed_executable "kubectl" "{{ .chezmoi.homeDir }}/.local/bin" not_needed_executable "flux" "{{ .chezmoi.homeDir }}/.local/bin" not_needed_executable "kustomize" "/usr/local/bin" not_needed_executable "sops" "/usr/local/bin" not_needed_executable "fzf" "/usr/local/bin" not_needed_executable "starship" "/usr/local/bin" not_needed_executable "zoxide" "/usr/local/bin" not_needed_executable "rg" "/usr/local/bin" not_needed_executable "fd" "/usr/local/bin" not_needed_executable "bat" "/usr/local/bin" not_needed_executable "age" "/usr/local/bin" not_needed_executable "age-keygen" "/usr/local/bin" not_needed_executable "chezmoi" "{{ .chezmoi.homeDir }}/.local/bin" not_needed_executable "node" "/usr/local/bin" not_needed_directory "{{ .chezmoi.homeDir }}/.local/share/nvim/lsp_servers" not_needed_directory "{{ .chezmoi.homeDir }}/.local/share/aquaproj-aqua" not_needed_directory "{{ .chezmoi.homeDir }}/.local/share/fonts/UbuntuMono" not_needed_directory "{{ .chezmoi.homeDir }}/.local/share/fonts/Hack" not_needed_directory "{{ .chezmoi.homeDir }}/.local/share/fonts/unifont" not_needed_directory "{{ .chezmoi.homeDir }}/.local/share/fonts/unifont_upper" not_needed_file "{{ .chezmoi.homeDir }}/.config/fish/conf.d/nix.fish" if [ "$(command -v brew)" ]; then echo "uninstalling brew" /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/uninstall.sh)" fi echo -e "\033[0;32m>>>>> Finish Executing Cleanup Job<<<<<\033[0m" ================================================ FILE: chezmoi/.chezmoiscripts/run_once_before_10-setup-fish.sh.tmpl ================================================ {{ if (eq .chezmoi.os "linux") -}} #!/bin/bash set -eufo pipefail echo -e "\0033[0;32m>>>>> Begin Setting Up Fish Shell <<<<<\033[0m" set_default_shell() { local expected_default="{{ .chezmoi.homeDir }}/.nix-profile/bin/fish" local current_default current_default=$(grep "^{{ .chezmoi.username }}:" /etc/passwd | cut -d: -f7) if [ ! -x "$expected_default" ]; then echo "No fish binary found in $expected_default, skip changing default shell" return fi if [ "$current_default" != "$expected_default" ]; then echo "Changing default shell to fish" if ! grep -wq "$expected_default" /etc/shells; then echo "Adding $expected_default to /etc/shells" echo "$expected_default" | sudo tee -a /etc/shells >/dev/null fi chsh -s "$expected_default" fi } # Set fish as default shell set_default_shell echo -e "\0033[0;32m>>>>> Finish Setting Up Fish Shell <<<<<\033[0m" {{ end -}} ================================================ FILE: chezmoi/.chezmoiscripts/run_once_before_20-install-packages-archlinux.sh.tmpl ================================================ {{ if (eq .chezmoi.osRelease.id "arch") -}} #!/bin/bash set -eufo pipefail echo -e "\033[0;32m>>>>> Begin Setting Up Arch Linux Packages <<<<<\033[0m" packages=( curl luajit npm unzip ) aur_packages=( ) {{ if (not .headless) -}} packages+=( wezterm ) {{ end -}} echo "updating packages" {{ if ne .chezmoi.username "root" -}} sudo pacman -Syu --noconfirm {{ else -}} pacman -Syu --noconfirm {{ end -}} for package in ${packages[@]}; do if [ "$(pacman -Qq $package 2> /dev/null)" != $package ]; then echo "installing $package" {{- if ne .chezmoi.username "root" }} sudo pacman -S --noconfirm $package {{- else }} pacman -S --noconfirm $package {{- end }} fi done ## Install yay if [ ! $(command -v yay) ]; then echo "installing yay" {{- if ne .chezmoi.username "root" }} sudo pacman -S --needed --noconfirm git base-devel {{- else}} pacman -S --needed --noconfirm git base-devel {{- end }} git clone https://aur.archlinux.org/yay.git /tmp/yay cd /tmp/yay makepkg -si --noconfirm rm -rf /tmp/yay fi for aur_package in ${aur_packages[@]}; do if [ $(pacman -Qq $aur_package 2> /dev/null) != $aur_package ]; then echo "installing $aur_package from AUR" yay -S --noconfirm $aur_package fi done echo -e "\033[0;32m>>>>> Finish Setting Up Arch Linux Packages <<<<<\033[0m" {{ end -}} ================================================ FILE: chezmoi/.chezmoiscripts/run_once_before_20-install-packages-ubuntu.sh.tmpl ================================================ {{ if (and (eq .chezmoi.os "linux") (eq .chezmoi.osRelease.id "debian" "ubuntu")) -}} #!/bin/bash set -eufo pipefail echo -e "\033[0;32m>>>>> Begin Setting Up Ubuntu Packages <<<<<\033[0m" # List of ppa repositories to add repositories=( ) # List of packages to install packages=( curl unzip ) # List of snap packages to install snaps=() for repository in ${repositories[@]}; do ppa_repo_source=${repository#ppa:} if ! $(apt-cache policy | grep http | awk '{print $2}' | sort -u | grep $ppa_repo_source &> /dev/null); then echo "adding $repository repository to apt" {{ if ne .chezmoi.username "root" -}} sudo add-apt-repository -y $repository {{ else -}} add-apt-repository -y $repository {{ end -}} echo "false" fi done for package in ${packages[@]}; do if ! $(dpkg-query -W -f='installed' $package &> /dev/null); then echo "installing $package" {{ if ne .chezmoi.username "root" -}} sudo apt install -y $package {{ else -}} apt install -y ${packages[@]} {{ end -}} fi done # renovate: depName=wez/wezterm datasource=github-releases current_wezterm_version=20240203-110809-5046fc22 if [ ! $(command -v wezterm) ] || [ $(wezterm -V | head -n1 | cut -d" " -f2) != "$current_wezterm_version" ]; then echo "installing / upgrading wezterm" curl -Lo /tmp/wezterm https://github.com/wez/wezterm/releases/download/"$current_wezterm_version"/WezTerm-"$current_wezterm_version"-Ubuntu20.04.AppImage chmod +x /tmp/wezterm mv /tmp/wezterm {{ .chezmoi.homeDir }}/.local/bin/wezterm fi {{ if (eq .chezmoi.osRelease.id "ubuntu") -}} for snap in ${snaps[@]}; do echo "installing $snap using snap" {{ if ne .chezmoi.username "root" -}} sudo snap install $snap {{ else -}} snap install $snap {{ end -}} done {{ end -}} echo -e "\033[0;32m>>>>> Finish Setting Up Ubuntu Packages <<<<<\033[0m" {{ end -}} ================================================ FILE: chezmoi/.chezmoiscripts/run_onchange_after_80-setup-terminal.sh.tmpl ================================================ {{ if (and (eq .chezmoi.os "linux") (eq .chezmoi.osRelease.id "debian" "ubuntu")) -}} #!/bin/bash echo -e "\033[0;32m>>>>> Begin Setting Up Nix Applications <<<<<\033[0m" set -eufo pipefail # This script will ensure the applications installed by Nix is known by the OS # So `update-alternatives` program used by Debian based OS to open program can work properly # Run this script whenever the content of the host configuration for this machine changes # {{ include (print "../" "/home/" .chezmoi.username "/hosts/" .chezmoi.hostname ".nix") | sha256sum }} function add_nix_app_to_update_alternative() { local program="{{ .chezmoi.homeDir }}/.nix-profile/bin/$1" local priority=$2 local to_link="${3:-x-terminal-emulator}" local found found=$(update-alternatives --quiet --list "$to_link" | grep -x "$program" || true) # if program is not installed, proceed to remove it from the list # this is not really an efficient approach but there's no way to get `update-alternatives` to show binary that doesn't exist if [[ ! -x "$program" ]]; then sudo update-alternatives --quiet --remove "$to_link" "$program" return fi if [[ -n "$found" ]]; then local cur_priority cur_priority="$(update-alternatives --quiet --display "$to_link" | sed -n "s|^$program - priority \([0-9]\+\)$|\1|p")" if [ "$cur_priority" != "$priority" ]; then echo "updating $program priority as $to_link from $cur_priority to $priority" sudo update-alternatives --install "$(which "$to_link")" "$to_link" "$program" "$priority" return fi else echo "installing $program as $to_link with priority of $priority" sudo update-alternatives --install "$(which "$to_link")" "$to_link" "$program" "$priority" return fi } add_nix_app_to_update_alternative "contour" "9999" add_nix_app_to_update_alternative "alacritty" "9998" add_nix_app_to_update_alternative "kitty" "9997" add_nix_app_to_update_alternative "wezterm" "9996" echo -e "\033[0;32m>>>>> Finish Setting Up Nix Applications <<<<<\033[0m" {{ end -}} ================================================ FILE: chezmoi/dot_config/fontconfig/fonts.conf.tmpl ================================================ {{ if eq .chezmoi.hostname "budimanjojo-main" -}} serif Hack Nerd Font Mono Ubuntu Nerd Font sans-serif Hack Nerd Font Mono Ubuntu Nerd Font sans Hack Nerd Font Mono Ubuntu Nerd Font monospace Hack Nerd Font Mono Ubuntu Nerd Font true hintslight true true false {{ end -}} ================================================ FILE: chezmoi/dot_config/termite/config ================================================ [options] #allow_bold = true #audible_bell = false #bold_is_bright = true #cell_height_scale = 1.0 #cell_width_scale = 1.0 #clickable_url = true #dynamic_title = true font = UbuntuMono Nerd Font 12 # font = Hack Nerd Font Mono 12 #fullscreen = true #icon_name = terminal #mouse_autohide = false #scroll_on_output = false #scroll_on_keystroke = true # Length of the scrollback buffer, 0 disabled the scrollback buffer # and setting it to a negative value means "infinite scrollback" scrollback_lines = 10000 #search_wrap = true #urgent_on_bell = true #hyperlinks = false # $BROWSER is used by default if set, with xdg-open as a fallback #browser = xdg-open # "system", "on" or "off" #cursor_blink = system # "block", "underline" or "ibeam" #cursor_shape = block # Hide links that are no longer valid in url select overlay mode #filter_unmatched_urls = true # Emit escape sequences for extra modified keys #modify_other_keys = false # set size hints for the window #size_hints = false # "off", "left" or "right" #scrollbar = off [colors] # If both of these are unset, cursor falls back to the foreground color, # and cursor_foreground falls back to the background color. #cursor = #dcdccc #cursor_foreground = #dcdccc foreground = #C0CAF5 #foreground_bold = #ffffff background = #1A1B26 # 20% background transparency (requires a compositor) background = rgba(29, 31, 33, 0.9) # If unset, will reverse foreground and background # highlight = #2f2f2f # Colors from color0 to color254 can be set color0 = #15161E color1 = #F7768E color2 = #9ECE6A color3 = #E0AF68 color4 = #7AA2F7 color5 = #BB9AF7 color6 = #7DCFFF color7 = #A9B1D6 color8 = #414868 color9 = #F7768E color10 = #9ECE6A color11 = #E0AF68 color12 = #7AA2F7 color13 = #BB9AF7 color14 = #7DCFFF color15 = #C0CAF5 [hints] #font = Monospace 12 #foreground = #dcdccc #background = #3f3f3f #active_foreground = #e68080 #active_background = #3f3f3f #padding = 2 #border = #3f3f3f #border_width = 0.5 #roundness = 2.0 # vim: ft=dosini cms=#% ================================================ FILE: flake.nix ================================================ { description = "My NixOS configurations IaC"; inputs = { # nixpkgs and unstable nixpkgs.url = "github:nixos/nixpkgs/nixos-25.11"; nixpkgs-unstable.url = "github:nixos/nixpkgs/nixpkgs-unstable"; # flake-parts - very lightweight flake framework # https://flake.parts flake-parts.url = "github:hercules-ci/flake-parts"; # home-manager - home user modules # https://github.com/nix-community/home-manager home-manager = { url = "github:nix-community/home-manager/release-25.11"; inputs.nixpkgs.follows = "nixpkgs"; }; # sops-nix - secrets with `sops` # https://github.com/Mic92/sops-nix sops-nix = { url = "github:Mic92/sops-nix"; inputs.nixpkgs.follows = "nixpkgs"; }; # Disko - declarative disk partitioning and formatting # https://github.com/nix-community/disko disko = { url = "github:nix-community/disko"; inputs.nixpkgs.follows = "nixpkgs"; }; # NUR - Nix User Repository # https://github.com/nix-community/NUR nur.url = "github:nix-community/NUR"; # nvfetcher - tool to automate nix packages updates # https://github.com/berberman/nvfetcher nvfetcher = { url = "github:berberman/nvfetcher"; inputs.nixpkgs.follows = "nixpkgs"; }; # nixvim - Neovim distribution built around Nix modules # https://github.com/nix-community/nixvim nixvim = { url = "github:nix-community/nixvim/nixos-25.11"; inputs.nixpkgs.follows = "nixpkgs"; }; # Talhelper - A tool to help creating Talos Kubernetes cluster # https://github.com/budimanjojo/talhelper talhelper = { url = "github:budimanjojo/talhelper"; }; # Catppuccin for Nix - Soothing pastel theme for Nix # https://github.com/catppuccin/nix catppuccin.url = "github:catppuccin/nix/release-25.11"; # nixGL - A wrapper tool for nix OpenGL application # https://github.com/nix-community/nixGL nixgl = { url = "github:nix-community/nixGL"; inputs.nixpkgs.follows = "nixpkgs"; }; # tdarr-plugins - My Tdarr plugins repo fork # https://github.com/budimanjojo/tdarr-plugins tdarr-plugins = { url = "github:budimanjojo/tdarr-plugins"; flake = false; }; # cache-nix-action - Cache Nix Store in GitHub Actions # https://github.com/nix-community/cache-nix-action cache-nix-action = { url = "github:nix-community/cache-nix-action"; flake = false; }; }; outputs = { flake-parts, ... }@inputs: let # function to make `pkgs` for defined system with my overlays mkPkgsWithSystem = system: let localSystem = system; config = { allowUnfree = true; allowUnfreePredicate = _: true; }; in import inputs.nixpkgs { inherit localSystem config; overlays = builtins.attrValues (import ./overlays { inherit inputs config; }); }; flakeLib = import ./flakeLib.nix { inherit inputs mkPkgsWithSystem; }; in inputs.flake-parts.lib.mkFlake { inherit inputs; } { # systems for which you want to build the `perSystem` attributes systems = [ "x86_64-linux" "aarch64-linux" ]; # everything below `perSystem` will be enumerated and have `${system}` # added in the middle by `flake-parts` perSystem = { system, inputs', self', pkgs, ... }: { # override pkgs used by everything in `perSystem` to have my overlays _module.args.pkgs = mkPkgsWithSystem system; # accessible via `nix fmt` to format code formatter = pkgs.nixfmt-rfc-style; # accessible via `nix build .#` legacyPackages = import ./packages { inherit inputs' self' pkgs; }; packages = { # this is for cache-nix-action so stuffs don't get garbage collected # before I cache the nix store, mostly to not redownload inputs a lot gc-keep = (import "${inputs.cache-nix-action}/saveFromGC.nix" { inherit pkgs inputs; inputsInclude = [ "nixpkgs" "nixpkgs-unstable" "flake-parts" "home-manager" "sops-nix" "disko" "nur" "nvfetcher" "nixvim" "talhelper" "catppuccin" "nixgl" ]; }).package; }; # accessible via `nix develop` devShells.default = import ./shell.nix { inherit inputs' pkgs; }; }; # all the other flake outputs those don't require `${system}` # string will be here and handled by `flake-parts` flake = rec { ghActions.matrix = flakeLib.mkGithubMatrix { inherit nixosConfigurations homeConfigurations; }; nixosConfigurations = { # this is a NixOS live CD that has SSH enabled and some of my stuffs baked in # build with `nix build .#nixosConfigurations.nixos-livecd.config.system.build.isoImage` nixos-livecd = flakeLib.mkSystem { hostname = "nixos-livecd"; homeUsers = [ ]; baseModules = [ ./system/hosts ]; extraModules = [ { config.ghMatrix.enable = false; } ]; }; budimanjojo-main = flakeLib.mkSystem { hostname = "budimanjojo-main"; }; budimanjojo-nas = flakeLib.mkSystem { hostname = "budimanjojo-nas"; extraModules = [ inputs.disko.nixosModules.disko ]; }; budimanjojo-firewall = flakeLib.mkSystem { hostname = "budimanjojo-firewall"; extraModules = [ inputs.disko.nixosModules.disko ]; }; # this is my Oracle always free instance that I use as my WireGuard relay server # so my firewall can live behind a NAT and be fine in case I switch to a different # ISP that doesn't give me public routeable IP budimanjojo-oracle = flakeLib.mkSystem { hostname = "budimanjojo-oracle"; system = "aarch64-linux"; }; }; homeConfigurations = { "budiman@budimanjojo-ubuntu" = flakeLib.mkHome { hostname = "budimanjojo-ubuntu"; system = "x86_64-linux"; }; }; }; }; } ================================================ FILE: flakeLib.nix ================================================ { inputs, mkPkgsWithSystem }: let # my own custom lib will be accessible with `lib.myLib.` lib = inputs.nixpkgs.lib.extend (final: prev: { myLib = import ./lib { lib = final; }; }); # This module is passed to all `nixosConfigurations` and `homeConfigurations` so I can # specify whether a machine should be built and pushed on CI or not ghMatrixModules = { config, lib, pkgs, ... }: let cfg = config.ghMatrix; inherit (lib) mkOption types; defaultRunners = { x86_64-linux = "ubuntu-24.04"; aarch64-linux = "ubuntu-24.04-arm"; x86_64-darwin = "macos-13"; aarch64-darwin = "macos-14"; }; in { options.ghMatrix = { enable = mkOption { type = types.bool; default = true; }; system = mkOption { type = types.str; default = pkgs.stdenv.hostPlatform.system; readOnly = true; }; runner = mkOption { type = types.oneOf [ types.str (types.listOf types.str) ]; default = defaultRunners.${cfg.system}; }; }; }; in { mkGithubMatrix = { nixosConfigurations ? { }, homeConfigurations ? { }, }: let matrixSet = name: cfg: { system = cfg.config.ghMatrix.system; runner = cfg.config.ghMatrix.runner; attrset = if cfg.config ? system && cfg.config.system ? build then "nixosConfigurations.${name}.config.system.build.toplevel" else if cfg ? activationPackage then "homeConfigurations.${name}.activationPackage" else throw "${name} is neither `nixosConfigurations` or `homeConfigurations`"; }; filterAttr = attr: lib.filterAttrs (n: v: v.config.ghMatrix.enable) attr; mkList = attr: cfgs: builtins.attrValues (builtins.mapAttrs attr cfgs); result.include = mkList matrixSet (filterAttr (nixosConfigurations // homeConfigurations)); in result; mkSystem = { hostname, adminUser ? "budiman", # default user to login with `sudo` access system ? "x86_64-linux", nixpkgs ? inputs.nixpkgs, # myPkgs defaulted to the `legacyPackages` from this flake myPkgs ? inputs.self.legacyPackages.${system}, # homeUsers is list of home-manager users for the host # each user needs a `./home-manager//default.nix` file present # set it to empty list to disable home-manager altogether for the host homeUsers ? [ adminUser ], # baseModules is the base of the entire machine building baseModules ? [ inputs.sops-nix.nixosModules.sops inputs.home-manager.nixosModules.home-manager inputs.catppuccin.nixosModules.catppuccin ./system/_modules # all machines get my own NixOS modules ./system/hosts # entrypoint to all machines { config = { mySystem = { adminUser = adminUser; }; }; } ], # extraModules is additional modules you want to add for the host extraModules ? [ ], }: let mkHomeUsers = lib.optionals (homeUsers != [ ]) [ { config.home-manager = { users = builtins.listToAttrs ( builtins.map (name: { name = name; value = { imports = [ ./home/${name} ]; config.myHome = { username = name; }; }; }) homeUsers ); useGlobalPkgs = true; useUserPackages = true; extraSpecialArgs = { inherit inputs myPkgs hostname; }; sharedModules = [ inputs.sops-nix.homeManagerModules.sops inputs.catppuccin.homeModules.catppuccin ./home/_modules # all users get my own home-manager modules ]; }; } ]; in nixpkgs.lib.nixosSystem { inherit system lib; pkgs = mkPkgsWithSystem system; specialArgs = { inherit inputs myPkgs hostname ; }; modules = baseModules ++ extraModules ++ mkHomeUsers ++ [ ghMatrixModules ]; }; mkHome = { hostname, username ? "budiman", system ? "x86_64-linux", nixpkgs ? inputs.nixpkgs, # myPkgs defaulted to the `legacyPackages` from this flake myPkgs ? inputs.self.legacyPackages.${system}, }: inputs.home-manager.lib.homeManagerConfiguration { inherit lib; pkgs = mkPkgsWithSystem system; extraSpecialArgs = { inherit inputs myPkgs hostname ; # need to have this because a lot of my modules depends on `osConfig` argument osConfig = { }; }; modules = [ inputs.sops-nix.homeManagerModules.sops inputs.catppuccin.homeModules.catppuccin ./home/_modules # all users get my own home-manager modules { imports = [ ./home/${username} ]; config.myHome.username = username; } ghMatrixModules ]; }; } ================================================ FILE: home/_modules/_default/default.nix ================================================ { config, ... }: let myHome = config.myHome; in { imports = [ ./nix.nix ./sops.nix ]; config = { home = { username = myHome.username; homeDirectory = "/home/" + myHome.username; stateVersion = "23.11"; }; programs.home-manager.enable = true; catppuccin = { flavor = "mocha"; accent = "mauve"; }; }; } ================================================ FILE: home/_modules/_default/nix.nix ================================================ { lib, osConfig, pkgs, inputs, ... }: let isNixos = lib.myLib.isNixos osConfig; in { # only enable when we are not on NixOS nix = lib.mkIf (!isNixos) { package = pkgs.nix; registry = { stable.flake = inputs.nixpkgs; unstable.flake = inputs.nixpkgs-unstable; }; gc.automatic = true; settings = { nix-path = "nixpkgs=${inputs.nixpkgs.outPath}"; extra-substituters = [ "https://budimanjojo.cachix.org" ]; trusted-substituters = [ "https://budimanjojo.cachix.org" ]; extra-trusted-public-keys = [ "budimanjojo.cachix.org-1:S0gy6IKTFXis9fFqEbVAS2zsvnZw/30NV2bWvGiN1YQ=" ]; experimental-features = [ "nix-command" "flakes" ]; auto-optimise-store = true; keep-outputs = true; keep-derivations = false; cores = 0; max-jobs = "auto"; }; }; } ================================================ FILE: home/_modules/_default/sops.nix ================================================ { lib, osConfig, pkgs, config, ... }: let isNixos = lib.myLib.isNixos osConfig; in { config = { home.packages = lib.mkIf (!isNixos) [ pkgs.sops pkgs.age ]; sops.age = { keyFile = "${config.home.homeDirectory}/.config/sops/age/keys.txt"; generateKey = true; # generate the key if it doesn't exist }; }; } ================================================ FILE: home/_modules/browser/firefox/default.nix ================================================ { config, lib, pkgs, options, ... }: let inherit (lib) getExe mkOption types mkPackageOption mkIf ; cfg = config.myHome.browser.firefox; rofiExe = getExe config.programs.rofi.package; firefoxExe = getExe config.programs.firefox.package; # need a wrapper to replace Firefox profile manager # https://github.com/nix-community/home-manager/issues/3117 rofiWrapper = pkgs.writeShellScriptBin "rofi-firefox-wrapper" '' cfgFile=$HOME/.mozilla/firefox/profiles.ini if test -f "$cfgFile"; then i=0 profiles=$(grep 'Name' "$cfgFile" | sed 's/Name=//g') while read -r; do i=$((i+1)) done < <(echo "$profiles") if [ $i -gt 1 ]; then options=$(echo "$profiles" | sed ':a; N; $!ba; s/\n/\\n/g') chosen=$(echo -e "$options" | ${rofiExe} -dmenu -P "Select Firefox Profile") if [ "$chosen" != "" ]; then ${firefoxExe} -P "$chosen" fi else ${firefoxExe} fi else ${firefoxExe} fi ''; in { options.myHome.browser.firefox = { enable = mkOption { type = types.bool; default = false; description = '' Enable Firefox Web Browser. This module include a wrapper that will open up `rofi` profile selector when you have more than one profile or open Firefox normally otherwise. The wrapper can be accessed by pressing `Super+W` on the keyboard in i3, Hyprland, and Sway window manager. ''; }; package = mkPackageOption pkgs "firefox" { }; profiles = mkOption { type = options.programs.firefox.profiles.type; default = { "" = { id = 0; name = "${config.myHome.username}"; isDefault = true; }; }; }; }; config = mkIf (cfg.enable) { programs.firefox = { enable = true; package = cfg.package; profiles = cfg.profiles; }; wayland.windowManager = { hyprland.settings.bind = [ "SUPER, w, exec, ${getExe rofiWrapper}" ]; sway.config.keybindings."Mod4+w" = "exec --no-startup-id ${getExe rofiWrapper}"; }; xsession.windowManager.i3.config.keybindings."Mod4+w" = "exec --no-startup-id ${getExe rofiWrapper}"; }; } ================================================ FILE: home/_modules/default.nix ================================================ { lib, osConfig, ... }: let inherit (lib) mkOption types myLib mkDefault ; in { imports = [ ../../system/_modules/myHardware.nix # base module enabled for all users ./_default ./browser/firefox ./editor/neovim ./homelab/kubernetes ./multiplexer/tmux ./multiplexer/zellij ./programs/beeaccounting ./programs/chezmoi ./programs/fontconfig ./programs/obs-studio ./programs/go ./programs/qmk ./programs/yamllint ./services/opencloud-client ./shell/dircolors ./shell/fish ./shell/git ./shell/lf ./shell/nix-direnv ./shell/starship ./terminal-emulator/alacritty ./terminal-emulator/contour ./terminal-emulator/kitty ./terminal-emulator/wezterm ./windowmanager/add-on/blueman-applet ./windowmanager/add-on/dunst ./windowmanager/add-on/gtk-theme ./windowmanager/add-on/nm-applet ./windowmanager/add-on/nwg-bar ./windowmanager/add-on/pasystray ./windowmanager/add-on/picom ./windowmanager/add-on/py3status ./windowmanager/add-on/rofi ./windowmanager/add-on/screenshotter ./windowmanager/add-on/swayidle ./windowmanager/add-on/swaylock ./windowmanager/add-on/terminal-emulator ./windowmanager/add-on/theme/tokyonight ./windowmanager/add-on/waybar ./windowmanager/add-on/xdg ./windowmanager/hyprland ./windowmanager/i3 ./windowmanager/sway ]; options.myHome = { username = mkOption { type = types.str; default = ""; }; isWayland = mkOption { type = types.bool; default = false; }; }; # defaulting to use the system config on NixOS machine config.myHardware = mkDefault (myLib.copyFromSystem "myHardware" osConfig); } ================================================ FILE: home/_modules/editor/neovim/default.nix ================================================ { config, lib, pkgs, ... }: let cfg = config.myHome.editor.neovim; in { options.myHome.editor.neovim = { enable = lib.mkEnableOption "Neovim"; package = lib.mkPackageOption pkgs "neovim" { }; }; config = lib.mkIf (cfg.enable) { home.packages = [ cfg.package ]; programs.fish = { shellAliases = { vimdiff = "nvim -d"; }; shellAbbrs = { vi = "nvim"; vim = "nvim"; }; }; home.sessionVariables = { EDITOR = "nvim"; VISUAL = "nvim"; MANPAGER = "nvim +Man!"; }; }; } ================================================ FILE: home/_modules/homelab/kubernetes/default.nix ================================================ { config, lib, pkgs, myPkgs, ... }: let cfg = config.myHome.homelab.kubernetes; in { options.myHome.homelab.kubernetes = { enable = lib.mkEnableOption "Kubernetes tools for homelab"; }; config = lib.mkIf (cfg.enable) { myHome = { programs.yamllint.enable = true; shell = { git.enable = true; nix-direnv.enable = true; }; }; sops.secrets = { kubeconfig = { sopsFile = ./secret.sops.yaml; path = "${config.home.homeDirectory}/.kube/config"; }; talosconfig = { sopsFile = ./secret.sops.yaml; path = "${config.home.homeDirectory}/.talos/config"; }; }; home.packages = with pkgs; [ age envsubst fluxcd go-task jq just kubectl kubernetes-helm kustomize nodePackages.zx talosctl myPkgs.kubectl-rook-ceph myPkgs.talhelper ]; programs.fish.shellAbbrs = { # kubectl k = { position = "anywhere"; expansion = "kubectl"; }; kgp = { position = "anywhere"; expansion = "kubectl get pod"; }; kgn = { position = "anywhere"; expansion = "kubectl get nodes"; }; kl = "kubectl logs"; kdp = "kubectl describe pod"; # flux f = { position = "anywhere"; expansion = "flux"; }; fgk = { position = "anywhere"; expansion = "flux get ks"; }; fsa = "flux suspend kustomizatin --all"; fra = "flux resume kustomization --all"; # talosctl t = "talosctl"; # talhelper th = "talhelper"; }; }; } ================================================ FILE: home/_modules/homelab/kubernetes/secret.sops.yaml ================================================ kubeconfig: ENC[AES256_GCM,data:pfVS55vfo3YNYpYLfQSot40QWl2VEJaVsO6SfbI2284uO/OLsK6VD/6XQBT9UhzPU8dbJolYePEfeaRIcU5v1qqX6ZsZRx4SSMuGw7yEjjSzVY3OdRIWeochIa5pUD8ke/l9g+3HfJxoD+cOOyobGw22okKqQ+VmHizl/45EoKp2pJa7qB9TtW/HD7HhNxmVKtGGfxuI5uvz46kFGkDviaRPKcrO4etwDSwLPuQ8OZWAAUilwZKgA9jCo6lRWtPdpQv9TDb4SGjWKeGiKo2Ojl2zUcxmKIBjbRtL29AwkYDcLWmzg+pTgHDNp9V8NLv/bIB6zRzdJQx4Ay/JAwskxy+7Zy1f+H3yj0c+Eh45JAVFuPdIqQoPBSjZBt3Rc0SOJXXg1yjT3OAk0EDkeepNBwNHdUSzYNvWDc+PXJDwbpDm+ivG3nYIGgUJ0vcWWZ9PpRDQ+GZ64wYOH7fCPR9JlesusdaCPqj0roSqXTBmsE42MlHFKYWTtOeNbWdpMr4EVmHhLxyPc/6pC3pBdxy430Ql0zfJrillLp55Qra+VDRg/ojXS307nT/eD/ItVatS+fopvh19Pw6pfXir2ouRsDfUr/hZ2inA9kYGmWHD5R1pzCazxywQ0lcIz0Rfvw1R07mF0bAVkUU+NSlALiCbfvWp4bhnS1SOBzZlW4Xdy1BWYnc8NfJIaDocNi8jISPRV0EjDrAj6SsjZ5VUdvD972XlTXM6/jfPtNr0sWwCMnquT+yvjLFKneqntgBk+zIdjj8d3w3l/Om2n2MOH3mxmweo1h9Oa8X/YYv43lZ0YxSdROWvVZY6SgYTTVj+/rCIm1yiGmwK0jGLEvHsWnnn1ROPZeDDFJ9+nB2pp79yn+c+WyoqOQLnfLSMVMrWnEBBaWIGVc7/mHhD/beUAl/ScLXkeyG3B1QOIiShjWtRsRpXZRz8wpl1JXNNnt6ybKpQIyBo4TPiRRzfdaoFbpk0OP2qIAnV3MQ7VcUNBNd5PbIK+/+jesvSQrEZAy2sCU9LBIOBEwd3tvfQOzPoY0V4y0rTHgIY8AWMBHA84JurzlbmHZ12OSVudn0LEca7xApUffyxe2TmesA2xdbTXAPTIAxS1CEc8UJeAjvDovZNZ+an8KAO8DLSHk0n7mYIDtCMtGNogZ2YYoDAhYppS65fUEkT8KRq1XCNTo6ZHGVfej3J6JCIoO5vtU/Yjq6O0TbEtYZyN5rlMGJJxK7zsjaBqlZFI/hYNehOwRb11kBjIC73grPGKMsgMhSlBT0nNT4YRfaUOr98sVheaYDpNVdqRhIDyLSD3QPTpky3VSdD4KiQheuyYUgFW7JvcF8GBknSQYR8PVWP3S3eAqj2YS2BxR0K7CeWK9PJYxVqAnkdjhhqk7CglFlS2UK76ER0gXIeRkpEWt4PaWDyMGJ10awOtYg6H0YUhTpKFs+r20XpC4bkoAfJajo3wnw30IwlqxIlL6M19wF87sc6lxzzifdJWTw78y1dKPorSGMBVAbbvaIF4gEEPd6W3dCyumMOaENayb3BEouABANGvhoQN4UWPI7tsYRnrx+s2yZBU1/tVtVQhGAobQcyeubbiERHRLbN+2q/kRleGKLQi2dG46pqNjYgYBG7QDIR7tg8c7FpNVx54SLhyFjvTnunGsSg5aRQKPE+NctreDhve4ILSog3cyrB6MBhhumIXg6KGhta9Sd/y78errveNp9FRQaKvvwgB1EShP2Qmj4epCZJCukiY8pPZA5dNQ0n+BC2E2A/MNaYv+OtQYzWGUs4+XGcvuJcQTH/M9MrLqNZSP9romUdhImFGE7vHpERDGueLkanDBX3cXU+d93FZ64YyPhkrv8VWY5EneCWKePiEkSuKiJp3W39qxNn4dzFFMcJHh51DA1dOEwevWrHfzPsr2QQjJ89Za7K4UzoU+Z4Ek0zm/LBG46rQ1opr2LVd17/km/yPNRivscIyDN1o+RH0cQtPPUhou6UpnA5gmMVXAtmHfXSjibE/ttdZ1dkzwnVDJAd55vmKEvQulE5e5g9WoaXo0Ysy58Kehp1g2tEjpA1uqrCbjqHfj9GvifDciAJa822a7NUwKu8ktW9vBpx/CPamnXkij+GFLm0uYWZdKrvcF47WqUXQ8Q4h7Y4ze5NheTJQenRMpjo65TyQmRqJlsZDc/Ie+Q9+9oarCS4o0aLvSedfDrd6tvKEExQ5ih6dtYazeYcZdT2ordG8G3Neww1EvKR6M+/+EMlwFTE/FyrX8Uk4kSFz25u7C+dp92O9B9WlvINHjBCkmJEOSXBzcr9sTxJLifAIIAfx8KCVZygGaEMV6JT1luXch7eme9CbV/nauQwhp1paqjFafMUG8B2SW17Ho9dkEW25HI+uDmQ1IvAUCRVhY3ngRZGpUKoaYVR3ZgEPsIF1F0dN9edQbjhog9rG0liD5Y0ZVGb9BKrSqUwHWp4zkAPW5NAIvxO9sq9/OfF/Jo3Qq45klbd/4n/w0M//R425HGwAs4JiPfBZctDcmZncqJdqULvUPRg9mjUWF8PagsJaVHW1Ukp8b+R7D5xLa+brc0+4QMs9VxgOah+BBJOAAq20IPjkeIwwc+EQI3/tyqiO3iCoNFE4hJssM5je3yAikimKYF1Do7JA9K/PrRoVtbkTlq6HrQBG9UM/CIeIsU1szkv55anWDhC/RcFB9J6WnVN6LLY/ZXkGM26D9hJui7oTsbyEVZUNwSB2CboNXB+EXpi79O+4RFJAnCh1OHYzbNEOOj6Zkju2E/Dk+8OttpOCrfRgk1n5xNsveP6fC8gj0vSrVimrWhnG2GCATL/0muwOW6nYnw1sLtCwiMKzemF3DpLYcFBWwQhwzyDk0z6raOgSErcH+j9UJsZEJNUfwt1mXS7yqWADLGvsnBMkKdbyGMUNr0w0BKwnK5ENS9gL6z5BSD32Zk5T7DB+JLgrn47Ry987tKUQQRqCrME2UUUnuk5UeUVWMdCzXME0lVsRqkInI2MTegoCkbT/HBK101+PjdLoNYPm9sHT6omfGdxlzNC9rnah15gXB2lCm4bliQ8+flq,iv:Dikm/1W8siNC4rK9elXCAWJvGNaGJtPRF+fL9kOBYLU=,tag:swr75OwWPjUwr8eMgBooEw==,type:str] talosconfig: ENC[AES256_GCM,data:zukEgmExjlNc1gvDQXedX0uCQgmsaLuIpaVJQLlLNBLGA4Jr9JZ86oKlVR9dDqhnZ22Y08KvNNZs0fG//nttr8GBU94a9N0mbRU+DhVCho3PfAc4Iza0ztZZSEjofIFmR8q1LTKQhbqmnGGF2JB45kos6+do46MpfxMIJndpwJqRY/IAAeeB5pCs0aF8HxPF2ehmzcKXEZMBb5g2RJtP9G/z8xlzqgrHDnvbichR13LCpBcoV6X23FYs93dQxrVwskW+39STa5R/570deUHwQLUzC1kmjqh5ktMvpzSJtzAzpeyM6gJMzMsdpqFb5pzX9myuulWF/boU6qa1qH6/XBOtoT005Dayi1L7sMUgRcuXCGOxCJUCt/KORHFqXL2kNjkwXjIBUT4YkAjsq3110pEXRhLUPr3dKMTxdrrWcpeCk13DqroSyYJKo/DYoMuw5YJDDQ7Nbc3rFL9aaOMwoCaT7r0lZgKThwR9pOPerMxODhxHj0FzQclvZzxITZ25SKnsJnRIbVfm7pIWIRnBaf3ZhuLgZMCzeTiCz4iBwakoUmHmw3acW+C7RP4KVZXN/Qz8HQHxGsKD3ITnextTDIxkFSeoC8JJHLkRy8aCROv82xwR2YrfXxGRnxlyHfY+CaVRl0Ff5Q7nhOk1EJMbiLtP39S7e6HEKpk8z9eP99QLatIhNMfP1uVhdEbaJxyNnrVAokaG4RrxXf3zJdrd9vqpCYVmRScVht3kjmh7L5OXUd8JZ9t9gpFtlf7AfkmFQv2J9IFZDHGB9PmXf4iW+NX3+b+2BkW6F57z6cYddHvIlTcgJGWYLGSd6EHiPbM4Y9V9yWbwGlBQIJCN/Vm3/M3z1EKZCREMUouUbKDnLr4/sowYkb4m9OcBsnMcCuzVe374HmoRCKb3WiWzT7+JabkK0kRcp/PcS69mg8F5B+UyHUwH2tx2WeS2OBR7pc5MeEL142PW8iIalSI3dFfZUw4xMTvePiQ0vku7hxcg0cSysQXtgamXHVhzxLW8utbiWJINvt4etRBhUVeFHWW1kaPFSdQNbDyDH3EJyy6gKEL5KYNQqHLBo0kybJVEOti6dwC9xf1hdH0VS53Ssh2RPE9bKPBIRy7aag+DpZLHgiZijVcnWqdyd5rBRU3dA+j9qzu4jdHqm64puKUG57g/1yN+8hfoaYiOV1XdwZ5s8YcWtPOX4ufRoJAKMIGDG2EVALYurasdv/r+2r1zuoWI/yDM3i85WefpHz47Wr0A4WagzxlMA9pGBZui5UxoLfaFdL5WcPkh7RRkF6bjlnERQKEKanf7W/1Vb6szHChXZYf1XBb4qjAhmwhqXjmG8NvdY8kLte6RjZfU2GQuWe1nyGbIOGeqEy22XO571crOuusZnnIHVGmrymj9WJKPN8VSsoiMpTN7ZukNKrfZ9fpImwy3dqrMZhUuU9PxNaaJxKy8UGNxRDXG3KzWCqm9DakBJYtwgaotSjrQAzYj0f0DvjTACvJRyUUSGo9kdFyRYr836qVP1C2/bF1FYqwFh7a0m/WUZV+GUG2aUTlV6lF55+pgmSFSv3VjxSVvoEzQdqXSN40AaVoEf3CBHAjtMUqt00JQWZGAFQ78qpQdr5Xo8RPUXsmmGAnVY/bmnvtlM7Vs0dbEt7RPrgvMbIxdJvDZXXv0z01pFueY8g/fibc/TIDQ2k/if5xWbSAYaWG8K8KnTIK91hHr8GYhkYkYP5nWfzr9txEAvy71asngO6aVR/ZMQcot0GYkW1eiipjuAuxR6o6wqn5fDRO1ZsE4RFa67D7Mu7eZHNAm75iRd2ECp2EWqm6wvoKjnXSratlPH3s58cTwzuMJYxa4EmM0t9ZdePfQPQ00xqshakBczvu+D1Wz6tOq5V+kRWncjko49uPMfmbyYBuBOF7AhFK+MpFFuc7JWUYW6YW0K4FgZkt0pE6hwFWyneIoDFSwcMVKs0X6JAu3TX7xL8ssT0StDvRWIVR0MQkmnGEho5Im9w8W8UXnzopl/91prX5ACPfsvgsxYOEIaCkyH5EkEsaMAUBWHhRadkXZBWaHYqChruc23y78sO3xfRY6YnFOrnf2kFURF6pFE7XYNBOE0fb+P+QnvdxFCsQlC9xcBYuC0MAJoKyYbdnwyb52a/GDPpvzKmC7BHjgh3FW9riHY4O4CgXLcYO0DX9J92KE67fjArXQufjU2xiUKYS6zHb0w5jf4lorgeVU/r3RcEp3jKdGmp2/mMXJtqM823zdy9RKgmGlgg==,iv:u+cocP8bYeHCUtjuQrjfrQsoUMV4c3p3mDVYS6Hd07o=,tag:TsTIFGKw0t/0YB6Efl42Mw==,type:str] sops: age: - recipient: age1zeqkpfz7e3s207ynea0z0auc0mrct0pc7w4sh6j3d0c4qac3dahqj9ufdg enc: | -----BEGIN AGE ENCRYPTED FILE----- YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB3UGVVYkw4ellQNU53OXRQ YmM3NjhNcFFmMThKMVBuNHZ2eElVYTlSWlZvCmZ0bSsvSjN5N1Vpd21BVmlrOWRm aXJzS0ZlQjduWlNiQjJabXA4UG9HQlEKLS0tIFQ5bUFtWCtJcCtSUGNCODk5OVJB azQ2bjFCd2V1VThBNERVb2V4VVkvMXcKqMOvVXQeb2RLQ+ce2s/YJ+gUoCbksFys WXLUNGwYF1rKOGdtWp3UBajpKIE2w4Gr6LAPm2Fn0raiVIWw4ucxVA== -----END AGE ENCRYPTED FILE----- - recipient: age1tdwlq9y4jgejkhasqwynw5uaen9xwatcvr52l70trsdxkeyvlesqjnh7l8 enc: | -----BEGIN AGE ENCRYPTED FILE----- YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBwQUg4YlMwYzUyc0ZUYStj dFJpeUJtbjZwdjI1SjMrMlVYT1I5TGdNWW5rCm1CaVpPME0vOEpSckc4azlTSlJj VlVReCtObXNLQWtVem9BMHF0Z0NlWjQKLS0tIGtiMjhpM0s1TnVWTEZudmM0aWo4 b2c1UUIvZHhndGF3NFF1V2dZYUxwRHMKlAvYwXo53J93mEbTShaYEHYgAfLmYz9R rZNxgFI7at1ayTpFQabtJDWmmKMbZK7hOZWrWnYGJ5FEAI144x8aFA== -----END AGE ENCRYPTED FILE----- - recipient: age16p3zls5n0jks6amszwcuaqgl5dyuyf8k8wgeyrw562s5s88xtq3qq046fh enc: | -----BEGIN AGE ENCRYPTED FILE----- YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA4WEZTYTcwajd1SEhYTUJZ L2xvZHdoaG5DTTB0UndyRS8xZnRlclB2KzFRCjlIQXdQTjA5eEE2ZmpVTWhzZk5W c0xQOCtFRURGMGxVa1d2VWUyaGlnU2sKLS0tIHUzVmJRcVVyQ1pqV3ZRSUZydDJw aGliY0ZIVlJPOHlzRTQzQWpzYTc1a1EKgmvkBFxGdcQbrRvARsq90WIUbqyx7lbt ZNkd2FHqFuGECe2L31AaW3pYj1x33EbI8+Dh6jU6OjZ7cuD1bwjcjA== -----END AGE ENCRYPTED FILE----- - recipient: age1v52mx8gs4ephprep0wcc4j3fvvprppvs9vktf2p24yv52sqsf33sd5crk9 enc: | -----BEGIN AGE ENCRYPTED FILE----- YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBkUkd5VjNES1UxMlhqeEda T3ozZ1BBdk84M2pCOVhYWTBlZHFSTzViTVVVCmorNkZ3ZTRmcWpSaWZJMWFFb2Nu OUpVQ3d0bFhPREdzSTVXQmFzQ2gyMkEKLS0tIFZuRWQ4U0dBbnZYTy82WVhsckhT WFRqcXV3ZkR4c1dtd3I0QTM5WC9HQ0EKECenrjPSPpZW/0kPZj31pA2P4rJ5Tvxx Fim4Ob8aUqAGjpI+0so+6imQfQ9a6b1jCOZMuwrbqbR93wfd7sYL1w== -----END AGE ENCRYPTED FILE----- - recipient: age1k8ufac2s0gs6nh0xsfavafz062vd36petmyv6nwmg00z4a7s4gnsjtd837 enc: | -----BEGIN AGE ENCRYPTED FILE----- YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBKd1ExR0c5SUpidUl6N2JD WE5JSzJaUkE1RE1rWlZ2V052Rld6SVdwZldJCk5LUTByMnhhV1lzcjVkYW1CclFp SDNrK1RZVzhjNStoRXUyOGt2WVNkRFUKLS0tIC9kVm01M3JTTlRRak9nblZRTDIw TzJPY2NQYkk4RVVWR1dlamVadGJNU2cKkEN8xZ2Oz/f3oA24WBzpVCMLyz4HsZMh KVnwkjo746csT+vM9Z7qzJw0Xo/dG7d/pv67AcFRZFp4HB4B5qvyEg== -----END AGE ENCRYPTED FILE----- - recipient: age1dcsm5awz8ekzchk7gsvndkc4kq65n2wzgavxtqe53vxdsfk9k9pqh3whru enc: | -----BEGIN AGE ENCRYPTED FILE----- YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBiMnR1TDNKV3pGeEg1ek1R M1NGZ2ZoeWw3aGZzeHkzV0J6RHpWd1V3Y1VJClVhQnNSSnpTQ3NRY2RCNXZPMGR3 bUtpNzVjc2szcEg4dXlJR21UZkVkRm8KLS0tIEdzbHVaK0MrRmk1QTV2ejhtYTky ZDVSUEtLdFVqQ0xUZnRlVW5rOURoMWcKfAQ4D2gvDwQE1ZxIz4DfnVfv2OekuqlX 0XuGxTpE/XP1yXqc7WgBr4DjV9L5ooYU5RiZD6hOaTlhPChE0VGVpg== -----END AGE ENCRYPTED FILE----- lastmodified: "2025-07-22T13:29:06Z" mac: ENC[AES256_GCM,data:krY/qF48bsUnCyeQ1gL8eA14QDpZHZk8zsjUhaJ3UFbaIvVkVZpdbhsgNjcNd8OSU7i36kSz8PvSobMKddBsNTW7J4+oAFuouBAteF/TUCAuMiUU7dz5WhCZ5O3gcY+eDdeErXmiYNtzZJz6WGq3k9mMoN2zjLQCs0dMZG3iXVg=,iv:fhGIMHp2/q8FBh9LeZUlJREfaS279RkhpQDw9OqfksY=,tag:BBE3OeojIWz8SdjPPI4rTA==,type:str] unencrypted_suffix: _unencrypted version: 3.10.2 ================================================ FILE: home/_modules/multiplexer/tmux/config/tmux.conf ================================================ # SERVER OPTIONS set -g buffer-limit 20 # number of buffers, older than limit buffer will be removed set -g default-terminal "tmux-256color" # 256 color set -g escape-time 0 # time for tmux to wait for key sequence set -ga terminal-features ",*256col*:RGB" # true color support set -ga terminal-overrides ',*:Smulx=\E[4::%p1%dm' # undercurl support set -ga terminal-overrides ',*:Setulc=\E[58::2::%p1%{65536}%/%d::%p1%{256}%/%{255}%&%d::%p1%{255}%&%d%;m' # undercurl colors # SESSION OPTIONS set -g base-index 1 # starting index for new window set -g display-time 0 # time for which status line messages and indicators are displayed in miliseconds, 0 means diplay until a key is pressed set -g history-limit 10000 # maximum number of lines held in window history set -g mouse on # enable mouse support set -g prefix M-a # prefix key is Alt+a set -g prefix-timeout 0 # disable timeout after prefix key is pressed set -g renumber-windows on # automatically renumber window when a window is closed # set -g repeat-time 32767 # typing command without pressing prefix key again in the specified time in miliseconds (can be 200000 after 354926a956ba07ef38d2ddd91a7820e3c9634ab0) set -g status-keys vi # use vi style keybindings in command prompt # WINDOW OPTIONS set -g aggressive-resize on # tmux will resize the window to the size of the smallest or largest session rather than the session it is attached set -g automatic-rename on # automatic window renaming set -g automatic-rename-format '#{pane_current_command}' # format of automatic window renaming set -g mode-keys vi # use vi style keybindings in copy mode set -g pane-base-index 1 # starting index for new pane # PANE OPTIONS set -g allow-rename off # allow programs to change the window name set -g remain-on-exit off # destroy window when program exits # KEYBINDING SETTINGS # Some keybindings below have `switch-client -T prefix` appended to the command. I use it to mimic the behavior of `bind -r` # so I can repeat commands without typing prefix key again. The problem with using `bind -r` is it doesn't work for bindings that # doesn't have `-r` flag. # For example, if I have a key to move to a pane with `-r` flag and a key to kill pane without `-r` flag # in `prefix` keytable, to move to a pane and kill it I have to: # Press prefix key, move to a pane, wait for `repeat-time`, press prefix key, kill the pane. # Now, I can do: # Press prefix key, move to a pane, kill the pane # The downside of this approach is I can be stucked in prefix mode forever, what I do is have a key to exit prefix mode which # I bound to `Esc` and `Space` key # unbind all the default key bindings unbind -T root -a unbind -T prefix -a unbind -T copy-mode -a unbind -T copy-mode-vi -a # root key-table bind -T root -N "Alt+z to send prefix key to client" M-z send-prefix bind -T root -N "select pane on mouse click on pane" MouseDown1Pane select-pane -t = \; send-keys -M bind -T root -N "select window on mouse click on status" MouseDown1Status select-window -t = bind -T root -N "resize pane on mouse drag on border" MouseDrag1Border resize-pane -M bind -T root -N "select previous window on mouse wheel up on status" WheelUpStatus previous-window bind -T root -N "select next window on mouse wheel down on status" WheelDownStatus next-window bind -T root -N "go into copy mode to scroll history on mouse wheel up on pane" WheelUpPane \ if-shell -F "#{||:#{pane_in_mode},#{mouse_any_flag}}" { send-keys -M } { copy-mode -e } # prefix key-table bind -T prefix -N "h to move to left pane" h select-pane -L \; switch-client -T prefix bind -T prefix -N "j to move to bottom pane" j select-pane -D \; switch-client -T prefix bind -T prefix -N "k to move to top pane" k select-pane -U \; switch-client -T prefix bind -T prefix -N "l to move to right pane" l select-pane -R \; switch-client -T prefix bind -T prefix -N "H to resize pane to the left by 2 columns" H resize-pane -L 2 \; switch-client -T prefix bind -T prefix -N "J to resize pane downwards by 2 lines" J resize-pane -D 2 \; switch-client -T prefix bind -T prefix -N "K to resize pane upwards by 2 lines" K resize-pane -U 2 \; switch-client -T prefix bind -T prefix -N "L to resize pane to the right by 2 columns" L resize-pane -R 2 \; switch-client -T prefix bind -T prefix -N "Alt+n to move to next window" M-n next-window \; switch-client -T prefix bind -T prefix -N "Alt+p to move to previous window" M-p previous-window \; switch-client -T prefix bind -T prefix -N "> to swap pane with the next pane" > swap-pane -D \; switch-client -T prefix bind -T prefix -N "< to swap pane with the previous pane" < swap-pane -U \; switch-client -T prefix bind -T prefix -N "Alt+s to split window horizontally with current pane path" M-s split-window -hc "#{pane_current_path}" bind -T prefix -N "Alt+v to split window vertically with current pane path" M-v split-window -vc "#{pane_current_path}" bind -T prefix -N "c to open a new window with current pane path" c new-window -c "#{pane_current_path}" bind -T prefix -N "Esc to switch to root key table" -r Escape switch-client -T root bind -T prefix -N "Space to switch to root key table" -r Space switch-client -T root bind -T prefix -N "a to enter copy mode" a copy-mode bind -T prefix -N ": to enter tmux command prompt" : command-prompt bind -T prefix -N "x to close current pane" x kill-pane bind -T prefix -N "X to close current window" X kill-window # copy-mode-vi key-table bind -T copy-mode-vi -N "h to move cursor left" h send-keys -X cursor-left bind -T copy-mode-vi -N "j to move cursor down" j send-keys -X cursor-down bind -T copy-mode-vi -N "k to move cursor down" k send-keys -X cursor-up bind -T copy-mode-vi -N "l to move cursor down" l send-keys -X cursor-right bind -T copy-mode-vi -N "b to move cursor to word beginning" b send-keys -X previous-word bind -T copy-mode-vi -N "e to move cursor to word ending" e send-keys -X next-word-end bind -T copy-mode-vi -N "Home to move cursor to start of line" Home send-keys -X start-of-line bind -T copy-mode-vi -N "0 to move cursor to start of line" 0 send-keys -X start-of-line bind -T copy-mode-vi -N "End to move cursor to end of line" End send-keys -X end-of-line bind -T copy-mode-vi -N "$ to move cursor to end of line" \$ send-keys -X end-of-line bind -T copy-mode-vi -N "PageUp to move cursor one page up" Npage send-keys -X page-up bind -T copy-mode-vi -N "PageDn to move cursor one page down" Ppage send-keys -X page-down bind -T copy-mode-vi -N "g to move cursor to top of history" g send-keys -X history-top bind -T copy-mode-vi -N "G to move cursor to bottom of history" G send-keys -X history-bottom bind -T copy-mode-vi -N "Scroll up 5 line on mouse wheel up on pane" WheelUpPane select-pane \; send-keys -X -N 5 scroll-up bind -T copy-mode-vi -N "Scroll down 5 line on mouse wheel down on pane" WheelDownPane select-pane \; send-keys -X -N 5 scroll-down bind -T copy-mode-vi -N "Select a line and copy it to clipboard on double click on pane" DoubleClick1Pane \ select-pane \; send-keys -X select-line \; run-shell -d 0.3 \; send-keys -X copy-pipe-and-cancel bind -T copy-mode-vi -N "Begin selection on mouse drag on pane" MouseDrag1Pane \ select-pane \; send-keys -X begin-selection bind -T copy-mode-vi -N "Copy selection to clipboard on releasing mouse drag on pane" MouseDragEnd1Pane \ send-key -X copy-pipe-and-cancel bind -T copy-mode-vi -N "v to begin selection" v send-keys -X begin-selection bind -T copy-mode-vi -N "Space to begin selection" Space send-keys -X begin-selection bind -T copy-mode-vi -N "V to select a line" V send-keys -X select-line bind -T copy-mode-vi -N "Alt+v to toggle box selection" M-v send-keys -X rectangle-toggle bind -T copy-mode-vi -N "y to copy selection to clipboard" y send-keys -X copy-pipe-and-cancel bind -T copy-mode-vi -N "Y to copy from cursor to end of line to clipboard" Y send-keys -X copy-pipe-end-of-line-and-cancel bind -T copy-mode-vi -N "Esc to exit copy mode" Escape send-keys -X cancel bind -T copy-mode-vi -N "q to exit copy mode" q send-keys -X cancel ================================================ FILE: home/_modules/multiplexer/tmux/default.nix ================================================ { config, lib, pkgs, ... }: let cfg = config.myHome.multiplexer.tmux; inherit (lib) mkEnableOption mkIf; in { options.myHome.multiplexer.tmux = { enable = mkEnableOption "tmux"; }; config = mkIf (cfg.enable) { catppuccin.tmux = { enable = true; extraConfig = '' set -g @catppuccin_window_number_position "right" set -g @catppuccin_window_current_number_color "#{@thm_green}" set -g @catppuccin_window_text "" set -g @catppuccin_window_number "#[bold]Tab ###I " set -g @catppuccin_window_current_text "" set -g @catppuccin_window_current_number "#[bold]Tab ###I " set -g @catppuccin_window_status_style "custom" set -g @catppuccin_window_right_separator "#[fg=#{@_ctp_status_bg},reverse]#[none]" set -g @catppuccin_window_left_separator "#[fg=#{@_ctp_status_bg}]#[none]" set -g @catppuccin_window_middle_separator "#[bg=#{@catppuccin_window_number_color},fg=#{@catppuccin_window_text_color}]" set -g @catppuccin_window_current_middle_separator "#[bg=#{@catppuccin_window_current_number_color},fg=#{@catppuccin_window_current_text_color}]" set -g window-status-separator "" set -g status-left-length 0 set -g status-left "#[fg=#{@thm_fg} bold]TMUX (#S) " set -ga status-left "#{?client_prefix,#[fg=#{@thm_red} bold]PREFIX ,#{?#{==:#{pane_mode},copy-mode},#[fg=#{@thm_yellow} bold]COPY ,#[fg=#{@thm_green} bold]NORMAL }}" set -g status-right "" ''; }; programs.tmux = { enable = true; package = pkgs.tmux; extraConfig = '' ${ if config.myHome.isWayland then "set -g copy-command '${pkgs.wl-clipboard}/bin/wl-copy'" else "set -g copy-command '${pkgs.xsel}/bin/xsel -i -b'" } ${builtins.readFile ./config/tmux.conf} ''; }; }; } ================================================ FILE: home/_modules/multiplexer/zellij/config/config.kdl ================================================ default_mode "normal" theme "catppuccin-mocha" keybinds clear-defaults=true { normal { bind "Alt a" { SwitchToMode "tmux"; } bind "Alt z" { SwitchToMode "locked"; } } locked { bind "Alt z" { SwitchToMode "tmux"; } } tmux { bind "Alt s" { NewPane "Right"; SwitchToMode "normal"; } bind "Alt v" { NewPane "Down"; SwitchToMode "normal"; } bind "r" { SwitchToMode "RenameTab"; } bind "h" { MoveFocus "Left"; } bind "j" { MoveFocus "Down"; } bind "k" { MoveFocus "Up"; } bind "l" { MoveFocus "Right"; } bind ">" { MovePane; } bind "H" { Resize "Left"; } bind "J" { Resize "Down"; } bind "K" { Resize "Up"; } bind "L" { Resize "Right"; } bind "Alt n" { GoToNextTab; } bind "Alt p" { GoToPreviousTab; } bind "c" { NewTab; SwitchToMode "normal"; } bind "x" { CloseFocus; SwitchToMode "normal"; } bind "a" { EditScrollback; SwitchToMode "normal"; } bind "Esc" "Space" "Enter" { SwitchToMode "normal"; } } RenameTab { bind "Alt a" { SwitchToMode "tmux"; } bind "Enter" { SwitchToMode "normal"; } bind "Esc" { UndoRenameTab; SwitchToMode "normal"; } } } plugins { tab-bar { path "tab-bar"; } status-bar { path "status-bar"; } strider { path "strider"; } compact-bar { path "compact-bar"; } } ui { pane_frames { rounded_corners true } } ================================================ FILE: home/_modules/multiplexer/zellij/config/layouts/default.kdl ================================================ layout { pane pane size=1 borderless=true { plugin location="zellij:compact-bar" } } ================================================ FILE: home/_modules/multiplexer/zellij/default.nix ================================================ { config, lib, pkgs, ... }: let cfg = config.myHome.multiplexer.zellij; cmd = lib.getExe config.programs.zellij.package; inherit (lib) mkEnableOption mkPackageOption mkIf; in { options.myHome.multiplexer.zellij = { enable = mkEnableOption "Zellij"; package = mkPackageOption pkgs "zellij" { }; }; config = mkIf (cfg.enable) { programs = { zellij = { enable = true; package = cfg.package; }; fish.interactiveShellInit = '' set -gx ZELLIJ_AUTO_ATTACH true set -gx ZELLIJ_AUTO_EXIT true eval (${cmd} setup --generate-auto-start fish | string collect) ''; }; xdg.configFile."zellij".source = ./config; }; } ================================================ FILE: home/_modules/programs/beeaccounting/default.nix ================================================ { lib, osConfig, config, pkgs, ... }: with lib; let systemEnabled = myLib.systemEnabled "mySystem.containers.beeaccounting.enable" osConfig; isAdminUser = myLib.isAdminUser config.myHome.username osConfig; adminUser = osConfig.users.users.${osConfig.mySystem.adminUser}; in { config = mkIf (systemEnabled && isAdminUser) { xdg.desktopEntries = { bee-app = { name = "Bee2.9"; genericName = "Accounting Software"; icon = ./bee-app.ico; exec = let script = pkgs.writeShellScriptBin "bee-app" '' ${pkgs.xorg.xhost}/bin/xhost +local:${toString adminUser.uid} > /dev/null 2>&1 && \ ${pkgs.docker}/bin/docker exec --user ${toString adminUser.uid} beeaccounting-app java -Xmx1g -jar /app/BeeUI-2.9.jar ''; in "${script}/bin/bee-app"; categories = [ "Office" "Finance" "Java" ]; comment = "Bee Accounting 2.9 Application"; type = "Application"; terminal = false; settings = { StartupWMClass = "AppLauncher"; }; }; bee-updater = { name = "Bee2.9-Updater"; genericName = "Software Updater"; icon = ./bee-updater.ico; exec = let script = pkgs.writeShellScriptBin "bee-updater" '' ${pkgs.xorg.xhost}/bin/xhost +local:${toString adminUser.uid} > /dev/null 2>&1 && \ ${pkgs.docker}/bin/docker exec --user ${toString adminUser.uid} beeaccounting-app java -Xmx1g -jar /app/BeeUpdater-2.9.jar ''; in "${script}/bin/bee-updater"; categories = [ "Office" "Finance" "Java" ]; comment = "Bee Accounting 2.9 Updater"; type = "Application"; terminal = false; }; }; wayland.windowManager.hyprland.settings.windowrulev2 = [ "workspace 2, class:^(AppLauncher)$" "nodim, class:^(AppLauncher)$" "nofocus, class:^(AppLauncher)$, title:^(win)(.*)$" "noborder, class:^(AppLauncher)$, title:^(win)(.*)$" "noanim, class:^(AppLauncher)$, title:^(win)(.*)$" "nofocus, class:^(AppLauncher)$, title:^(JidePopup)$" "noborder, class:^(AppLauncher)$, title:^(JidePopup)$" "noanim, class:^(AppLauncher)$, title:^(JidePopup)$" "maxsize 400 180, class:^(AppLauncher)$, title:^(JidePopup)$" ]; }; } ================================================ FILE: home/_modules/programs/chezmoi/default.nix ================================================ { config, lib, pkgs, ... }: let cfg = config.myHome.programs.chezmoi; inherit (lib) mkEnableOption mkIf; in { options.myHome.programs.chezmoi = { enable = mkEnableOption "chezmoi"; }; config = mkIf (cfg.enable) { # we want to do stuffs after HM has finished linking stuffs in `$NIX_PROFILES/bin` home.activation.chezmoi = lib.hm.dag.entryAfter [ "installPackages" ] '' # I want chezmoi to have access to the userspace $PATH _saved_path=$PATH PATH="${config.home.path}/bin:$PATH" # a lot of my chezmoi scripts needs system programs to work, might be a bad idea idk PATH=$PATH:/usr/local/bin:/usr/bin:/bin run ${pkgs.chezmoi}/bin/chezmoi apply -S ${../../../..} $VERBOSE_ARG # return it back PATH=$_saved_path ''; }; } ================================================ FILE: home/_modules/programs/fontconfig/default.nix ================================================ { lib, osConfig, pkgs, ... }: { config = lib.mkIf (!lib.myLib.isNixos osConfig) { fonts.fontconfig.enable = true; home.packages = with pkgs; [ nerd-fonts.ubuntu-mono unifont ]; }; } ================================================ FILE: home/_modules/programs/go/default.nix ================================================ { config, lib, pkgs, ... }: let cfg = config.myHome.programs.go; in { options.myHome.programs.go.enable = lib.mkEnableOption "Go"; config = lib.mkIf (cfg.enable) { programs.go = { enable = true; package = pkgs.unstable.go; }; }; } ================================================ FILE: home/_modules/programs/obs-studio/default.nix ================================================ { config, lib, pkgs, ... }: let cfg = config.myHome.programs.obs-studio; in { options.myHome.programs.obs-studio = { enable = lib.mkEnableOption "OBS Studio"; }; config = lib.mkIf (cfg.enable) { programs.obs-studio = { enable = true; plugins = lib.mkIf config.myHome.isWayland [ pkgs.obs-studio-plugins.wlrobs ]; }; }; } ================================================ FILE: home/_modules/programs/qmk/default.nix ================================================ { lib, config, osConfig, pkgs, ... }: let inherit (lib) myLib mkEnableOption mkIf; cfg = config.myHome.programs.qmk; systemEnabled = myLib.systemEnabled "mySystem.programs.qmk.enable" osConfig; isNixos = myLib.isNixos osConfig; in { options.myHome.programs.qmk = { enable = mkEnableOption "QMK"; }; config = mkIf (cfg.enable) { warnings = mkIf (!systemEnabled && isNixos) [ '' You have enabled QMK home-manager module but not the NixOS system module and you are using NixOS. Some things might not work properly. '' ]; myHome.shell.git.enable = true; xdg.configFile."qmk/qmk.ini".source = ./qmk.ini; home.packages = with pkgs; [ qmk ]; }; } ================================================ FILE: home/_modules/programs/qmk/qmk.ini ================================================ [user] qmk_home = /home/budiman/Github/qmk_firmware keyboard = crkbd/rev1 keymap = budimanjojo [mass_compile] keymap = default [config] [console] [compile] [docs] [flash] [format_c] [format_python] [format_text] [generate_autocorrect_data] [generate_compilation_database] [generate_develop_pr_list] [generate_dfu_header] [generate_info_json] [hello] [info] [lint] [kle2json] [list_keymaps] [list_layouts] [new_keyboard] [new_keymap] [painter_convert_graphics] [painter_make_font_image] [painter_convert_font_image] [general] ================================================ FILE: home/_modules/programs/yamllint/config.yaml ================================================ --- extends: relaxed yaml-files: - '*.yaml' - '*.yml' - '.yamllint' rules: empty-lines: max: 1 empty-values: enable indentation: indent-sequences: consistent new-line-at-end-of-file: disable truthy: allowed-values: - 'true' - 'false' - 'yes' - 'no' - 'on' - 'off' ================================================ FILE: home/_modules/programs/yamllint/default.nix ================================================ { config, lib, pkgs, ... }: let cfg = config.myHome.programs.yamllint; in { options.myHome.programs.yamllint = { enable = lib.mkEnableOption "YAMLlint"; }; config = lib.mkIf (cfg.enable) { home.packages = [ pkgs.yamllint ]; xdg.configFile."yamllint/config".source = ./config.yaml; }; } ================================================ FILE: home/_modules/services/opencloud-client/default.nix ================================================ { config, lib, pkgs, ... }: let cfg = config.myHome.services.opencloud-client; in { options.myHome.services.opencloud-client = { enable = lib.mkEnableOption "OpenCloud client"; }; config = lib.mkIf cfg.enable { systemd.user.services.opencloud-client = { Unit = { Description = "OpenCloud client"; After = [ "graphical-session.target" ]; PartOf = [ "graphical-session.target" ]; }; Service = { Environment = [ "PATH=${config.home.profileDirectory}/bin" ]; ExecStart = "${pkgs.unstable.opencloud-desktop}/bin/opencloud"; }; Install = { WantedBy = [ "graphical-session.target" ]; }; }; }; } ================================================ FILE: home/_modules/shell/dircolors/default.nix ================================================ { config, lib, ... }: let cfg = config.myHome.shell.dircolors; in { options.myHome.shell.dircolors = { enable = lib.mkEnableOption "dircolors"; }; config = lib.mkIf cfg.enable { programs.dircolors = { enable = true; settings = { DIR = "01;34"; LINK = "01;36"; FIFO = "40;33"; SOCK = "01;35"; BLK = "01;33"; CHR = "33"; ORPHAN = "31"; EXEC = "01;32"; "*.7z" = "01;31"; "*.bz2" = "01;31"; "*.gz" = "01;31"; "*.lz" = "01;31"; "*.lzma" = "01;31"; "*.lzo" = "01;31"; "*.rar" = "01;31"; "*.tar" = "01;31"; "*.tbz" = "01;31"; "*.tgz" = "01;31"; "*.xz" = "01;31"; "*.zip" = "01;31"; "*.zst" = "01;31"; "*.zstd" = "01;31"; "*.bmp" = "01;35"; "*.tiff" = "01;35"; "*.tif" = "01;35"; "*.TIFF" = "01;35"; "*.gif" = "01;35"; "*.jpeg" = "01;35"; "*.jpg" = "01;35"; "*.png" = "01;35"; "*.webp" = "01;35"; "*.pot" = "01;35"; "*.pcb" = "01;35"; "*.gbr" = "01;35"; "*.scm" = "01;35"; "*.xcf" = "01;35"; "*.spl" = "01;35"; "*.stl" = "01;35"; "*.dwg" = "01;35"; "*.ply" = "01;35"; "*.apk" = "01;31"; "*.deb" = "01;31"; "*.rpm" = "01;31"; "*.jad" = "01;31"; "*.jar" = "01;31"; "*.crx" = "01;31"; "*.xpi" = "01;31"; "*.avi" = "01;35"; "*.divx" = "01;35"; "*.m2v" = "01;35"; "*.m4v" = "01;35"; "*.mkv" = "01;35"; "*.MOV" = "01;35"; "*.mov" = "01;35"; "*.mp4" = "01;35"; "*.mpeg" = "01;35"; "*.mpg" = "01;35"; "*.sample" = "01;35"; "*.wmv" = "01;35"; "*.3g2" = "01;35"; "*.3gp" = "01;35"; "*.gp3" = "01;35"; "*.webm" = "01;35"; "*.flv" = "01;35"; "*.ogv" = "01;35"; "*.f4v" = "01;35"; "*.3ga" = "01;35"; "*.aac" = "01;35"; "*.m4a" = "01;35"; "*.mp3" = "01;35"; "*.mp4a" = "01;35"; "*.oga" = "01;35"; "*.ogg" = "01;35"; "*.wma" = "01;35"; "*.flac" = "01;35"; "*.alac" = "01;35"; "*.mid" = "01;35"; "*.midi" = "01;35"; "*.pcm" = "01;35"; "*.wav" = "01;35"; "*.ass" = "01;33"; "*.srt" = "01;33"; "*.ssa" = "01;33"; "*.sub" = "01;33"; "*.git" = "01;33"; "*README" = "33"; "*README.rst" = "33"; "*README.md" = "33"; "*LICENSE" = "33"; "*COPYING" = "33"; "*INSTALL" = "33"; "*COPYRIGHT" = "33"; "*AUTHORS" = "33"; "*HISTORY" = "33"; "*CONTRIBUTOS" = "33"; "*PATENTS" = "33"; "*VERSION" = "33"; "*NOTICE" = "33"; "*CHANGES" = "33"; "*CHANGELOG" = "33"; "*log" = "33"; "*.txt" = "33"; "*.md" = "33"; "*.markdown" = "33"; "*.nfo" = "33"; "*.org" = "33"; "*.pod" = "33"; "*.rst" = "33"; "*.tex" = "33"; "*.texttile" = "33"; "*.bib" = "35"; "*.json" = "35"; "*.jsonl" = "35"; "*.jsonnet" = "35"; "*.libsonnet" = "35"; "*.rss" = "35"; "*.xml" = "35"; "*.fxml" = "35"; "*.toml" = "35"; "*.yaml" = "35"; "*.yml" = "35"; "*.dtd" = "35"; "*.cbr" = "35"; "*.cbz" = "35"; "*.chm" = "35"; "*.pdf" = "35"; "*.PDF" = "35"; "*.epub" = "35"; "*.awk" = "35"; "*.bash" = "35"; "*.bat" = "35"; "*.BAT" = "35"; "*.sed" = "35"; "*.sh" = "35"; "*.zsh" = "35"; "*.vim" = "35"; "*.py" = "35"; "*.ipynb" = "35"; "*.rb" = "35"; "*.gemspec" = "35"; "*.pl" = "35"; "*.PL" = "35"; "*.t" = "35"; "*.msql" = "35"; "*.mysql" = "35"; "*.pgsql" = "35"; "*.sql" = "35"; "*.r" = "35"; "*.R" = "35"; "*.cljw" = "35"; "*.scala" = "35"; "*.sc" = "35"; "*.dart" = "35"; "*.asm" = "35"; "*.cl" = "35"; "*.lisp" = "35"; "*.rkt" = "35"; "*.el" = "35"; "*.elc" = "35"; "*.eln" = "35"; "*.lua" = "35"; "*.c" = "35"; "*.C" = "35"; "*.h" = "35"; "*.H" = "35"; "*.tcc" = "35"; "*.c++" = "35"; "*.h++" = "35"; "*.hpp" = "35"; "*.hxx" = "35"; "*ii." = "35"; "*.m" = "35"; "*.M" = "35"; "*.cc" = "35"; "*.cs" = "35"; "*.cp" = "35"; "*.cpp" = "35"; "*.cxx" = "35"; "*.go" = "35"; "*.f" = "35"; "*.F" = "35"; "*.nim" = "35"; "*.nimble" = "35"; "*.s" = "35"; "*.S" = "35"; "*.rs" = "35"; "*.scpt" = "35"; "*.swift" = "35"; "*.vala" = "35"; "*.vapi" = "35"; "*.hs" = "35"; "*.lhs" = "35"; "*.zig" = "35"; "*.v" = "35"; "*.pyc" = "35"; "*.tf" = "35"; "*.tfstate" = "35"; "*.tfvars" = "35"; "*.css" = "35"; "*.less" = "35"; "*.sass" = "35"; "*.scss" = "35"; "*.htm" = "35"; "*.html" = "35"; "*.jhtm" = "35"; "*.mht" = "35"; "*.eml" = "35"; "*.coffee" = "35"; "*.java" = "35"; "*.js" = "35"; "*.mjs" = "35"; "*.jsm" = "35"; "*.jsp" = "35"; "*.rasi" = "35"; "*.php" = "35"; "*.twig" = "35"; "*.vb" = "35"; "*.vba" = "35"; "*.vbs" = "35"; "*.Dockerfile" = "35"; "*.dockerignore" = "35"; "*.Makefile" = "35"; "*.MANIFEST" = "35"; "*.am" = "35"; "*.in" = "35"; "*.hin" = "35"; "*.scan" = "35"; "*.m4" = "35"; "*.old" = "35"; "*.out" = "35"; "*.SKIP" = "35"; "*.diff" = "35"; "*.patch" = "35"; "*.tmpl" = "35"; "*.j2" = "35"; "*PKGBUILD" = "35"; "*config" = "35"; "*.conf" = "35"; "*.service" = "31"; "*.@.service" = "31"; "*.socket" = "31"; "*.swap" = "31"; "*.device" = "31"; "*.mount" = "31"; "*.automount" = "31"; "*.target" = "31"; "*.path" = "31"; "*.timer" = "31"; "*.snapshot" = "31"; "*.allow" = "31"; "*.swp" = "31"; "*.swo" = "31"; "*.tmp" = "31"; "*.pid" = "31"; "*.state" = "31"; "*.lock" = "31"; "*.lockfile" = "31"; "*.pacnew" = "31"; "*.un" = "31"; "*.orig" = "31"; }; }; }; } ================================================ FILE: home/_modules/shell/fish/default.nix ================================================ { lib, config, osConfig, pkgs, myPkgs, ... }: let inherit (lib) myLib mkEnableOption mkIf; cfg = config.myHome.shell.fish; isNixos = myLib.isNixos osConfig; in { options.myHome.shell.fish = { enable = mkEnableOption "fish shell"; }; config = mkIf (cfg.enable) { myHome.shell = { dircolors.enable = true; starship.enable = true; }; catppuccin = { fish.enable = true; # TODO: wait for the IFD issue is fixed # ref: https://github.com/catppuccin/nix/issues/392 fzf.enable = mkIf (pkgs.stdenv.hostPlatform.system == "x86_64-linux") true; bat.enable = true; }; programs = { fish = { enable = true; package = mkIf (!isNixos) ( pkgs.fish.override { fishEnvPreInit = '' # This will ensure that $NIX_PROFILE variable is set as fast as possible # so Nix managed programs can work properly without needing to tweak the OS. # Usually this is done by Nix installer by putting these lines inside fish system config dir # but it's a hit or miss especially when fish is not installed in the system beforehand. # And I also found problem that vendor completion of Nix installed programs are not working # without calling fish twice, I assume it's because $NIX_PROFILE is not set fast enough if test -e '/nix/var/nix/profiles/default/etc/profile.d/nix-daemon.fish' . '/nix/var/nix/profiles/default/etc/profile.d/nix-daemon.fish' end ''; } ); plugins = with pkgs.fishPlugins; [ { name = "autopair"; src = autopair.src; } { # TODO: might be better off use native fzf support name = "fzf-fish"; src = fzf-fish.src; } { name = "puffer"; src = puffer.src; } { name = "abbreviation-tips"; src = myPkgs.fish-plugins.abbreviation-tips.src; } { name = "fish-completion-sync"; src = myPkgs.fish-plugins.fish-completion-sync.src; } ] ++ [ (mkIf (config.programs.tmux.enable && !config.programs.zellij.enable) { name = "tmux-fish"; src = myPkgs.fish-plugins.tmux-fish.src; }) ]; functions = { mkcd = { body = "mkdir -p $argv; cd $argv"; description = "mkdir and cd into in"; }; }; shellInit = '' # disable fish greeting set -g fish_greeting # Termite and Alacritty tmux ssh error workaround if test $TERM = xterm-termite; or test $TERM = alacritty set -gx TERM xterm-256color end ''; interactiveShellInit = '' # use vi keybindings fish_vi_key_bindings # fzf-fish set fzf_preview_dir_cmd eza --all --color=always set fzf_fd_opts --hidden --exclude=.git --exclude=.github --exclude=.cache fzf_configure_bindings --git_log=\cg --git_status=\cs --variables=\cv --directory=\cf --history=\cr ${lib.optionalString config.programs.tmux.enable "set fish_tmux_autostart true"} ''; shellAbbrs = { cpr = "cp -rf"; rmr = "rm -rf"; md = "mdir -p"; rd = "rmdir"; # eza lsa = "eza -lag --git --icons --sort=type"; ll = "eza -l --git --icons --sort = type"; }; }; zoxide.enable = true; bat.enable = true; fzf = { enable = true; defaultCommand = "${pkgs.fd}/bin/fd --type f"; defaultOptions = [ "--cycle" "--layout reverse" "--border" "--height 40%" "--preview-window wrap" "--marker *" ]; changeDirWidgetCommand = "${pkgs.fd}/bin/fd --type d"; changeDirWidgetOptions = [ "--preview 'tree -C {} | head -200'" ]; }; }; home.packages = with pkgs; [ eza fd ]; }; } ================================================ FILE: home/_modules/shell/git/default.nix ================================================ { config, lib, options, ... }: let cfg = config.myHome.shell.git; inherit (lib) mkEnableOption mkOption mkIf ; in { options.myHome.shell.git = { enable = mkEnableOption "git"; config = mkOption { type = options.programs.git.extraConfig.type; default = options.programs.git.extraConfig.default; }; }; config = mkIf (cfg.enable) { programs.git = { enable = true; settings = cfg.config; }; programs.gh = { enable = true; settings = { # https://github.com/nix-community/home-manager/issues/4744 version = 1; }; }; programs.fish.shellAbbrs = { g = "git"; ga = "git add ."; gaa = "git add --all"; gc = "git commit"; gcm = "git commit -m"; gs = "git status"; gp = "git push"; gpl = "git pull"; gl = "git log"; gd = "git diff"; gds = "git diff --staged"; gr = "git restore ."; grs = "git restore --staged ."; }; }; } ================================================ FILE: home/_modules/shell/lf/configs/colors ================================================ # vim:ft=dircolors # (This is not a dircolors file but it helps to highlight colors and comments) # default values from dircolors # (entries with a leading # are not implemented in lf) # #no 00 # NORMAL # fi 00 # FILE # #rs 0 # RESET # di 01;34 # DIR # ln 01;36 # LINK # #mh 00 # MULTIHARDLINK # pi 40;33 # FIFO # so 01;35 # SOCK # #do 01;35 # DOOR # bd 40;33;01 # BLK # cd 40;33;01 # CHR # or 40;31;01 # ORPHAN # #mi 00 # MISSING # su 37;41 # SETUID # sg 30;43 # SETGID # #ca 30;41 # CAPABILITY # tw 30;42 # STICKY_OTHER_WRITABLE # ow 34;42 # OTHER_WRITABLE # st 37;44 # STICKY # ex 01;32 # EXEC # default values from lf (with matching order) # ln 01;36 # LINK # or 31;01 # ORPHAN # tw 01;34 # STICKY_OTHER_WRITABLE # ow 01;34 # OTHER_WRITABLE # st 01;34 # STICKY # di 01;34 # DIR # pi 33 # FIFO # so 01;35 # SOCK # bd 33;01 # BLK # cd 33;01 # CHR # su 01;32 # SETUID # sg 01;32 # SETGID # ex 01;32 # EXEC # fi 00 # FILE # file types (with matching order) ln 01;36 # LINK or 31;01 # ORPHAN tw 34 # STICKY_OTHER_WRITABLE ow 34 # OTHER_WRITABLE st 01;34 # STICKY di 01;34 # DIR pi 33 # FIFO so 01;35 # SOCK bd 33;01 # BLK cd 33;01 # CHR su 01;32 # SETUID sg 01;32 # SETGID ex 01;32 # EXEC fi 00 # FILE # archives or compressed (dircolors defaults) *.tar 01;31 *.tgz 01;31 *.arc 01;31 *.arj 01;31 *.taz 01;31 *.lha 01;31 *.lz4 01;31 *.lzh 01;31 *.lzma 01;31 *.tlz 01;31 *.txz 01;31 *.tzo 01;31 *.t7z 01;31 *.zip 01;31 *.z 01;31 *.dz 01;31 *.gz 01;31 *.lrz 01;31 *.lz 01;31 *.lzo 01;31 *.xz 01;31 *.zst 01;31 *.tzst 01;31 *.bz2 01;31 *.bz 01;31 *.tbz 01;31 *.tbz2 01;31 *.tz 01;31 *.deb 01;31 *.rpm 01;31 *.jar 01;31 *.war 01;31 *.ear 01;31 *.sar 01;31 *.rar 01;31 *.alz 01;31 *.ace 01;31 *.zoo 01;31 *.cpio 01;31 *.7z 01;31 *.rz 01;31 *.cab 01;31 *.wim 01;31 *.swm 01;31 *.dwm 01;31 *.esd 01;31 # image formats (dircolors defaults) *.jpg 01;35 *.jpeg 01;35 *.mjpg 01;35 *.mjpeg 01;35 *.gif 01;35 *.bmp 01;35 *.pbm 01;35 *.pgm 01;35 *.ppm 01;35 *.tga 01;35 *.xbm 01;35 *.xpm 01;35 *.tif 01;35 *.tiff 01;35 *.png 01;35 *.svg 01;35 *.svgz 01;35 *.mng 01;35 *.pcx 01;35 *.mov 01;35 *.mpg 01;35 *.mpeg 01;35 *.m2v 01;35 *.mkv 01;35 *.webm 01;35 *.ogm 01;35 *.mp4 01;35 *.m4v 01;35 *.mp4v 01;35 *.vob 01;35 *.qt 01;35 *.nuv 01;35 *.wmv 01;35 *.asf 01;35 *.rm 01;35 *.rmvb 01;35 *.flc 01;35 *.avi 01;35 *.fli 01;35 *.flv 01;35 *.gl 01;35 *.dl 01;35 *.xcf 01;35 *.xwd 01;35 *.yuv 01;35 *.cgm 01;35 *.emf 01;35 *.ogv 01;35 *.ogx 01;35 # audio formats (dircolors defaults) *.aac 00;36 *.au 00;36 *.flac 00;36 *.m4a 00;36 *.mid 00;36 *.midi 00;36 *.mka 00;36 *.mp3 00;36 *.mpc 00;36 *.ogg 00;36 *.ra 00;36 *.wav 00;36 *.oga 00;36 *.opus 00;36 *.spx 00;36 *.xspf 00;36 ================================================ FILE: home/_modules/shell/lf/configs/icons ================================================ # vim:ft=conf # These examples require Nerd Fonts or a compatible font to be used. # See https://www.nerdfonts.com for more information. # default values from lf (with matching order) # ln l # LINK # or l # ORPHAN # tw t # STICKY_OTHER_WRITABLE # ow d # OTHER_WRITABLE # st t # STICKY # di d # DIR # pi p # FIFO # so s # SOCK # bd b # BLK # cd c # CHR # su u # SETUID # sg g # SETGID # ex x # EXEC # fi - # FILE # file types (with matching order) ln  # LINK or  # ORPHAN tw t # STICKY_OTHER_WRITABLE ow  # OTHER_WRITABLE st t # STICKY di  # DIR pi p # FIFO so s # SOCK bd b # BLK cd c # CHR su u # SETUID sg g # SETGID ex  # EXEC fi  # FILE # file extensions (vim-devicons) *.styl  *.sass  *.scss  *.htm  *.html  *.slim  *.haml  *.ejs  *.css  *.less  *.md  *.mdx  *.markdown  *.rmd  *.json  *.webmanifest  *.js  *.mjs  *.jsx  *.rb  *.gemspec  *.rake  *.php  *.py  *.pyc  *.pyo  *.pyd  *.coffee  *.mustache  *.hbs  *.conf  *.ini  *.yml  *.yaml  *.toml  *.bat  *.mk  *.jpg  *.jpeg  *.bmp  *.png  *.webp  *.gif  *.ico  *.twig  *.cpp  *.c++  *.cxx  *.cc  *.cp  *.c  *.cs  *.h  *.hh  *.hpp  *.hxx  *.hs  *.lhs  *.nix  *.lua  *.java  *.sh  *.fish  *.bash  *.zsh  *.ksh  *.csh  *.awk  *.ps1  *.ml λ *.mli λ *.diff  *.db  *.sql  *.dump  *.clj  *.cljc  *.cljs  *.edn  *.scala  *.go  *.dart  *.xul  *.sln  *.suo  *.pl  *.pm  *.t  *.rss  '*.f#'  *.fsscript  *.fsx  *.fs  *.fsi  *.rs  *.rlib  *.d  *.erl  *.hrl  *.ex  *.exs  *.eex  *.leex  *.heex  *.vim  *.ai  *.psd  *.psb  *.ts  *.tsx  *.jl  *.pp  *.vue  *.elm  *.swift  *.xcplayground  *.tex ﭨ *.r ﳒ *.rproj 鉶 *.sol ﲹ *.pem  # file names (vim-devicons) (case-insensitive not supported in lf) *gruntfile.coffee  *gruntfile.js  *gruntfile.ls  *gulpfile.coffee  *gulpfile.js  *gulpfile.ls  *mix.lock  *dropbox  *.ds_store  *.gitconfig  *.gitignore  *.gitattributes  *.gitlab-ci.yml  *.bashrc  *.zshrc  *.zshenv  *.zprofile  *.vimrc  *.gvimrc  *_vimrc  *_gvimrc  *.bashprofile  *favicon.ico  *license  *node_modules  *react.jsx  *procfile  *dockerfile  *docker-compose.yml  *rakefile  *config.ru  *gemfile  *makefile  *cmakelists.txt  *robots.txt ﮧ # file names (case-sensitive adaptations) *Gruntfile.coffee  *Gruntfile.js  *Gruntfile.ls  *Gulpfile.coffee  *Gulpfile.js  *Gulpfile.ls  *Dropbox  *.DS_Store  *LICENSE  *React.jsx  *Procfile  *Dockerfile  *Docker-compose.yml  *Rakefile  *Gemfile  *Makefile  *CMakeLists.txt  # file patterns (vim-devicons) (patterns not supported in lf) # .*jquery.*\.js$  # .*angular.*\.js$  # .*backbone.*\.js$  # .*require.*\.js$  # .*materialize.*\.js$  # .*materialize.*\.css$  # .*mootools.*\.js$  # .*vimrc.*  # Vagrantfile$  # file patterns (file name adaptations) *jquery.min.js  *angular.min.js  *backbone.min.js  *require.min.js  *materialize.min.js  *materialize.min.css  *mootools.min.js  *vimrc  Vagrantfile  # archives or compressed (extensions from dircolors defaults) *.tar  *.tgz  *.arc  *.arj  *.taz  *.lha  *.lz4  *.lzh  *.lzma  *.tlz  *.txz  *.tzo  *.t7z  *.zip  *.z  *.dz  *.gz  *.lrz  *.lz  *.lzo  *.xz  *.zst  *.tzst  *.bz2  *.bz  *.tbz  *.tbz2  *.tz  *.deb  *.rpm  *.jar  *.war  *.ear  *.sar  *.rar  *.alz  *.ace  *.zoo  *.cpio  *.7z  *.rz  *.cab  *.wim  *.swm  *.dwm  *.esd  # image formats (extensions from dircolors defaults) *.jpg  *.jpeg  *.mjpg  *.mjpeg  *.gif  *.bmp  *.pbm  *.pgm  *.ppm  *.tga  *.xbm  *.xpm  *.tif  *.tiff  *.png  *.svg  *.svgz  *.mng  *.pcx  *.mov  *.mpg  *.mpeg  *.m2v  *.mkv  *.webm  *.ogm  *.mp4  *.m4v  *.mp4v  *.vob  *.qt  *.nuv  *.wmv  *.asf  *.rm  *.rmvb  *.flc  *.avi  *.fli  *.flv  *.gl  *.dl  *.xcf  *.xwd  *.yuv  *.cgm  *.emf  *.ogv  *.ogx  # audio formats (extensions from dircolors defaults) *.aac  *.au  *.flac  *.m4a  *.mid  *.midi  *.mka  *.mp3  *.mpc  *.ogg  *.ra  *.wav  *.oga  *.opus  *.spx  *.xspf  # other formats *.pdf  ================================================ FILE: home/_modules/shell/lf/default.nix ================================================ { pkgs, lib, config, ... }: let myHome = config.myHome; cfg = myHome.shell.lf; in { options.myHome.shell.lf = { enable = lib.mkEnableOption "lf"; }; config = lib.mkIf (cfg.enable) { myHome.shell.dircolors.enable = true; xdg.configFile."lf/colors".source = ./configs/colors; xdg.configFile."lf/icons".source = ./configs/icons; programs.lf = { enable = true; keybindings = { "D" = "delete"; "n" = ""; "nd" = "push :newdir"; "nf" = "push :newfile"; "gf" = ":fzf_search"; "gz" = "push :z"; }; extraConfig = '' # previews set previewer ${pkgs.ctpv}/bin/ctpv set cleaner ${pkgs.ctpv}/bin/ctpvclear &${pkgs.ctpv}/bin/ctpv -s $id &${pkgs.ctpv}/bin/ctpvquit $id cmd newdir %{{ ${pkgs.coreutils}/bin/mkdir "$@" lf -remote "send $id select \"$@\"" }} cmd newfile %{{ ${pkgs.coreutils}/bin/touch "$@" lf -remote "send $id select \"$@\"" }} # zoxide cmd z %{{ result="$(${pkgs.zoxide}/bin/zoxide query --exclude $PWD $@)" lf -remote "send $id cd \"$result\"" }} # fzf with ripgrep cmd fzf_search ''${{ res="$( \ RG_PREFIX="${pkgs.ripgrep}/bin/rg --column --line-number --no-heading --color=always \ --smart-case " FZF_DEFAULT_COMMAND="$RG_PREFIX '''" \ ${pkgs.fzf}/bin/fzf --bind "change:reload:$RG_PREFIX {q} || true" \ --ansi --layout=reverse --header 'Search in files' \ | cut -d':' -f1 )" [ ! -z "$res" ] && lf -remote "send $id select \"$res\"" }} # opening files cmd open ''${{ case $(${pkgs.file}/bin/file --mime-type -Lb "$f") in # text files text/*|application/json) $EDITOR "$fx";; # archive files application/x-bzip*|application/*zip|application/x-xz|application/x-rar|application/x-*-image) mntdir="$f-archivemount" [ ! -d "$mntdir" ] && { mkdir "$mntdir" ${pkgs.archivemount}/bin/archivemount "$f" "$mntdir" echo "$mntdir" >> "/tmp/__lf_archivemount_$id" } lf -remote "send $id cd \"$mntdir\"" lf -remote "send $id reload" ;; esac }} # umount archives on quit cmd on-quit ''${{ archivemount_dir="/tmp/__lf_archivemount_$id" if [ -f "$archivemount_dir" ]; then cat "$archivemount_dir" | \ while read -r line; do /run/wrappers/bin/sudo ${pkgs.umount}/bin/umount "$line" ${pkgs.coreutils}/bin/rmdir "$line" done rm -f "$archivemount_dir" fi }} # dynamically set number of columns ''${{ w=$(${pkgs.ncurses}/bin/tput cols) if [ "$w" -le 80 ]; then lf -remote "send $id set ratios 1:2" elif [ "$w" -le 160 ]; then lf -remote "send $id set ratios 1:2:3" else lf -remote "send $id set ratios 1:2:3:5" fi }} ''; settings = { icons = true; drawbox = true; }; }; home.packages = with pkgs; [ chafa poppler-utils ffmpegthumbnailer ]; }; } ================================================ FILE: home/_modules/shell/nix-direnv/default.nix ================================================ { config, lib, ... }: let cfg = config.myHome.shell.nix-direnv; in { options.myHome.shell.nix-direnv = { enable = lib.mkEnableOption "nix-direnv"; }; config = lib.mkIf (cfg.enable) { programs.direnv = { enable = true; nix-direnv.enable = true; }; home.sessionVariables.DIRENV_LOG_FORMAT = ""; }; } ================================================ FILE: home/_modules/shell/starship/default.nix ================================================ { config, lib, pkgs, ... }: let cfg = config.myHome.shell.starship; in { options.myHome.shell.starship = { enable = lib.mkEnableOption "Starship cross-shell prompt"; }; config = lib.mkIf (cfg.enable) { # TODO: wait for the IFD issue is fixed # ref: https://github.com/catppuccin/nix/issues/392 catppuccin.starship.enable = lib.mkIf (pkgs.stdenv.hostPlatform.system == "x86_64-linux") true; programs.starship = { enable = true; settings = { add_newline = false; command_timeout = 1000; format = lib.concatStrings [ "$os" "$username" "$hostname" "$directory" "$git_branch" "$git_status" "$fill" "$direnv" "$nix_shell" "$python" "$cmd_duration" "$status" "$line_break" "$character" ]; os = { disabled = false; format = "[](fg:blue)[ $symbol ](fg:mantle bg:blue)[](fg:blue bg:surface0)"; symbols = { Arch = "󰣇 "; NixOS = "󱄅 "; Ubuntu = " "; }; }; username = { style_user = "fg:peach bg:surface0"; style_root = "fg:red bg:surface0"; format = "[ $user]($style)"; show_always = false; }; hostname = { ssh_only = true; format = "[@$hostname](fg:green bg:surface0)"; }; directory = { truncation_length = 3; format = "[ in](fg:text bg:surface0)[ $path ](fg:blue bg:surface0)([$read_only ](fg:red bg:surface0))[](fg:surface0)"; read_only = ""; truncation_symbol = "../"; truncate_to_repo = true; fish_style_pwd_dir_length = 1; }; git_branch = { format = "[ $symbol$branch ](fg:mauve)"; symbol = " "; }; git_status = { format = "[$all_status$ahead_behind](fg:mauve)"; conflicted = " "; ahead = " "; behind = " "; diverged = "󰆗 "; up_to_date = " "; untracked = " "; stashed = " "; modified = " "; staged = " "; renamed = " "; deleted = " "; }; fill = { symbol = " "; }; direnv = { disabled = false; format = "[$symbol\\($loaded/$allowed\\) ](fg:blue)"; symbol = " "; }; nix_shell = { format = "[$symbol(\\($name\\)) ](fg:blue)"; heuristic = true; # needed to detect `nix shell` symbol = "󱄅 "; # the default unicode is causing issue https://github.com/starship/starship/issues/5924 }; python = { format = "[\${symbol}\${pyenv_prefix}(\${version} )(\($virtualenv\) )]($style)"; symbol = "🐍 "; }; cmd_duration = { min_time = 0; format = "[](fg:surface0)[ took](fg:text bg:surface0)[ $duration ](fg:yellow bg:surface0)"; }; status = { disabled = false; format = "[](fg:blue bg:surface0)[ $symbol](fg:mantle bg:blue)[](fg:blue)"; symbol = " "; success_symbol = " "; }; character = { success_symbol = "[](green)"; error_symbol = "[](green)"; vicmd_symbol = "[](mauve)"; }; }; }; }; } ================================================ FILE: home/_modules/terminal-emulator/alacritty/default.nix ================================================ { lib, config, pkgs, ... }: with lib; let cfg = config.myHome.terminal-emulator.alacritty; in { options.myHome.terminal-emulator.alacritty = { enable = mkEnableOption "alacritty"; }; config = mkIf (cfg.enable) { catppuccin.alacritty.enable = true; programs.alacritty = { enable = true; package = (config.lib.nixGL.wrap pkgs.alacritty); settings = { general.live_config_reload = true; env.TERM = "xterm-256color"; window = { padding = { x = 10; y = 10; }; dynamic_padding = true; decorations = "none"; startup_mode = "Maximized"; }; scrolling.history = 10000; font = { size = 12.0; normal = { family = "UbuntuMono Nerd Font"; style = "Regular"; }; }; colors = { draw_bold_text_with_bright_colors = false; }; bell.duration = 0; }; }; }; } ================================================ FILE: home/_modules/terminal-emulator/contour/config/contour.yml ================================================ --- on_mouse_select: CopyToSelectionClipboard profiles: main: show_title_bar: false maximized: true environment: TERM: xterm-256color COLORTERM: truecolor margins: horizontal: 10 vertical: 10 history: limit: 10000 auto_scroll_on_update: true scroll_multiplier: 3 scrollbar: position: Hidden mouse: hide_while_typing: true font: # using 12 causes the underscore and long characters to cut off # https://github.com/contour-terminal/contour/issues/1603 size: 12.1 regular: family: UbuntuMono Nerd Font weight: normal slant: normal emoji: emoji draw_bold_text_with_bright_colors: false cursor: shape: rectangle blinking: false vi_mode_highlight_timeout: 300 vi_mode_scrolloff: 8 status_line: display: none colors: catppuccin_mocha hyperlink_decoration: normal: dotted hover: underline color_schemes: catppuccin_mocha: default: background: "#1E1E2E" # base foreground: "#CDD6F4" # text cursor: default: "#F5E0DC" # rosewater text: "#1E1E2E" # base normal: black: "#45475A" # surface1 red: "#F38BA8" # red green: "#A6E3A1" # green yellow: "#F9E2AF" # yellow blue: "#89B4FA" # blue magenta: "#F5C2E7" # pink cyan: "#94E2D5" # teal white: "#BAC2DE" # subtext1 bright: black: "#585B70" # surface2 red: "#F38BA8" # red green: "#A6E3A1" # green yellow: "#F9E2AF" # yellow blue: "#89B4FA" # blue magenta: "#F5C2E7" # pink cyan: "#94E2D5" # teal white: "#A6ADC8" # subtext0 vi_mode_highlight: foreground: CellForeground background: "#1E1E2E" # base vi_mode_cursorline: foreground: CellForeground background: "#585B70" # surface2 background_alpha: 0.1 selection: foreground: CellForeground background: "#585B70" # surface2 indicator_statusline: foreground: CellForeground background: "#89B4FA" # blue input_mapping: - { mods: [Control, Shift], key: Plus, action: IncreaseFontSize } - { mods: [Control, Shift], key: Minus, action: DecreaseFontSize } - { mods: [Control], key: Backspace, action: ResetFontSize } - { mods: [Control, Shift], key: V, action: PasteClipboard, strip: false } - { mods: [Control, Shift], key: C, action: ViNormalMode, mode: "Insert" } ================================================ FILE: home/_modules/terminal-emulator/contour/default.nix ================================================ { config, lib, pkgs, ... }: let cfg = config.myHome.terminal-emulator.contour; in { options.myHome.terminal-emulator.contour = { enable = lib.mkEnableOption "Contour Terminal Emulator"; }; config = lib.mkIf (cfg.enable) { home.packages = [ (config.lib.nixGL.wrap pkgs.contour) ]; xdg.configFile."contour".source = ./config; }; } ================================================ FILE: home/_modules/terminal-emulator/kitty/default.nix ================================================ { config, lib, pkgs, ... }: let cfg = config.myHome.terminal-emulator.kitty; in { options.myHome.terminal-emulator.kitty = { enable = lib.mkEnableOption "kitty"; }; config = lib.mkIf (cfg.enable) { programs.kitty = { enable = true; package = (config.lib.nixGL.wrap pkgs.kitty); catppuccin.enable = true; font = { name = "UbuntuMono Nerd Font"; size = 12; }; settings = { # Cursor cursor = "#C0CAF5"; cursor_text_color = "#202124"; cursor_shape = "underline"; cursor_blink_interval = "-1"; # Scrollback scrollback_lines = 10000; # Mouse mouse_hide_wait = "-1"; url_color = "#73DACA"; url_style = "curly"; #Terminal bell enable_audio_bell = "no"; visual_bell_duration = 0; # Window layout window_padding_width = 4; confirm_os_window_close = 0; # Advanced allow_remote_control = "no"; shell_integration = "disabled"; term = "xterm-256color"; }; }; }; } ================================================ FILE: home/_modules/terminal-emulator/wezterm/config/wezterm.lua ================================================ local wezterm = require 'wezterm' local act = wezterm.action -- Start window maximized wezterm.on("gui-startup", function(cmd) local _, _, window = wezterm.mux.spawn_window(cmd or {}) window:gui_window():maximize() end) return { -- Fonts font = wezterm.font("UbuntuMono Nerd Font"), font_size = 12.0, -- Appearance color_scheme = "Catppuccin Mocha", window_decorations = "RESIZE", enable_tab_bar = false, enable_scroll_bar = false, warn_about_missing_glyphs = false, window_close_confirmation = "NeverPrompt", enable_wayland = false, -- this fixes weird issues that I don't understand why it's happening -- Window layout window_padding = { left = "1cell", right = "1cell", top = "0.5cell", bottom = "0.5cell", }, -- Keyboard shortcuts disable_default_key_bindings = true, keys = { { key = "C", mods = "CTRL|SHIFT", action = act.ActivateCopyMode }, { key = "V", mods = "CTRL|SHIFT", action = act.PasteFrom("Clipboard") }, { key = "+", mods = "CTRL|SHIFT", action = act.IncreaseFontSize }, { key = "_", mods = "CTRL|SHIFT", action = act.DecreaseFontSize }, { key = "Backspace", mods = "CTRL|SHIFT", action = act.ResetFontSize }, }, } ================================================ FILE: home/_modules/terminal-emulator/wezterm/default.nix ================================================ { config, lib, pkgs, ... }: let cfg = config.myHome.terminal-emulator.wezterm; in { options.myHome.terminal-emulator.wezterm = { enable = lib.mkEnableOption "WezTerm"; }; config = lib.mkIf (cfg.enable) { programs.wezterm = { enable = true; package = (config.lib.nixGL.wrap pkgs.wezterm); extraConfig = builtins.readFile ./config/wezterm.lua; }; home.packages = [ (config.lib.nixGL.wrap pkgs.contour) ]; }; } ================================================ FILE: home/_modules/windowmanager/add-on/blueman-applet/default.nix ================================================ { config, lib, ... }: let myHome = config.myHome; cfg = myHome.windowmanager.add-on.blueman-applet; in { options.myHome.windowmanager.add-on.blueman-applet = { enable = lib.mkEnableOption "blueman-applet"; }; config = lib.mkIf (cfg.enable) { services.blueman-applet.enable = true; }; } ================================================ FILE: home/_modules/windowmanager/add-on/dunst/default.nix ================================================ { config, lib, pkgs, ... }: let cfg = config.myHome.windowmanager.add-on.dunst; in { options.myHome.windowmanager.add-on.dunst = { enable = lib.mkEnableOption "dunst"; }; config = lib.mkIf (cfg.enable) { home.packages = [ pkgs.libnotify ]; services.dunst = { enable = true; settings = { global = { follow = "mouse"; width = 300; height = 300; origin = "top-right"; offset = "10x10"; scale = 0; notification_limit = 10; progress_bar = true; progress_bar_height = 10; progress_bar_frame_width = 1; progress_bar_min_width = 150; progress_bar_max_width = 300; indicate_hidden = "yes"; transparency = 0; separator_height = 2; padding = 8; horizontal_padding = 8; text_icon_padding = 0; frame_width = 1; frame_color = "#C0CAF5"; separator_color = "frame"; sort = "yes"; idle_threshold = 120; font = "UbuntuMono Nerd Font 12"; line_height = 0; markup = "full"; format = "%s\\n%b"; alignment = "left"; vertical_alignment = "center"; show_age_threshold = 60; ellipsize = "middle"; ignore_newline = "no"; stack_duplicates = false; hide_duplicate_count = false; show_indicators = "yes"; icon_position = "left"; min_icon_size = 16; max_icon_size = 16; icon_path = "/usr/share/icons/Vimix-Doder-dark/16/actions/:/usr/share/icons/Vimix-Doder-dark/16/devices:/usr/share/icons/Vimix-Doder-dark/places/:/usr/share/icons/Vimix-Doder-dark/16/mimetypes/:/usr/share/icons/Vimix-Doder-dark/16/panel/:/usr/share/icons/Vimix-Doder-dark/16/status/"; sticky_history = "yes"; history_length = 20; dmenu = "/usr/bin/dmenu -p dunst:"; browser = "$BROWSER"; always_run_script = true; title = "Dunst"; class = "Dunst"; corner_radius = 5; ignore_dbusclose = false; layer = "top"; force_xwayland = false; force_xinerama = false; mouse_left_click = "do_action"; mouse_middle_click = "close_current"; mouse_right_click = "close_current"; }; experimental = { per_monitor_dpi = false; }; urgency_low = { background = "#1A1B26"; foreground = "#7AA2F7"; timeout = 10; }; urgency_normal = { background = "#1A1B26"; foreground = "#9ECE6A"; timeout = 10; }; urgency_critical = { background = "#1A1B26"; foreground = "#F7768E"; timeout = 0; }; }; }; }; } ================================================ FILE: home/_modules/windowmanager/add-on/gtk-theme/default.nix ================================================ { config, lib, pkgs, myPkgs, ... }: let cfg = config.myHome.windowmanager.add-on.gtk-theme; in { options.myHome.windowmanager.add-on.gtk-theme = { enable = lib.mkEnableOption "gtk-theme"; }; config = lib.mkIf (cfg.enable) { home.pointerCursor = { package = pkgs.catppuccin-cursors.mochaMauve; name = "catppuccin-mocha-mauve-cursors"; size = 24; gtk.enable = true; x11.enable = true; }; gtk = { enable = true; font = { name = "UbuntuMono Nerd Font"; package = pkgs.nerd-fonts.ubuntu-mono; size = 12; }; theme = { name = "Tokyonight-Dark"; package = myPkgs.tokyonight-gtk-theme; }; iconTheme = { name = "Tokyonight-Dark"; package = myPkgs.tokyonight-icon-theme; }; }; }; } ================================================ FILE: home/_modules/windowmanager/add-on/nm-applet/default.nix ================================================ { config, lib, ... }: let cfg = config.myHome.windowmanager.add-on.nm-applet; in { options.myHome.windowmanager.add-on.nm-applet = { enable = lib.mkEnableOption "nm-applet"; }; config = lib.mkIf (cfg.enable) { services.network-manager-applet.enable = true; xsession.preferStatusNotifierItems = true; }; } ================================================ FILE: home/_modules/windowmanager/add-on/nwg-bar/default.nix ================================================ { config, lib, pkgs, ... }: let cfg = config.myHome.windowmanager.add-on.nwg-bar; in { options.myHome.windowmanager.add-on.nwg-bar = { enable = lib.mkEnableOption "nwg-bar"; }; config = lib.mkIf (cfg.enable) { myHome.windowmanager.add-on.swaylock.enable = true; home.packages = [ pkgs.nwg-bar ]; xdg.configFile = { "nwg-bar/bar.json".text = '' [ { "label": "_Lock", "exec": "${config.programs.swaylock.package}/bin/swaylock", "icon": "${pkgs.nwg-bar}/share/nwg-bar/images/system-lock-screen.svg" }, { "label": "_Exit", "exec": "${ if config.wayland.windowManager.sway.enable then "swaymsg exit" else if config.wayland.windowManager.hyprland.enable then "hyprctl dispatch exit" else "" }", "icon": "${pkgs.nwg-bar}/share/nwg-bar/images/system-log-out.svg" }, { "label": "_Reboot", "exec": "systemctl reboot", "icon": "${pkgs.nwg-bar}/share/nwg-bar/images/system-reboot.svg" }, { "label": "_Shutdown", "exec": "systemctl -i poweroff", "icon": "${pkgs.nwg-bar}/share/nwg-bar/images/system-shutdown.svg" }, { "label": "UE_FI", "exec": "systemctl reboot --firmware-setup", "icon": "${pkgs.nwg-bar}/share/nwg-bar/images/system-reboot.svg" } ] ''; "nwg-bar/style.css".text = '' window { border: 1px solid #C0CAF5; border-radius: 10px; background-color: #1A1B26; } button, image { color: #C0CAF5; background: none; border: none; box-shadow: none; } button { padding-left: 10px; padding-right: 10px; margin: 5px; } button:hover { background-color: #16161E } ''; }; }; } ================================================ FILE: home/_modules/windowmanager/add-on/pasystray/default.nix ================================================ { config, lib, ... }: let cfg = config.myHome.windowmanager.add-on.pasystray; in { options.myHome.windowmanager.add-on.pasystray = { enable = lib.mkEnableOption "pasystray"; }; config = lib.mkIf (cfg.enable) { services.pasystray.enable = true; }; } ================================================ FILE: home/_modules/windowmanager/add-on/picom/default.nix ================================================ { config, lib, ... }: let cfg = config.myHome.windowmanager.add-on.picom; in { options.myHome.windowmanager.add-on.picom = { enable = lib.mkEnableOption "picom"; }; config = lib.mkIf (cfg.enable) { services.picom = { enable = true; settings = { # Shadows shadow = true; shadow-radius = 7; shadow-opacity = 0.7; shadow-offset-x = -7; shadow-offset-y = -7; shadow-red = 0.8; shadow-green = 0; shadow-blue = 0; shadow-exclude = [ "name = 'Notification'" "class_g = 'Conky'" "_NET_WM_STATE@:32a *= '_NET_WM_STATE_HIDDEN'" "_GTK_FRAME_EXTENTS@:c" #"_NET_WM_STATE@:32a *= '_NET_WM_STATE_STICKY'", "class_g ?= 'i3-frame'" "class_g ?= 'Dunst'" ]; # shadow-exclude-reg = "" # xinerama-shadow-crop = false # xinerama-shadow-crop = true; # Fading fading = true; fade-in-step = 3.0e-2; fade-out-step = 3.0e-2; # fade-delta = 10 fade-exclude = [ "_NET_WM_STATE@:32a *= '_NET_WM_STATE_FULLSCREEN'" ]; # no-fading-openclose = false # no-fading-destroyed-argb = false # Transparency / Opacity inactive-opacity = 0.98; frame-opacity = 1.0; inactive-opacity-override = false; active-opacity = 1.0; inactive-dim = 0.3; focus-exclude = [ "class_g = 'Cairo-clock'" "_NET_WM_STATE@:32a *= '_NET_WM_STATE_FULLSCREEN'" ]; inactive-dim-fixed = 1.0; opacity-rule = [ "50:class_g = 'Dmenu'" "93:class_g = 'URxvt' && !_NET_WM_STATE@:32a" "0:_NET_WM_STATE@:32a *= '_NET_WM_STATE_HIDDEN'" "93:class_g = 'Gnome-terminal'" "93:class_g = 'Thunar'" ]; # Corners corner-radius = 0; rounded-corners-exclude = [ "window_type = 'dock'" "window_type = 'desktop'" ]; # Background-Blurring # blur-method = # blur-size = 12 # blur-deviation = false # blur-strength = 5 # blur-background = false # blur-background-frame = false # blur-background-fixed = false blur-kern = "3x3box"; blur-background-exclude = [ "window_type = 'dock'" "window_type = 'desktop'" "_GTK_FRAME_EXTENTS@:c" ]; # General Settings # daemon = false backend = "xrender"; # vsync = true; # dbus = false mark-wmwin-focused = true; mark-ovredir-focused = true; detect-rounded-corners = true; detect-client-opacity = true; use-ewmh-active-win = true; unredir-if-possible = false; unredir-if-possible-delay = 5000; # miliseconds unredir-if-possible-exclude = [ ]; detect-transient = true; detect-client-leader = true; # resize-damage = 1 invert-color-include = [ ]; glx-no-stencil = true; glx-no-rebind-pixmap = true; use-damage = true; # xrender-sync-fence = false # glx-fshader-win = "" # force-win-blend = false # no-ewmh-fullscreen = false # max-brightness = 1.0 # transparent-clipping = false log-level = "info"; # log-file = "/path/to/your/log/file" # show-all-xerrors = false # write-pid-path = "/path/to/your/log/file" wintypes = { tooltip = { fade = true; shadow = false; opacity = 0.85; focus = true; }; dock = { shadow = false; }; dnd = { shadow = false; }; popup_menu = { opacity = 0.8; }; dropdown_menu = { opacity = 0.9; shadow = false; }; }; }; }; }; } ================================================ FILE: home/_modules/windowmanager/add-on/py3status/config ================================================ order += "frame net" order += "net_rate" order += "frame disks" order += "uname" order += "frame timedate" order += "whoami" general { separator_block_width = 15 } net_rate { format = "[\?color=down  {down}] [\?color=up  {up}]" format_value = "{value:.0f}{unit}" thresholds = { 'down': [ (0, "#7AA2F7"), (1024, "#7AA2F7"), (1048576, "#7AA2F7"), ], 'up': [ (0, "#F7768E"), (1024, "#F7768E"), (1048576, "#F7768E"), ], } } frame disks { static_string { separator = false separator_block_width = 5 format = shell(printf " " && df -h ~ | tail -n 1 | awk '{$1="";$6="";print $0}' | awk -F ' ' '{print $3}', str) color = "#9ECE6A" } static_string { format = shell(printf " " && df -h / | tail -n 1 | awk '{$1="";$6="";print $0}' | awk -F ' ' '{print $3}', str) color = "#F7768E" } } uname { format = " {release}" color = "#E0AF68" } frame timedate { clock { format = " {Asia/Jakarta}" format_time = "%a, %d %b %y" color = "#BB9AF7" } clock { format = " {Asia/Jakarta}" format_time = "%I:%M:%S %p" color = "#9ECE6A" } } whoami { format = " {username}" color = "#7AA2F7" } ================================================ FILE: home/_modules/windowmanager/add-on/py3status/default.nix ================================================ { config, lib, ... }: let cfg = config.myHome.windowmanager.add-on.py3status; in { options.myHome.windowmanager.add-on.py3status = { enable = lib.mkEnableOption "py3status"; }; config = lib.mkIf (cfg.enable) { xdg.configFile."py3status/config".source = ./config; }; } ================================================ FILE: home/_modules/windowmanager/add-on/rofi/default.nix ================================================ { config, lib, pkgs, ... }: let myHome = config.myHome; cfg = myHome.windowmanager.add-on.rofi; in { options.myHome.windowmanager.add-on.rofi = { enable = lib.mkEnableOption "rofi"; }; config = lib.mkIf (cfg.enable) { programs.rofi = { enable = true; extraConfig = { font = "UbuntuMono Nerd Font 12"; display-drun = " "; show-icons = true; drun-display-format = "{name}"; disable-history = false; sidebar-mode = false; }; theme = ./style.rasi; }; }; } ================================================ FILE: home/_modules/windowmanager/add-on/rofi/style.rasi ================================================ * { selected-normal-foreground: #1A1B26; foreground: #C0CAF5; normal-foreground: @foreground; alternate-normal-background: @background; selected-urgent-foreground: @selected-normal-foreground; urgent-foreground: @selected-normal-foreground; alternate-urgent-background: @selected-normal-background; active-foreground: #C0CAF5; selected-active-foreground: @active-foreground; alternate-active-background: #1A1B26; background: #1A1B26; alternate-normal-foreground: @foreground; normal-background: @background; selected-normal-background: #7AA2F7; spacing: 2px; separatorcolor: #C0CAF5; urgent-background: #F7768E; selected-urgent-background: @selected-normal-background; alternate-urgent-foreground: @urgent-foreground; background-color: @background; alternate-active-foreground: @active-foreground; active-background: @background; selected-active-background: @selected-normal-background; prompt-foreground: #F7768E; } #window { background-color: @background; border-color: @separatorcolor; border: 1px; border-radius: 5px; padding: 5px; width: 25%; } #mainbox { border: 0; padding: 2px; } #message { border: 2px 0px 0px; border-color: @separatorcolor; padding: 1px; } #textbox { text-color: @foreground; } #listview { fixed-height: 0; border: 1px 0px 0px ; border-color: @separatorcolor; spacing: 5px; scrollbar: false; padding: 10px 0px 0px ; lines: 10; } #element { border: 0; padding: 3px; cursor: pointer; spacing: 5px; } #element.normal.normal { background-color: @normal-background; text-color: @normal-foreground; } #element.normal.urgent { background-color: @urgent-background; text-color: @urgent-foreground; } #element.normal.active { background-color: @active-background; text-color: @active-foreground; } #element.selected.normal { background-color: @selected-normal-background; text-color: @selected-normal-foreground; } #element.selected.urgent { background-color: @selected-urgent-background; text-color: @selected-urgent-foreground; } #element.selected.active { background-color: @selected-active-background; text-color: @selected-active-foreground; } #element.alternate.normal { background-color: @alternate-normal-background; text-color: @alternate-normal-foreground; } #element.alternate.urgent { background-color: @alternate-urgent-background; text-color: @alternate-urgent-foreground; } #element.alternate.active { background-color: @alternate-active-background; text-color: @alternate-active-foreground; } #element-text { background-color: transparent; cursor: inherit; highlight: inherit; text-color: inherit; } #element-icon { background-color: transparent; size: 1em; cursor: inherit; text-color: inherit; } #scrollbar { width: 4px ; border: 0; handle-width: 8px ; padding: 0; handle-color: var(normal-foreground); } #sidebar { border-color: @separatorcolor; border: 2px dash 0 0; } #mode-switcher { border: 2px 0px 0px ; border-color: @separatorcolor; } #button { cursor: pointer; spacing: 0; text-color: @normal-foreground; } #button.selected { background-color: @selected-normal-background; text-color: @selected-normal-foreground; } #inputbar { spacing: 5px; text-color: @normal-foreground; padding: 3px ; } #case-indicator { spacing: 0; text-color: @normal-foreground; } #entry { spacing: 0; text-color: @normal-foreground; } #prompt { spacing: 0; text-color: @prompt-foreground; } #textbox-prompt-colon { expand: false; str: ":"; text-color: @prompt-foreground; } #inputbar { children: [prompt,entry,case-indicator]; } ================================================ FILE: home/_modules/windowmanager/add-on/screenshotter/default.nix ================================================ { config, pkgs, lib, ... }: let cfg = config.myHome.windowmanager.add-on.screenshotter; wl-full-screenshot = pkgs.writeShellScriptBin "wl-full-screenshot" '' IMG=~/Desktop/$(date +%Y%m%d_%Hh%mm%Ss).png ${pkgs.grim}/bin/grim $IMG \ && ${pkgs.wl-clipboard}/bin/wl-copy < "$IMG" \ && ${pkgs.pipewire}/bin/pw-cat -p ${./camera-shutter.oga} \ && ${pkgs.libnotify}/bin/notify-send "Screenshot copied to clipboard and saved in your Desktop folder" ''; wl-region-screenshot = pkgs.writeShellScriptBin "wl-region-screenshot" '' IMG=~/Desktop/$(date +%Y%m%d_%Hh%mm%Ss).png ${pkgs.grim}/bin/grim -g "$(${pkgs.slurp}/bin/slurp)" "$IMG" \ && ${pkgs.wl-clipboard}/bin/wl-copy < "$IMG" \ && ${pkgs.pipewire}/bin/pw-cat -p ${./camera-shutter.oga} \ && ${pkgs.libnotify}/bin/notify-send "Screenshot copied to clipboard and saved in your Desktop folder" ''; x-full-screenshot = pkgs.writeShellScriptBin "x-full-screenshot" '' IMG=~/Desktop/$(date +%Y%m%d_%Hh%mm%Ss).png ${pkgs.scrot}/bin/scrot -e '${pkgs.xclip}/bin/xclip -sel clipboard -t image/png -i $f' -F "$IMG" \ && ${pkgs.pipewire}/bin/pw-cat -p ${./camera-shutter.oga} \ && ${pkgs.libnotify}/bin/notify-send "Screenshot copied to clipboard and saved in your Desktop folder" ''; x-region-screenshot = pkgs.writeShellScriptBin "x-region-screenshot" '' IMG=~/Desktop/$(date +%Y%m%d_%Hh%mm%Ss).png ${pkgs.scrot}/bin/scrot -s -e '${pkgs.xclip}/bin/xclip -sel clipboard -t image/png -i $f' -F "$IMG" \ && ${pkgs.pipewire}/bin/pw-cat -p ${./camera-shutter.oga} \ && ${pkgs.libnotify}/bin/notify-send "Screenshot copied to clipboard and saved in your Desktop folder" ''; modeName = "Screenshot: (Enter) Full screenshot, (s) Select region"; in { options.myHome.windowmanager.add-on.screenshotter = { enable = lib.mkOption { type = lib.types.bool; default = false; description = '' Enable screenshotter. This module will let you go to `screenshot mode` on `Printscreen` keypress in. Once in the mode, you can press `Enter` to take full screenshot or `r` to take region screenshot with mouse selection. The screenshot will be saved in `~/Desktop` directory and in your clipboard so you can paste the picture in applications. Only works in i3, Hyprland, and Sway window manager. ''; }; }; config = lib.mkIf (cfg.enable) { wayland.windowManager = { hyprland = { extraConfig = '' submap = ${modeName} bind = , return, execr, ${lib.getExe wl-full-screenshot} bind = , return, submap, reset bind = , s, exec, ${lib.getExe wl-region-screenshot} bind = , s, submap, reset # return to normal mode bind = , escape, submap, reset submap = reset ''; settings.bind = [ ", print, submap, ${modeName}" ]; }; sway.config = { modes.${modeName} = { Return = "exec ${lib.getExe wl-full-screenshot}, mode default"; "--release s" = "exec ${lib.getExe wl-region-screenshot}, mode default"; Escape = "mode default"; }; keybindings = { Print = "mode \"${modeName}\""; }; }; }; xsession.windowManager.i3.config = { modes.${modeName} = { Return = "exec ${lib.getExe x-full-screenshot}, mode default"; "--release s" = "exec ${lib.getExe x-region-screenshot}, mode default"; Escape = "mode default"; }; keybindings = { Print = "mode \"${modeName}\""; }; }; }; } ================================================ FILE: home/_modules/windowmanager/add-on/swayidle/default.nix ================================================ { config, lib, pkgs, ... }: let cfg = config.myHome.windowmanager.add-on.swayidle; wmCfg = config.wayland.windowManager; in with lib; { options.myHome.windowmanager.add-on.swayidle = { enable = mkEnableOption "Swayidle"; }; config = mkIf (cfg.enable) { services.swayidle = { enable = true; timeouts = [ { timeout = 1800; command = ( mkMerge [ (mkIf wmCfg.sway.enable "${pkgs.sway}/bin/swaymsg \"output * dpms off\"") (mkIf wmCfg.hyprland.enable "${wmCfg.hyprland.finalPackage}/bin/hyprctl dispatch dpms off") ] ); resumeCommand = ( mkMerge [ (mkIf wmCfg.sway.enable "${pkgs.sway}/bin/swaymsg \"output * dpms on\"") (mkIf wmCfg.hyprland.enable "${wmCfg.hyprland.finalPackage}/bin/hyprctl dispatch dpms on") ] ); } ]; systemdTarget = mkIf wmCfg.hyprland.enable "hyprland-session.target"; }; }; } ================================================ FILE: home/_modules/windowmanager/add-on/swaylock/default.nix ================================================ { config, lib, pkgs, ... }: let cfg = config.myHome.windowmanager.add-on.swaylock; in { options.myHome.windowmanager.add-on.swaylock = { enable = lib.mkEnableOption "Swaylock"; }; config = lib.mkIf (cfg.enable) { programs.swaylock = { enable = true; package = pkgs.swaylock-effects; settings = { screenshots = true; show-failed-attempts = true; line-uses-inside = true; clock = true; timestr = "%I:%M%p"; datestr = "%a, %d %b"; indicator = true; effect-blur = "7x5"; effect-compose = "50%,35%;center;${./text.png}"; # Inside colors inside-color = "1A1B26"; inside-wrong-color = "1A1B26"; inside-ver-color = "1A1B26"; inside-clear-color = "1A1B26"; # Text colors text-color = "C0CAF5"; text-wrong-color = "C0CAF5"; text-ver-color = "C0CAF5"; text-clear-color = "C0CAF5"; text-caps-lock-color = "E0AF68"; # Ring colors ring-color = "9ECE6A"; ring-wrong-color = "F7768E"; ring-ver-color = "7AA2F7"; ring-clear-color = "E0AF68"; # Highlight colors key-hl-color = "BB9AF7"; bs-hl-color = "BB9AF7"; }; }; }; } ================================================ FILE: home/_modules/windowmanager/add-on/terminal-emulator/default.nix ================================================ { config, lib, ... }: let cfg = config.myHome.windowmanager.add-on.terminal-emulator; super = "Mod4"; inherit (lib) mkOption types mkIf mkMerge ; mkDefaultTerminal = term: { myHome.terminal-emulator.${term}.enable = true; wayland.windowManager = { hyprland.settings.bind = [ "SUPER, t, exec, ${term}" ]; sway.config.keybindings."${super}+t" = "exec --no-startup-id ${term}"; }; xsession.windowManager.i3.config.keybindings."${super}+t" = "exec --no-startup-id ${term}"; }; in { options.myHome.windowmanager.add-on.terminal-emulator = { default = mkOption { type = types.nullOr ( types.enum [ "alacritty" "contour" "kitty" "wezterm" ] ); default = null; }; }; config = mkIf (cfg.default != null) (mkMerge [ (mkIf (cfg.default == "alacritty") (mkDefaultTerminal "alacritty")) (mkIf (cfg.default == "contour") (mkDefaultTerminal "contour")) (mkIf (cfg.default == "kitty") (mkDefaultTerminal "kitty")) (mkIf (cfg.default == "wezterm") (mkDefaultTerminal "wezterm")) ]); } ================================================ FILE: home/_modules/windowmanager/add-on/theme/tokyonight/default.nix ================================================ { config, lib, ... }: let cfg = config.myHome.windowmanager.add-on.theme.tokyonight; # Tokyonight Night color scheme color = { bg = "#1a1b26"; bg_dark = "#16161e"; bg_highlight = "#292e42"; blue = "#7aa2f7"; blue0 = "#3d59a1"; blue1 = "#2ac3de"; blue2 = "#0db9d7"; blue5 = "#89ddff"; blue6 = "#b4f9f8"; blue7 = "#394b70"; comment = "#565f89"; cyan = "#7dcfff"; dark3 = "#545c7e"; dark5 = "#737aa2"; fg = "#c0caf5"; fg_dark = "#a9b1d6"; fg_gutter = "#3b4261"; green = "#9ece6a"; green1 = "#73daca"; green2 = "#41a6b5"; magenta = "#bb9af7"; magenta2 = "#ff007c"; orange = "#ff9e64"; purple = "#9d7cd8"; red = "#f7768e"; red1 = "#db4b4b"; teal = "#1abc9c"; terminal_black = "#414868"; yellow = "#e0af68"; }; in { options.myHome.windowmanager.add-on.theme.tokyonight = { enable = lib.mkOption { type = lib.types.bool; default = false; description = '' Enable Tokyonight Night theme for window manager. Only works in i3 and Sway window manager. ''; }; }; config = lib.mkIf (cfg.enable) { wayland.windowManager.sway.config.colors = { focused = { border = color.bg; background = color.bg; text = color.red; indicator = color.blue; childBorder = color.fg_dark; }; unfocused = { border = color.bg; background = color.bg; text = color.fg_dark; indicator = color.blue; childBorder = color.fg_dark; }; focusedInactive = { border = color.bg; background = color.bg; text = color.fg_dark; indicator = color.blue; childBorder = color.fg_dark; }; urgent = { border = color.red; background = color.red; text = color.fg_dark; indicator = color.blue; childBorder = color.fg_dark; }; }; xsession.windowManager.i3.config = { colors = { focused = { border = color.bg; background = color.bg; text = color.red; indicator = color.fg_dark; childBorder = color.bg; }; unfocused = { border = color.bg; background = color.bg; text = color.fg_dark; indicator = color.fg_dark; childBorder = color.bg; }; focusedInactive = { border = color.bg; background = color.bg; text = color.fg_dark; indicator = color.fg_dark; childBorder = color.bg; }; urgent = { border = color.red; background = color.red; text = color.fg; indicator = color.fg_dark; childBorder = color.bg; }; }; # TODO: use polibar instead bars = [ { trayOutput = (builtins.head (builtins.filter (m: m.primary) config.myHardware.monitors)).name; statusCommand = "py3status"; fonts = { names = [ "UbuntuMono Nerd Font" ]; size = 12.0; }; position = "top"; colors = { background = color.bg; separator = color.fg; activeWorkspace = { border = color.bg; background = color.bg; text = color.red; }; focusedWorkspace = { border = color.bg; background = color.bg; text = color.red; }; inactiveWorkspace = { border = color.bg; background = color.bg; text = color.fg; }; urgentWorkspace = { border = color.red; background = color.red; text = color.fg; }; }; } ]; }; }; } ================================================ FILE: home/_modules/windowmanager/add-on/waybar/default.nix ================================================ { config, lib, pkgs, ... }: let myHome = config.myHome; myHardware = config.myHardware; cfg = myHome.windowmanager.add-on.waybar; leftModules = ( if myHome.windowmanager.sway.enable then [ "sway/workspaces" "sway/mode" ] else if myHome.windowmanager.hyprland.enable then [ "hyprland/workspaces" "hyprland/submap" ] else [ ] ); centerModules = [ "clock" ]; rightModules = [ "network#down" "network#up" ]; modulesCfg = { "network#down" = { format = "󰁅 {bandwidthDownBits}"; tooltip-format = "{ifname} {ipaddr}"; interval = 1; }; "network#up" = { format = "󰁝 {bandwidthUpBits}"; tooltip-format = "{ifname} {ipaddr}"; interval = 1; }; clock = { format = "{:%A, %d %b %I:%M%p}"; interval = 1; timezone = "Asia/Jakarta"; tooltip = false; }; "custom/power" = { format = "󰤆 "; tooltip = true; on-click = "${pkgs.nwg-bar}/bin/nwg-bar"; }; "sway/workspaces" = { format = "{value}"; }; "sway/mode" = { format = "󰔡 {}"; max-length = 100; }; "hyprland/workspaces" = { format = "{name}"; on-click = "activate"; on-scroll-up = "${config.wayland.windowManager.hyprland.finalPackage}/bin/hyprctl dispatch workspace e+1"; on-scroll-down = "${config.wayland.windowManager.hyprland.finalPackage}/bin/hyprctl dispatch workspace e-1"; }; "hyprland/submap" = { format = "󰔡 {}"; max-length = 100; }; }; in { options.myHome.windowmanager.add-on.waybar = { enable = lib.mkEnableOption "waybar"; }; config = lib.mkIf (cfg.enable) { programs.waybar = { enable = true; systemd.enable = true; settings = builtins.map ( monitor: { output = monitor.name; layer = "top"; position = "top"; margin = "10 20 -5 20"; modules-left = leftModules; modules-center = centerModules; modules-right = rightModules ++ ( if monitor.primary then [ "tray" "custom/power" ] else [ ] ); } // modulesCfg ) myHardware.monitors; style = '' * { border: none; border-radius: 0; font-family: "UbuntuMono Nerd Font"; font-size: 16px; font-weight: normal; padding: 1px; } button { min-height: 24px; min-width: 16px; } window#waybar { background-color: transparent; color: #C0CAF5; transition-property: background-color; transition-duration: .5s; border: 1px solid #C0CAF5; border-radius: 10px; } window#waybar.hidden { opacity: 0.2; } #workspaces button { color: #C0CAF5; padding: 0 3px; border-radius: 5px; } #workspaces button.focused { color: #7AA2F7; } #workspaces button.active { color: #7AA2F7; } #workspaces button.urgent { color: #F7768E; } #mode { color: #F7768E; padding-left: 2px; } #submap { color: #F7768E; padding-left: 2px; } #clock { color: #BB9AF7; } #network.down { color: #9ECE6A; padding-right: 8px; } #network.up { color: #7AA2F7; padding-right: 8px; } #tray { color: #C0CAF5; padding-right: 8px; } #custom-power { color: #F7768E; padding-right: 8px; } ''; }; }; } ================================================ FILE: home/_modules/windowmanager/add-on/xdg/default.nix ================================================ { config, lib, ... }: let cfg = config.myHome.windowmanager.add-on.xdg; in { options.myHome.windowmanager.add-on.xdg = { enable = lib.mkEnableOption "xdg"; }; config = lib.mkIf (cfg.enable) { xdg.userDirs = { enable = true; createDirectories = true; }; }; } ================================================ FILE: home/_modules/windowmanager/hyprland/config/appearances.nix ================================================ { general = { gaps_in = 5; gaps_out = 20; "col.inactive_border" = "rgb(A9B1D6)"; "col.active_border" = "rgb(A9B1D6)"; layout = "master"; }; decoration = { rounding = 10; inactive_opacity = 0.7; dim_inactive = true; dim_strength = 0.3; }; master = { new_on_top = true; }; } ================================================ FILE: home/_modules/windowmanager/hyprland/config/default.nix ================================================ { config, pkgs, lib, ... }: let myHardware = config.myHardware; resizeMode = "Resize: (h/Left) width- (j/Down) height-, (k/Up) height+, (l/Right) width+"; in { # Submaps are impossible to be defined in settings extraConfig = '' # Resize window submap = ${resizeMode} binde = , h, resizeactive, -10 0 binde = , j, resizeactive, 0 10 binde = , k, resizeactive, 0 -10 binde = , l, resizeactive, 10 0 # Or use arrow keys binde = , left, resizeactive, -10 0 binde = , down, resizeactive, 0 10 binde = , up, resizeactive, 0 -10 binde = , right, resizeactive, 10 0 # Return to normal mode bind = , escape, submap, reset submap = reset ''; settings = { # Monitor placement # [ ",x,x,1" ... ] monitor = builtins.map ( m: "${m.name},${toString m.width}x${toString m.height},${toString m.x}x${toString m.y},1" ) myHardware.monitors; # Wallpaper # [ "swaybg -o -i " ... ] exec-once = builtins.map (m: "${pkgs.swaybg}/bin/swaybg -o ${m.name} -i ${m.wallpaper}") ( builtins.filter (m: m.wallpaper != null) myHardware.monitors ); # General settings general = { resize_on_border = true; }; input = { numlock_by_default = true; follow_mouse = 1; }; misc = { mouse_move_enables_dpms = true; key_press_enables_dpms = true; }; env = lib.mkIf (myHardware.gpuDriver == "nvidia") [ "LIBVA_DRIVER_NAME,nvidia" "XDG_SESSION_TYPE,wayland" "GBM_BACKEND,nvidia-drm" "__GLX_VENDOR_LIBRARY_NAME,nvidia" "WLR_NO_HARDWARE_CURSORS,1" ]; } // (import ./keybindings.nix { inherit config resizeMode pkgs; }) // (import ./ws-outputs.nix { inherit config; }) // (import ./window-rules.nix) // (import ./appearances.nix); } ================================================ FILE: home/_modules/windowmanager/hyprland/config/keybindings.nix ================================================ { config, resizeMode, pkgs, }: { bindm = [ # Super+mouse to drag/resize floating windows "SUPER, mouse:272, movewindow" "SUPER, mouse:273, resizewindow" ]; bind = [ # Kill focused window "SUPER, F4, killactive," # Change focus around "SUPER, h, movefocus, l" "SUPER, j, movefocus, d" "SUPER, k, movefocus, u" "SUPER, l, movefocus, r" # Or use arrow keys "SUPER, left, movefocus, l" "SUPER, down, movefocus, d" "SUPER, up, movefocus, u" "SUPER, right, movefocus, r" # Move the focused window with the same, but add Shift "SUPER SHIFT, h, movewindow, l" "SUPER SHIFT, j, movewindow, d" "SUPER SHIFT, k, movewindow, u" "SUPER SHIFT, l, movewindow, r" # Or use arrow keys "SUPER SHIFT, left, movewindow, l" "SUPER SHIFT, down, movewindow, d" "SUPER SHIFT, up, movewindow, u" "SUPER SHIFT, right, movewindow, r" # Next/previous workspace "SUPER, tab, workspace, e+1" "SUPER SHIFT, tab, workspace, e-1" # Toggle fullscreen mode "SUPER SHIFT, f, fullscreen, 1" # Toggle floating mode "SUPER SHIFT, space, togglefloating," # Move the currently focused window to the scratchpad "SUPER SHIFT, p, movetoworkspacesilent, special" # Show the next scratchpad window or hide the focused scratchpad window "SUPER, p, togglespecialworkspace," # Switch to workspace "SUPER, 1, workspace, 1" "SUPER, 2, workspace, 2" "SUPER, 3, workspace, 3" "SUPER, 4, workspace, 4" "SUPER, 5, workspace, 5" "SUPER, 6, workspace, 6" "SUPER, 7, workspace, 7" "SUPER, 8, workspace, 8" "SUPER, 9, workspace, 9" "SUPER, 0, workspace, 10" # Move focused container to workspace "SUPER SHIFT, 1, movetoworkspace, 1" "SUPER SHIFT, 2, movetoworkspace, 2" "SUPER SHIFT, 3, movetoworkspace, 3" "SUPER SHIFT, 4, movetoworkspace, 4" "SUPER SHIFT, 5, movetoworkspace, 5" "SUPER SHIFT, 6, movetoworkspace, 6" "SUPER SHIFT, 7, movetoworkspace, 7" "SUPER SHIFT, 8, movetoworkspace, 8" "SUPER SHIFT, 9, movetoworkspace, 9" "SUPER SHIFT, 0, movetoworkspace, 10" # Reload the configuration file "SUPER SHIFT, c, execr, hyprctl reload" # Most used applications "SUPER, f, exec, thunar" # use system provided thunar package "SUPER, c, exec, ${pkgs.gnome-calculator}/bin/gnome-calculator" # Rofi as dmenu replacement "SUPER, grave, exec, ${config.programs.rofi.package}/bin/rofi -show drun" "SUPER, Escape, exec, ${config.programs.rofi.package}/bin/rofi -show drun" # Modes "SUPER SHIFT, r, submap, ${resizeMode}" # Logout menu "CTRL SUPER, delete, exec, ${pkgs.nwg-bar}/bin/nwg-bar" ]; } ================================================ FILE: home/_modules/windowmanager/hyprland/config/window-rules.nix ================================================ { windowrulev2 = [ # Window floating and layout "float, class:^(pavucontrol)$" "float, class:^(thunar)$" "float, class:^(obs)$" "float, class:^(gnome-calculator|org\.gnome\.Calculator)$" "float, class:^(pamac-manager)$" "float, class:^(eog)$" "float, class:^(blueman-manager)$" "float, class:^(nm-connection-editor)$" "float, class:^(rhythmbox)$" "size 1000 640, class:^(rhythmbox)$" "float, title:^(File Transfer*)$" "float, title:^(Lxappearance)$" "pin, title:^(Lxappearance)$" "float, title:^(VirtualBox)$" "float, title:^(VirtualBox)$" "idleinhibit focus, fullscreen:1" "idleinhibit focus, class:^(org.libretro.RetroArch)$" "idleinhibit focus, class:^(com.github.iwalton3.jellyfin-media-player)$" # Window placement "workspace silent special, title:^(updater)$, class:^(kitty)$" "workspace 5, class:^(smplayer)$" "workspace 5, class:^(totem)$" "workspace 5, class:^(rhythmbox)$" "workspace 6, class:^(org.libretro.RetroArch)$" "workspace 7, class:^(Gimp)$" "workspace 9, class:^(obs)$" "workspace 10, class:^(VirtualBox)$" "noborder, title:^(Syncthing Tray( \(.*\))?)$" "size 550 400, title:^(Syncthing Tray( \(.*\))?)$" "move 1365 30, title:^(Syncthing Tray( \(.*\))?)$" "move 1465 30, title:^(Nextcloud)$, class:^(Nextcloud)$" ]; } ================================================ FILE: home/_modules/windowmanager/hyprland/config/ws-outputs.nix ================================================ { config }: let strBool = b: if b then "true" else "false"; mkWorkspaces = monitors: builtins.concatMap ( mon: builtins.map ( ws: "${toString ws}, monitor:${mon.name}, default:${strBool (ws == builtins.head mon.workspaces)}" ) mon.workspaces ) monitors; in { # [ ", monitor:, default:" "..." ] workspace = mkWorkspaces config.myHardware.monitors; } ================================================ FILE: home/_modules/windowmanager/hyprland/default.nix ================================================ { config, osConfig, lib, pkgs, ... }: let myHardware = config.myHardware; cfg = config.myHome.windowmanager.hyprland; systemEnabled = lib.myLib.systemEnabled "mySystem.windowmanager.hyprland.enable" osConfig; in { options.myHome.windowmanager.hyprland = { enable = lib.mkEnableOption "Hyprland window manager"; }; config = lib.mkIf (cfg.enable) { myHome = { isWayland = true; windowmanager.add-on = { blueman-applet.enable = true; dunst.enable = true; gtk-theme.enable = true; nm-applet.enable = true; nwg-bar.enable = true; pasystray.enable = true; rofi.enable = true; swayidle.enable = true; waybar.enable = true; screenshotter.enable = true; terminal-emulator.default = "alacritty"; xdg.enable = true; }; }; warnings = lib.mkIf (!systemEnabled) [ '' You have enabled Hyprland home-manager module but not the NixOS system module. Some things might not work properly. '' ]; assertions = [ { assertion = builtins.length myHardware.monitors > 0; message = '' At least one monitor in the `config.myHardware.monitors` is needed to use Hyprland home-manager module. ''; } ]; wayland.windowManager.hyprland = { enable = true; } // (import ./config { inherit config lib pkgs; }); systemd.user.startServices = "sd-switch"; }; } ================================================ FILE: home/_modules/windowmanager/i3/config/commands.nix ================================================ [ { criteria = { window_role = "pop-up"; }; command = "floating enable"; } { criteria = { window_role = "prefwindow"; }; command = "floating enable"; } { criteria = { class = "notify"; }; command = "floating enable border pixel 1"; } { criteria = { title = "File Transfer*"; }; command = "floating enable"; } { criteria = { class = "Galculator"; }; command = "floating enable"; } { criteria = { class = "Lxappearance"; }; command = "floating enable sticky enable border normal"; } { criteria = { class = "Oblogout"; }; command = "fullscreen enable"; } { criteria = { class = "Pavucontrol"; }; command = "floating enable"; } { criteria = { class = "VirtualBox"; }; command = "floating enable"; } { criteria = { class = "Skype"; }; command = "floating enable"; } { criteria = { class = "(?i)nvidia-settings"; }; command = "floating enable"; } { criteria = { class = "smplayer"; }; command = "floating enable"; } { criteria = { class = "Eog"; }; command = "floating enable"; } { criteria = { class = "Rhythmbox"; }; command = "floating enable resize set 1000 640"; } { criteria = { class = "obs"; }; command = "floating enable"; } ] ================================================ FILE: home/_modules/windowmanager/i3/config/default.nix ================================================ { config, pkgs, lib, }: let myHardware = config.myHardware; modes = { resize = "Resize: (h/Left) width-, (j/Down) height-, (k/Up) height+, (l/Right) width+"; gaps = "Gaps (o) outer, (i) inner"; gapsOuter = "Outer Gaps (k/Up) grow locally, (K/Shift+Up) grow globally"; gapsInner = "Inner Gaps (k/Up) grow locally, (K/Shift+Up) grow globally"; system = "System (l) lock, (e) logout, (r) reboot, (s) shutdown, (f) UEFI"; }; in { floating.modifier = "Mod4"; focus = { wrapping = "yes"; followMouse = true; newWindow = "urgent"; }; gaps = { inner = 10; outer = 5; smartGaps = true; }; # [ { output = ""; workspace = ""; } {...} ] workspaceOutputAssign = builtins.concatMap ( mon: builtins.map (ws: { workspace = (toString ws); output = mon.xname; }) mon.workspaces ) myHardware.monitors; keybindings = import ./keybindings.nix { inherit config pkgs modes; }; modes = import ./modes.nix { inherit modes pkgs; }; window.commands = import ./commands.nix; startup = import ./startups.nix { inherit config pkgs lib; }; } ================================================ FILE: home/_modules/windowmanager/i3/config/keybindings.nix ================================================ { config, pkgs, modes, }: let super = "Mod4"; in { # kill focused window "${super}+F4" = "kill"; # change focus "${super}+h" = "focus left"; "${super}+j" = "focus down"; "${super}+k" = "focus up"; "${super}+l" = "focus right"; # alternatively, you can use the cursor keys "${super}+Left" = "focus left"; "${super}+Down" = "focus down"; "${super}+Up" = "focus up"; "${super}+Right" = "focus right"; # move focused window "${super}+Shift+h" = "move left"; "${super}+Shift+j" = "move down"; "${super}+Shift+k" = "move up"; "${super}+Shift+l" = "move right"; # alternatively, you can use the cursor keys "${super}+Shift+Left" = "move left"; "${super}+Shift+Down" = "move down"; "${super}+Shift+Up" = "move up"; "${super}+Shift+Right" = "move right"; # split in orientation "${super}+Control+h" = "split h; exec notify-send 'tile horizontally'"; "${super}+Control+v" = "split v; exec notify-send 'tile vertically'"; "${super}+Control+q" = "split toggle"; # next/previous workspace "${super}+Tab" = "workspace next"; "${super}+Shift+Tab" = "workspace prev"; # toggle window border "${super}+Control+t" = "border toggle"; # enter fullscreen mode for the focused container "${super}+Control+f" = "fullscreen toggle"; # toggle gaps on and off, please uncomment if you don't have i3-gaps installed "${super}+Control+g" = '' exec --no-startup-id "if [ `i3-msg -t get_tree | grep -Po \ '.*\\"gaps\\":{\\"inner\\":\K(-|)[0-9]+(?=.*\\"focused\\":true)'` -eq 0 ]; then \ i3-msg gaps inner current set 0; i3-msg gaps outer current set 0; \ else \ i3-msg gaps inner current set 10; i3-msg gaps outer current set 5; \ fi ''; # change container layout (stacked, tabbed, toggle split) "${super}+Control+s" = "layout stacking"; "${super}+Control+w" = "layout tabbed"; "${super}+Control+e" = "layout toggle split"; # toggle floating mode "${super}+Shift+space" = "floating toggle"; # change focus between tiling / floating windows "${super}+space" = "focus mode_toggle"; # switch to workspace "${super}+1" = "workspace 1"; "${super}+2" = "workspace 2"; "${super}+3" = "workspace 3"; "${super}+4" = "workspace 4"; "${super}+5" = "workspace 5"; "${super}+6" = "workspace 6"; "${super}+7" = "workspace 7"; "${super}+8" = "workspace 8"; "${super}+9" = "workspace 9"; "${super}+0" = "workspace 10"; # move focused container to workspace "${super}+Shift+1" = "move container to workspace 1"; "${super}+Shift+2" = "move container to workspace 2"; "${super}+Shift+3" = "move container to workspace 3"; "${super}+Shift+4" = "move container to workspace 4"; "${super}+Shift+5" = "move container to workspace 5"; "${super}+Shift+6" = "move container to workspace 6"; "${super}+Shift+7" = "move container to workspace 7"; "${super}+Shift+8" = "move container to workspace 8"; "${super}+Shift+9" = "move container to workspace 9"; "${super}+Shift+0" = "move container to workspace 10"; # reload the configuration file "${super}+Shift+c" = "reload"; # exit i3 (logs you out of your X session) "${super}+Shift+e" = "exec \"i3-nagbar -t warning -m 'You pressed the exit shortcut. Do you really want to exit i3? This will end your X session.' -b 'Yes, exit i3' 'i3-msg exit'\""; # most used application shortcuts "${super}+f" = "exec --no-startup-id thunar"; # use system provided thunar package "${super}+c" = "exec ${pkgs.gnome-calculator}/bin/gnome-calculator"; # rofi as dmenu replacement "${super}+grave" = "exec ${config.programs.rofi.package}/bin/rofi -show drun"; "${super}+Escape" = "exec ${config.programs.rofi.package}/bin/rofi -show drun"; # modes "${super}+Shift+r" = "mode \"${modes.resize}\""; "${super}+Shift+g" = "mode \"${modes.gaps}\""; "${super}+Control+Delete" = "mode \"${modes.system}\""; } ================================================ FILE: home/_modules/windowmanager/i3/config/modes.nix ================================================ { modes, pkgs }: { # resize window (you can also use the mouse for that) ${modes.resize} = { h = "resize shrink width 10 px or 10 ppt"; j = "resize shrink height 10 px or 10 ppt"; k = "resize grow height 10 px or 10 ppt"; l = "resize grow width 10 px or 10 ppt"; # same bindings, but for the arrow keys Left = "resize shrink width 10 px or 10 ppt"; Down = "resize shrink height 10 px or 10 ppt"; Up = "resize grow height 10 px or 10 ppt"; Right = "resize grow width 10 px or 10 ppt"; # back to normal: Enter or Escape Return = "mode default"; Escape = "mode default"; }; # resize gaps ${modes.gaps} = { o = "mode \"$mode_gaps_outer\""; i = "mode \"$mode_gaps_inner\""; Return = "mode \"${modes.gaps}\""; Escape = "mode default"; }; ${modes.gapsOuter} = { k = "gaps outer current plus 5"; j = "gaps outer current minus 5"; # same bindings, but for the arrow keys Up = "gaps outer current plus 5"; Down = "gaps outer current minus 5"; "Shift+k" = "gaps outer all plus 5"; "Shift+j" = "gaps outer all minus 5"; # same keybindings, but for the arrow keys "Shift+Up" = "gaps outer all plus 5"; "Shift+Down" = "gaps outer all minus 5"; Return = "mode \"${modes.gaps}\""; Escape = "mode default"; }; ${modes.gapsInner} = { k = "gaps inner current plus 5"; j = "gaps inner current minus 5"; # same bindings, but for the arrow keys Up = "gaps inner current plus 5"; Down = "gaps inner current minus 5"; "Shift+k" = "gaps inner all plus 5"; "Shift+j" = "gaps inner all minus 5"; # same keybindings, but for the arrow keys "Shift+Up" = "gaps inner all plus 5"; "Shift+Down" = "gaps inner all minus 5"; Return = "mode \"${modes.gaps}\""; Escape = "mode default"; }; # Press Ctrl+Alt+Delete will show log out menu ${modes.system} = { l = "exec --no-startup-id ${pkgs.i3lock}/bin/i3lock && sleep 1, mode default"; e = "exec --no-startup-id i3-msg exit, mode default"; r = "exec --no-startup-id systemctl reboot, mode default"; s = "exec --no-startup-id systemctl poweroff -i, mode default"; f = "exec --no-startup-id systemctl reboot --firmware-setup, mode default"; # back to normal: Enter or Escape Return = "mode default"; Escape = "mode default"; }; } ================================================ FILE: home/_modules/windowmanager/i3/config/startups.nix ================================================ { config, pkgs, lib, }: let wallpapers = builtins.map (m: m.wallpaper) ( builtins.filter (m: m.wallpaper != null) config.myHardware.monitors ); in [ { # "xrandr --pos x --mode x ..." command = "${pkgs.xorg.xrandr}/bin/xrandr" + (lib.concatMapStrings ( mon: " --output ${mon.xname} --pos ${toString mon.x}x${toString mon.y} --mode ${toString mon.width}x${toString mon.height}" + (lib.optionalString (mon.primary) " --primary") ) config.myHardware.monitors); notification = false; always = true; } { # "feh --bg-fill ..." command = "${pkgs.feh}/bin/feh" + (lib.concatMapStrings (wp: " --bg-fill ${wp}")) wallpapers; notification = false; always = true; } { command = "${pkgs.xfce.xfce4-power-manager}/bin/xfce4-power-manager"; notification = false; } { command = "${pkgs.numlockx}/bin/numlockx"; notification = false; } ] ================================================ FILE: home/_modules/windowmanager/i3/default.nix ================================================ { config, lib, osConfig, pkgs, ... }: let myHardware = config.myHardware; cfg = config.myHome.windowmanager.i3; systemEnabled = lib.myLib.systemEnabled "mySystem.windowmanager.i3.enable" osConfig; in { options.myHome.windowmanager.i3 = { enable = lib.mkEnableOption "i3"; }; config = lib.mkIf (cfg.enable) { myHome = { isWayland = false; windowmanager.add-on = { blueman-applet.enable = true; dunst.enable = true; gtk-theme.enable = true; nm-applet.enable = true; rofi.enable = true; theme.tokyonight.enable = true; screenshotter.enable = true; pasystray.enable = true; picom.enable = true; py3status.enable = true; terminal-emulator.default = "wezterm"; xdg.enable = true; }; }; warnings = lib.mkIf (!systemEnabled) [ '' You have enabled i3 home-manager module but not the NixOS system module. Some things might not work properly. '' ]; assertions = [ { assertion = builtins.length myHardware.monitors > 0; message = '' At least one monitor in the `config.myHardware.monitors` is needed to use i3 home-manager module. ''; } ]; xsession.windowManager.i3 = { enable = true; package = pkgs.i3-gaps; config = import ./config { inherit config pkgs lib; }; }; }; } ================================================ FILE: home/_modules/windowmanager/sway/config/commands.nix ================================================ [ # Window title formatting { criteria = { class = ".*"; }; command = "title_format \" %title\""; } # window floating and layout { criteria = { window_role = "pop-up"; }; command = "floating enable"; } { criteria = { window_role = "prefwindow"; }; command = "floating enable"; } { criteria = { app_id = "pavucontrol"; }; command = "floating enable"; } { criteria = { app_id = "thunar"; }; command = "floating enable"; } { criteria = { app_id = "obs"; }; command = "floating enable"; } { criteria = { app_id = "(gnome-calculator)|(org\.gnome\.Calculator)"; }; command = "floating enable"; } { criteria = { app_id = "eog"; }; command = "floating enable"; } { criteria = { app_id = "blueman-manager"; }; command = "floating enable"; } { criteria = { app_id = "nm-connection-editor"; }; command = "floating enable"; } { criteria = { app_id = "rhythmbox"; }; command = "floating enable, resize set 1000 640"; } { criteria = { title = "File Transfer*"; }; command = "floating enable"; } { criteria = { class = "Lxappearance"; }; command = "floating enable, sticky enable"; } { criteria = { class = "VirtualBox"; }; command = "floating enable"; } # window placement { criteria = { app_id = "retroarch"; }; command = "move container to workspace 6, focus"; } { criteria = { class = "Gimp"; }; command = "move container to workspace 7, focus"; } { criteria = { app_id = "obs"; }; command = "move container to workspace 9, focus"; } { criteria = { class = "VirtualBox"; }; command = "move container to workspace 10, focus"; } { criteria = { title = "^Syncthing Tray( \(.*\))?$"; }; command = "floating enable, border none, resize set 550 400, move position 1350 0"; } ] ================================================ FILE: home/_modules/windowmanager/sway/config/default.nix ================================================ { config, pkgs, lib, }: let myHardware = config.myHardware; modes = { resize = "Resize: (h/Left) width-, (j/Down) height-, (k/Up) height+, (l/Right) width+"; gaps = "Gaps (o) outer, (i) inner"; gapsOuter = "Outer Gaps (k/Up) grow locally, (K/Shift+Up) grow globally"; gapsInner = "Inner Gaps (k/Up) grow locally, (K/Shift+Up) grow globally"; }; in { floating = { modifier = "Mod4"; titlebar = false; border = 0; }; window.border = 1; fonts = { names = [ "pango:UbuntuMono Nerd Font" ]; size = 12.0; }; focus = { wrapping = "yes"; followMouse = true; newWindow = "smart"; }; gaps.inner = 10; bars = [ ]; # { = { bg = " fill"; pos = " "; }; ... = {...}; } output = builtins.listToAttrs ( builtins.map (m: { name = m.name; value = { bg = "${m.wallpaper} fill"; pos = "${toString m.x} ${toString m.y}"; }; }) (builtins.filter (m: m.wallpaper != null) myHardware.monitors) ); # [ { output = ""; workspace = ""; } {...} ] workspaceOutputAssign = builtins.concatMap ( mon: builtins.map (ws: { workspace = (toString ws); output = mon.name; }) mon.workspaces ) myHardware.monitors; keybindings = import ./keybindings.nix { inherit config modes pkgs; }; modes = import ./modes.nix { inherit modes pkgs; }; window.commands = import ./commands.nix; startup = import ./startups.nix { inherit pkgs; }; } ================================================ FILE: home/_modules/windowmanager/sway/config/keybindings.nix ================================================ { config, modes, pkgs, }: let super = "Mod4"; in { # Kill focused window "${super}+F4" = "kill"; # Change focus around "${super}+h" = "focus left"; "${super}+j" = "focus down"; "${super}+k" = "focus up"; "${super}+l" = "focus right"; # Or use arrow keys "${super}+Left" = "focus left"; "${super}+Down" = "focus down"; "${super}+Up" = "focus up"; "${super}+Right" = "focus right"; # Move the focused window with the same, but add Shift "${super}+Shift+h" = "move left"; "${super}+Shift+j" = "move down"; "${super}+Shift+k" = "move up"; "${super}+Shift+l" = "move right"; # Or use arrow keys "${super}+Shift+Left" = "move left"; "${super}+Shift+Down" = "move down"; "${super}+Shift+Up" = "move up"; "${super}+Shift+Right" = "move right"; # Split current container "${super}+Control+h" = "splith; exec notify-send 'tile horizontally'"; "${super}+Control+v" = "splitv; exec notify-send 'tile vertically'"; "${super}+Control+q" = "split toggle"; # Next/previous workspace "${super}+Tab" = "workspace next"; "${super}+Shift+Tab" = "workspace prev"; # Toggle window border "${super}+Control+t" = "border toggle"; # toggle gaps on and off "${super}+Control+g" = '' exec "if [ `swaymsg -t get_tree | grep -Po \ '.*\\"gaps\\":{\\"inner\\":\K(-|)[0-9]+(?=.*\\"focused\\":true)'` -eq 0 ]; then \ swaymsg gaps inner current set 0; swaymsg gaps outer current set 0; \ else \ swaymsg gaps inner current set 10; swaymsg gaps outer current set 5; \ fi ''; # Toggle fullscreen mode "${super}+Shift+f" = "fullscreen toggle"; # Change container layout "${super}+Control+s" = "layout stacking"; "${super}+Control+w" = "layout tabbed"; "${super}+Control+e" = "layout toggle split"; # Toggle floating mode "${super}+Shift+space" = "floating toggle"; # Swap focus between tiling / floating windows "${super}+space" = "focus mode_toggle"; # Focus the parent container #"${super}+a" = "focus parent"; # Focus the child container #"${super}+d" = "focus child"; # Move the currently focused window to the scratchpad "${super}+Shift+p" = "move scratchpad"; # Show the next scratchpad window or hide the focused scratchpad window. "${super}+p" = "scratchpad show"; # Switch to workspace "${super}+1" = "workspace 1"; "${super}+2" = "workspace 2"; "${super}+3" = "workspace 3"; "${super}+4" = "workspace 4"; "${super}+5" = "workspace 5"; "${super}+6" = "workspace 6"; "${super}+7" = "workspace 7"; "${super}+8" = "workspace 8"; "${super}+9" = "workspace 9"; "${super}+0" = "workspace 10"; # Move focused container to workspace "${super}+Shift+1" = "move container to workspace 1"; "${super}+Shift+2" = "move container to workspace 2"; "${super}+Shift+3" = "move container to workspace 3"; "${super}+Shift+4" = "move container to workspace 4"; "${super}+Shift+5" = "move container to workspace 5"; "${super}+Shift+6" = "move container to workspace 6"; "${super}+Shift+7" = "move container to workspace 7"; "${super}+Shift+8" = "move container to workspace 8"; "${super}+Shift+9" = "move container to workspace 9"; "${super}+Shift+0" = "move container to workspace 10"; # Reload the configuration file "${super}+Shift+c" = "reload"; # Exit sway (logs you out of your Wayland session) "${super}+Shift+e" = "exec swaynag -t warning -m 'You pressed the exit shortcut. Do you really want to exit sway? This will end your Wayland session.' -b 'Yes, exit sway' 'swaymsg exit'"; # Most used applications "${super}+f" = "exec thunar"; # use system provided thunar package "${super}+c" = "exec ${pkgs.gnome-calculator}/bin/gnome-calculator"; # Rofi as dmenu replacement "${super}+grave" = "exec ${config.programs.rofi.package}/bin/rofi -show drun"; "${super}+Escape" = "exec ${config.programs.rofi.package}/bin/rofi -show drun"; # Modes "${super}+Shift+r" = "mode \"${modes.resize}\""; "${super}+Shift+g" = "mode \"${modes.gaps}\""; # Logout menu "${super}+Control+Delete" = "exec ${pkgs.nwg-bar}/bin/nwg-bar"; } ================================================ FILE: home/_modules/windowmanager/sway/config/modes.nix ================================================ { modes, pkgs }: { # Resize window ${modes.resize} = { h = "resize shrink width"; j = "resize grow height"; k = "resize shrink height"; l = "resize grow width"; # Or use arrow keys Left = "resize shrink width"; Down = "resize grow height"; Up = "resize shrink height"; Right = "resize grow width"; # Return to normal mode Return = "mode default"; Escape = "mode default"; }; # Resize gaps ${modes.gaps} = { o = "mode \"$gaps_outer\""; i = "mode \"$gaps_inner\""; # Return to normal mode Return = "mode default"; Escape = "mode default"; }; ${modes.gapsOuter} = { k = "gaps outer current plus 5"; j = "gaps outer current minus 5"; "Shift+k" = "gaps outer all plus 5"; "Shift+j" = "gaps outer all minus 5"; # Or use arrow keys Up = "gaps outer current plus 5"; Down = "gaps outer current minus 5"; "Shift+Up" = "gaps outer all plus 5"; "Shift+Down" = "gaps outer all minus 5"; # Return to gaps/normal mode Return = "mode \"${modes.gaps}\""; Escape = "mode default"; }; ${modes.gapsInner} = { k = "gaps inner current plus 5"; j = "gaps inner current minus 5"; "Shift+k" = "gaps inner all plus 5"; "Shift+j" = "gaps inner all minus 5"; # Or use arrow keys Up = "gaps inner current plus 5"; Down = "gaps inner current minus 5"; "Shift+Up" = "gaps inner all plus 5"; "Shift+Down" = "gaps inner all minus 5"; # Return to gaps/normal mode Return = "mode \"${modes.gaps}\""; Escape = "mode default"; }; } ================================================ FILE: home/_modules/windowmanager/sway/config/startups.nix ================================================ { pkgs }: [ { command = "${pkgs.sway-contrib.inactive-windows-transparency}/bin/inactive-windows-transparency.py -o 0.9"; always = true; } ] ================================================ FILE: home/_modules/windowmanager/sway/default.nix ================================================ { config, osConfig, lib, pkgs, ... }: let myHardware = config.myHardware; cfg = config.myHome.windowmanager.sway; systemEnabled = lib.myLib.systemEnabled "mySystem.windowmanager.sway.enable" osConfig; in { options.myHome.windowmanager.sway = { enable = lib.mkEnableOption "sway"; }; config = lib.mkIf (cfg.enable) { myHome = { isWayland = true; windowmanager.add-on = { blueman-applet.enable = true; dunst.enable = true; gtk-theme.enable = true; nm-applet.enable = true; nwg-bar.enable = true; pasystray.enable = true; rofi.enable = true; theme.tokyonight.enable = true; screenshotter.enable = true; swayidle.enable = true; terminal-emulator.default = "alacritty"; waybar.enable = true; xdg.enable = true; }; }; warnings = lib.mkIf (!systemEnabled) [ '' You have enabled Sway home-manager module but not the NixOS system module. Some things might not work properly. '' ]; assertions = [ { assertion = builtins.length myHardware.monitors > 0; message = '' At least one monitor in the `config.myHardware.monitors` is needed to use Sway home-manager module. ''; } ]; wayland.windowManager.sway = { enable = true; package = null; # use the system provided package config = import ./config { inherit config pkgs lib; }; }; home.sessionVariables = { XDG_CURRENT_DESKTOP = "sway"; GTK_USE_PORTAL = "1"; }; }; } ================================================ FILE: home/budiman/config/gitcommit-message ================================================ Signed-off-by: budimanjojo # [optional scope]:  (not more than 72 chars) # # [optional body]  (not more than 72 chars) # # [optional footer(s)]  (not more than 72 chars) #-------------------------------EXAMPLE---------------------------------- # feat!(frontend): new button added in dashboard # # A new "Follow Us" button is added in your Account dashboard. # If you don't see it, please delete your browser cache and reload the # webpage. # # BREAKING CHANGE: this button replaces the custom button you have, # you need to reconfigure your custom button. ================================================ FILE: home/budiman/config/neovim/appearance.nix ================================================ { pkgs, ... }: { config = { ## Highlght on yank autoGroups.yankhighlight.clear = true; autoCmd = [ { event = [ "TextYankPost" ]; pattern = [ "*" ]; group = "yankhighlight"; callback = { __raw = "function() vim.hl.on_yank() end"; }; } ]; opts = { background = "dark"; termguicolors = true; signcolumn = "yes"; cursorcolumn = true; }; colorschemes.catppuccin = { enable = true; package = pkgs.unstable.vimPlugins.catppuccin-nvim; settings = { flavor = "mocha"; float.transparent = true; }; }; }; } ================================================ FILE: home/budiman/config/neovim/autocmds.nix ================================================ { config = { autoGroups.disabledeindenthastag.clear = true; autoGroups.disableautocomment.clear = true; autoGroups.ftdetection.clear = true; autoGroups.ftconfiguration.clear = true; autoGroups.closewithq.clear = true; autoGroups.autoimportformatgo.clear = true; autoGroups.autoformatnix.clear = true; autoCmd = [ ## Disable removing indentation on files when first letter is # except some filetypes { event = [ "FileType" ]; pattern = [ "*\\(^c$\\|^cpp$\\)\\@ { event = [ "FileType" ]; pattern = [ "PlenaryTestPopup" "help" "lspinfo" "man" "notify" "qf" "query" "spectre_panel" "startuptime" "tsplayground" "neotest-output" "checkhealth" "neotest-summary" "neotest-output-panel" ]; group = "closewithq"; callback = { __raw = "function(event) vim.bo[event.buf].buflisted = false vim.keymap.set('n', 'q', 'close', { buffer = event.buf, silent = true }) end"; }; } ## Auto imports and format on save for golang { event = [ "BufWritePre" ]; pattern = "*.go"; group = "autoimportformatgo"; callback = { __raw = "function() local params = vim.lsp.util.make_range_params() params.context = { only = { 'source.organizeImports' } } local result = vim.lsp.buf_request_sync(0, 'textDocument/codeAction', params) for cid, res in pairs(result or {}) do for _, r in pairs(res.result or {}) do if r.edit then local enc = (vim.lsp.get_client_by_id(cid) or {}).offset_encoding or 'utf-16' vim. lsp.util.apply_workspace_edit(r.edit, enc) end end end vim.lsp.buf.format({ async = false }) end"; }; } ## Auto format on save for nix files { event = [ "BufWritePre" ]; pattern = "*.nix"; group = "autoformatnix"; callback = { __raw = "function() vim.lsp.buf.format({ async = false }) end"; }; } ]; extraConfigLua = '' -- Filetype detection function local detectFt = function() local fpath = vim.fn.expand '%:p' if string.match(fpath, '.*/playbooks/.*%.yaml') or string.match(fpath, '.*/playbooks/.*%.yaml') then vim.o.filetype = 'yaml.ansible' elseif string.match(fpath, '.*/inventory/hosts%.yaml') or string.match(fpath, '.*/inventory/hosts%.yaml') then vim.o.filetype = 'yaml.ansible' elseif string.match(fpath, '.*/%.chezmoiscripts/.*%.sh.tmpl') then vim.o.filetype = 'sh.chezmoitmpl' elseif string.match(fpath, '.*/%.chezmoiscripts/.*%.fish.tmpl') then vim.o.filetype = 'fish.chezmoitmpl' end end ''; }; } ================================================ FILE: home/budiman/config/neovim/default.nix ================================================ { imports = [ ./lsp.nix ./plugins ./general.nix ./appearance.nix ./autocmds.nix ./diagnostic.nix ./keymaps.nix ]; config = { extraFiles = { "lua/utils.lua".source = ./lua/utils.lua; }; }; } ================================================ FILE: home/budiman/config/neovim/diagnostic.nix ================================================ { lib, ... }: { config = { diagnostic.settings = { virtual_text = false; update_in_insert = true; float = { source = "if_many"; focusable = false; zindex = 1; }; signs = let signs = { Error = "󰅚 "; Warn = "󰗖 "; Hint = "󰌶 "; Info = " "; }; mkDiagnosticWithValue = valueFn: lib.nixvim.toRawKeys ( lib.mapAttrs' ( n: v: lib.nameValuePair "vim.diagnostic.severity.${lib.toUpper n}" (valueFn n v) ) signs ); in { # { "__rawKey__vim.diagnostic.severity.ERROR" = "󰅚 "; ... } text = mkDiagnosticWithValue (_: v: v); # { "__rawKey__vim.diagnostic.severity.ERROR" = "DiagnosticSignError"; ... } texthl = mkDiagnosticWithValue (n: _: "DiagnosticSign${n}"); numhl = mkDiagnosticWithValue (n: _: "DiagnosticSign${n}"); }; }; # set updatetime to 150ms opts.updatetime = 150; autoCmd = [ { desc = "Show diagnostics on hover"; event = "CursorHold"; pattern = "*"; callback = { __raw = "function() vim.diagnostic.open_float() end"; }; } ]; }; } ================================================ FILE: home/budiman/config/neovim/general.nix ================================================ { config = { extraConfigLua = '' -- General options vim.cmd("filetype plugin indent on") vim.cmd("syntax enable") -- Temporary undodir local undodir = "/tmp/.vim-undodir-" .. vim.env.USER if vim.fn.isdirectory(undodir) ~= 1 then vim.fn.mkdir(undodir, "p", "0700") end vim.o.undodir = undodir vim.o.undofile = true ''; opts = { mouse = "a"; # # Enable mouse support in all modes number = true; # # Show line numbers relativenumber = true; # # Relative line numbers modelines = 0; # # Turn off modelines cursorline = true; # # Highlight current line list = true; # # Display whitespace chars listchars = "tab:...,trail:_,extends:>,precedes:<,nbsp:~"; # # Format of list wrap = true; # # Wrap lines on small screen whichwrap = "b,s,<,>,h,l,[,]"; # # Able to move line using these keys backspace = "indent,eol,start"; # # Fixes common backspace problem scrolloff = 5; # # Display 5 lines above/below the cursor when scrolling matchpairs = "(:),{:},[:],<:>"; # # Highlight matching pairs, use % to jump between hlsearch = true; # # Incremental search highlight incsearch = true; # # Incremental search highlight ignorecase = true; # # Ignore case while searching smartcase = true; # # Override ignorecase if using uppercase words history = 1000; # # History lines to be remembered wildmenu = true; # # Better commandline completion cmdheight = 0; # # Height of commandline bar hidden = true; # # Hide current buffer when opening new file to new buffer backup = false; # # Disable creating backup file writebackup = false; # # Disable creating backup file swapfile = false; # # Disable creating swap file showmode = false; # # Don't show current mode showmatch = true; # # Show matching brackets when cursor is over them splitbelow = true; # # Open new split below splitright = true; # # Open new vertical split on the right expandtab = true; # # Tabs are spaces smarttab = true; # # Smarter tabs shiftwidth = 2; # # 1 tab is 2 spaces tabstop = 2; # # 1 tab is 2 spaces autoindent = true; # # Copy indentation from previous line when starting new line cindent = true; # # Copy indentation from file being edited spell = true; # # Enable spelling spelllang = "en_us"; # # Spelling language }; clipboard = { register = "unnamedplus"; # # Use unnamedplus register }; }; } ================================================ FILE: home/budiman/config/neovim/keymaps.nix ================================================ { config = { extraConfigLuaPre = '' -- Space is vim.keymap.set("", "", "", { silent = true }) vim.g.mapleader = " " vim.g.maplocalleader = " " ''; keymaps = [ ## Fast save, save quit, force exit { mode = "n"; key = "w"; action = ":w!"; options = { desc = "Write!"; silent = true; }; } { mode = "n"; key = "x"; action = ":x"; options = { desc = "Write and exit"; silent = true; }; } { mode = "n"; key = "qq"; action = ":q"; options = { desc = "Quit"; silent = true; }; } { mode = "n"; key = "qa"; action = ":qa!"; options = { desc = "Quit all!"; silent = true; }; } { mode = "n"; key = "wq"; action = ":wq!"; options = { desc = "Write and quit!"; silent = true; }; } ## Better cursor movement on wrapped line ## NOTE: we use "x" instead of "v" because luasnip uses select ## mode for snippet node and I don't want it to do weird stuffs { mode = [ "n" "x" ]; key = "k"; action = "gk"; } { mode = [ "n" "x" ]; key = "j"; action = "gj"; } { mode = [ "n" "v" ]; key = ""; action = "gk"; } { mode = [ "n" "v" ]; key = ""; action = "gj"; } { mode = "i"; key = ""; action = "gk"; } { mode = "i"; key = ""; action = "gj"; } ## Y to yank from cursor to end of line { mode = "n"; key = "Y"; action = "y$"; options.desc = "Yank to end of line"; } ## Move selected lines up and down with updated indentation { mode = "v"; key = "J"; action = ":m '>+1gv=gv"; } { mode = "v"; key = "K"; action = ":m '<-2gv=gv"; } ## Better search movement { mode = "n"; key = "n"; action = "nzzzv"; options.desc = "Go to the next search result and center"; } { mode = "n"; key = "N"; action = "Nzzzv"; options.desc = "Go to the previous search result and center"; } ## Move between splits { mode = "n"; key = ""; action = ""; options.desc = "Move focus to split above"; } { mode = "n"; key = ""; action = ""; options.desc = "Move focus to split below"; } { mode = "n"; key = ""; action = ""; options.desc = "Move focus to left split"; } { mode = "n"; key = ""; action = ""; options.desc = "Move focus to right split"; } ## Open new split { mode = "n"; key = "s"; action = ":new"; options = { desc = "Open new horizontal split"; silent = true; }; } { mode = "n"; key = "v"; action = ":vnew"; options = { desc = "Open new vertical split"; silent = true; }; } ## Tab navigation { mode = "n"; key = "tn"; action = ":tabnew"; options = { desc = "Create new tab"; silent = true; }; } { mode = "n"; key = "tq"; action = ":tabclose"; options = { desc = "Close current tab"; silent = true; }; } { mode = "n"; key = "th"; action = ":tabprev"; options = { desc = "Go to the previous tab"; silent = true; }; } { mode = "n"; key = "tl"; action = ":tabnext"; options = { desc = "Go to the next tab"; silent = true; }; } { mode = "n"; key = "te"; action = ":tabedit =expand('%:p:h')/"; options.desc = "Open new tab with current buffer's path"; } ## Indent or de-indent { mode = "n"; key = ""; action.__raw = "function() return require('utils').always_working_indent_line() end"; options.desc = "Add indentation"; } { mode = "n"; key = ""; action = "<<"; options.desc = "De-indentation"; } { mode = "v"; key = ""; action = ">gv"; options.desc = "Add indentation"; } { mode = "v"; key = ""; action = " ++once set eventignore=""]]) end -- LuaSnip popup when there are choices -- https://github.com/L3MON4D3/LuaSnip/wiki/Misc#choicenode-popup local current_nsid = vim.api.nvim_create_namespace('LuaSnipChoiceListSelections') local current_win = nil local function window_for_choiceNode(choiceNode) local buf = vim.api.nvim_create_buf(false, true) local buf_text = {} local row_selection = 0 local row_offset = 0 local text for _, node in ipairs(choiceNode.choices) do text = node:get_docstring() -- find one that is currently showing if node == choiceNode.active_choice then -- current line is starter from buffer list which is length usually row_selection = #buf_text -- finding how many lines total within a choice selection row_offset = #text end vim.list_extend(buf_text, text) end vim.api.nvim_buf_set_text(buf, 0, 0, 0, 0, buf_text) local w, h = vim.lsp.util._make_floating_popup_size(buf_text) -- adding highlight so we can see which one is being selected local extmark = vim.api.nvim_buf_set_extmark(buf, current_nsid, row_selection, 0, { hl_group = 'incsearch', end_line = row_selection + row_offset }) -- shows window at a beginning of choiceNode local win = vim.api.nvim_open_win(buf, false, { relative = "win", width = w, height = h, bufpos = choiceNode.mark:pos_begin_end(), style = "minimal", border = 'rounded' }) -- return with 3 main important so we can use them again return { win_id = win, extmark = extmark, buf = buf } end function M.choice_popup(choiceNode) -- build stack for nested choiceNodes. if current_win then vim.api.nvim_win_close(current_win.win_id, true) vim.api.nvim_buf_del_extmark(current_win.buf, current_nsid, current_win.extmark) end local create_win = window_for_choiceNode(choiceNode) current_win = { win_id = create_win.win_id, prev = current_win, node = choiceNode, extmark = create_win.extmark, buf = create_win.buf } end function M.update_choice_popup(choiceNode) vim.api.nvim_win_close(current_win.win_id, true) vim.api.nvim_buf_del_extmark(current_win.buf, current_nsid, current_win.extmark) local create_win = window_for_choiceNode(choiceNode) current_win.win_id = create_win.win_id current_win.extmark = create_win.extmark current_win.buf = create_win.buf end function M.choice_popup_close() vim.api.nvim_win_close(current_win.win_id, true) vim.api.nvim_buf_del_extmark(current_win.buf, current_nsid, current_win.extmark) -- now we are checking if we still have previous choice we were in after exit nested choice current_win = current_win.prev if current_win then -- reopen window further down in the stack. local create_win = window_for_choiceNode(current_win.node) current_win.win_id = create_win.win_id current_win.extmark = create_win.extmark current_win.buf = create_win.buf end end -- Make sure indenting `#` works even if `smartindent` and `cindent` is on function M.always_working_indent_line() -- get the current settings local smartindent = vim.o.smartindent local cindent = vim.o.cindent -- set smartindent and cindent off vim.o.smartindent = false vim.o.cindent = false -- indent the line vim.api.nvim_feedkeys('>>', 'x', true) -- reset it back to previous settings vim.o.smartindent = smartindent vim.o.cindent = cindent end return M ================================================ FILE: home/budiman/config/neovim/plugins/blink-cmp/default.nix ================================================ { pkgs, lib, config, ... }: { config.plugins = { blink-cmp = { enable = true; package = pkgs.unstable.vimPlugins.blink-cmp; settings = { appearance.nerd_font_variant = "normal"; # UbuntuMono Nerd Font is not a mono font? snippets.preset = "luasnip"; sources = { providers = { path = { score_offset = 40; opts.trailing_slash = false; }; lsp.score_offset = 30; snippets = { score_offset = 20; opts.use_label_description = true; }; buffer.score_offset = 10; }; default = [ "lsp" "path" "snippets" "buffer" ]; }; keymap = { preset = "none"; "" = [ "select_next" "fallback" ]; "" = [ "select_prev" "fallback" ]; "" = [ "scroll_documentation_up" "fallback" ]; "" = [ "scroll_documentation_down" "fallback" ]; "" = [ "accept" "fallback" ]; "" = [ "select_and_accept" "fallback" ]; }; completion = { keyword.range = "full"; list.selection.preselect = false; menu.draw.columns = [ { __raw = "{'label', 'label_description', gap = 1}"; } { __raw = "{'kind_icon', 'kind'}"; } { __raw = "{'source_name'}"; } ]; documentation.auto_show = true; ghost_text = { enabled = true; show_without_selection = true; }; }; fuzzy.sorts = [ "exact" "score" "sort_text" ]; cmdline = { keymap = { preset = "none"; "" = [ "show_and_insert_or_accept_single" "select_next" ]; "" = [ "show_and_insert_or_accept_single" "select_prev" ]; "" = [ "select_next" "fallback" ]; "" = [ "select_prev" "fallback" ]; "" = [ "accept" "fallback" ]; "" = [ "select_accept_and_enter" "fallback" ]; }; completion = { menu.auto_show = lib.mkIf config.plugins.noice.enable { __raw = '' function(ctx) return vim.fn.getcmdtype() == ':' end ''; }; list.selection.preselect = false; }; }; }; }; }; } ================================================ FILE: home/budiman/config/neovim/plugins/chezmoi-vim/default.nix ================================================ { myPkgs, ... }: { config = { extraPlugins = [ myPkgs.nvim-plugins.chezmoi-vim ]; globals."chezmoi#use_tmp_buffer" = true; }; } ================================================ FILE: home/budiman/config/neovim/plugins/cord/default.nix ================================================ { config.plugins.cord = { enable = true; settings.display.theme = "catppuccin"; }; } ================================================ FILE: home/budiman/config/neovim/plugins/default.nix ================================================ { imports = [ ./blink-cmp ./chezmoi-vim ./cord ./endec ./gitsigns ./lualine ./grug-far ./luasnip ./mini ./noice ./none-ls ./nvim-autopairs ./oil ./snacks ./toggleterm ./treesitter ]; config.plugins = { web-devicons.enable = true; colorizer.enable = true; ts-autotag.enable = true; }; } ================================================ FILE: home/budiman/config/neovim/plugins/endec/default.nix ================================================ { pkgs, ... }: { config = { extraPlugins = [ pkgs.unstable.vimPlugins.endec-nvim ]; extraConfigLua = '' require('endec').setup({ keymaps = { defaults = false, decode_base64_popup = "bd", vdecode_base64_popup = "bd", encode_base64_inplace = "be", vencode_base64_inplace = "be", } }) ''; }; } ================================================ FILE: home/budiman/config/neovim/plugins/gitsigns/default.nix ================================================ { config.plugins.gitsigns = { enable = true; settings = { linehl = true; current_line_blame = true; signs = { add.text = "󰐕"; change.text = "󰐕"; delete.text = "_"; topdelete.text = "‾"; changedelete.text = "~"; untracked.text = "┆"; }; signs_staged_enable = true; signs_staged = { add.text = "󰐕"; change.text = "󰐕"; delete.text = "_"; topdelete.text = "‾"; changedelete.text = "~"; untracked.text = "┆"; }; on_attach.__raw = '' function(bufnr) local gitsigns = require('gitsigns') local function map(mode, lhs, rhs, opts) opts = opts or {} opts.buffer = bufnr vim.keymap.set(mode, lhs, rhs, opts) end map('n', '', function() gitsigns.nav_hunk('next') end, { desc = "Go to next hunk" }) map('n', '', function() gitsigns.nav_hunk('prev') end, { desc = "Go to prev hunk" }) map('n', 'ga', gitsigns.stage_hunk, { desc = "Git stage hunk" }) map('n', 'gr', gitsigns.reset_hunk, { desc = "Git reset hunk" }) map('v', 'ga', function() gitsigns.stage_hunk({ vim.fn.line('.'), vim.fn.line('v') }) end, { desc = "Git stage hunk" }) map('v', 'gr', function() gitsigns.reset_hunk({ vim.fn.line('.'), vim.fn.line('v') }) end, { desc = "Git reset hunk" }) map('n', 'gaa', gitsigns.stage_buffer, { desc = "Git stage buffer" }) map('n', 'gra', gitsigns.reset_buffer, { desc = "Reset git actions in buffer" }) map('n', 'gh', gitsigns.preview_hunk, { desc = "Preview git actions in hunk" }) map('n', 'gd', gitsigns.diffthis, { desc = "Git diff this hunk" }) end ''; }; }; } ================================================ FILE: home/budiman/config/neovim/plugins/grug-far/default.nix ================================================ { config = { plugins.grug-far = { enable = true; settings.keymaps = { replace.n = "W"; syncLocations.n = "w"; syncLine.n = ""; applyNext.n = "j"; applyPrev.n = "k"; openLocation.n = ""; previewLocation.n = "i"; refresh.n = "f"; close.n = "q"; help.n = "g?"; abort.n = "qq"; qflist = false; historyOpen = false; historyAdd = false; openNextLocation = false; openPrevLocation = false; pickHistoryEntry = false; toggleShowCommand = false; swapEngine = false; swapReplacementInterpreter = false; syncNext = false; syncPrev = false; syncFile = false; nextInput = false; prevInput = false; }; }; keymaps = [ { mode = [ "n" "v" ]; key = "sr"; action.__raw = '' function() return require('grug-far').open({ prefills = { search = vim.api.nvim_get_current_line() }; }) end ''; options.desc = "Open grug-far search and replace window with current line under cursor prefilled"; } ]; }; } ================================================ FILE: home/budiman/config/neovim/plugins/lualine/default.nix ================================================ { config.plugins.lualine = { enable = true; settings = { options = { theme = "catppuccin-nvim"; globalstatus = false; icons_enabled = true; section_separators = { left = ""; right = ""; }; component_separators = ""; disabled_filetypes = [ "oil" ]; }; sections = { lualine_a = [ { __unkeyed-1 = "mode"; separator = { left = ""; right = ""; }; } ]; lualine_b = [ { __unkeyed-1 = "branch"; separator = { left = ""; right = ""; }; } ]; lualine_c = [ { __unkeyed-1 = "diff"; symbols = { added = " "; modified = " "; removed = " "; }; } { __unkeyed-1 = "diagnostics"; sources = [ "nvim_diagnostic" ]; } { __unkeyed-1 = "filename"; path = 1; shorting_target = 30; symbols = { modified = " 󰎜"; readonly = " 󰈡"; unnamed = " 󰎞"; }; } ]; lualine_x = [ "filetype" ]; lualine_y = [ "progress" ]; lualine_z = [ { __unkeyed-1 = "location"; separator = { left = ""; right = ""; }; } ]; }; tabline = { lualine_b = [ { __unkeyed-1 = "buffers"; separator = { left = ""; right = ""; }; show_modified_status = false; buffers_color = { active = "lualine_b_normal"; inactive = "lualine_c_inactive"; }; } ]; lualine_z = [ { # display macro recording cond.__raw = "function() return vim.fn.reg_recording() ~= '' end"; __unkeyed-1.__raw = "function() return 'Recording @' .. vim.fn.reg_recording() end"; separator = { left = ""; right = ""; }; } ]; }; }; }; } ================================================ FILE: home/budiman/config/neovim/plugins/luasnip/default.nix ================================================ { pkgs, myPkgs, lib, ... }: let vimPlugins = pkgs.vimPlugins // myPkgs.nvim-plugins; in { config = { extraPlugins = with vimPlugins; [ friendly-snippets k8s-snippets ]; plugins.luasnip = { enable = true; fromLua = [ { paths = ./lua-snippets; } ]; fromVscode = [ { exclude = [ "gitcommit" ]; } ]; # Hint snippet node-type with virtual text # https://github.com/L3MON4D3/LuaSnip/wiki/Nice-Configs#hint-node-type-with-virtual-text settings.ext_opts = lib.nixvim.toRawKeys { "require('luasnip.util.types').choiceNode".active.virt_text = [ [ "● " "DiagnosticVirtualTextWarn" ] ]; "require('luasnip.util.types').insertNode".active.virt_text = [ [ "● " "DiagnosticVirtualTextInfo" ] ]; }; }; keymaps = [ { mode = [ "i" "s" ]; key = ""; action.__raw = '' function() if require('luasnip').choice_active() then require('luasnip').change_choice(1) else vim.api.nvim_input("") end end''; options.desc = "Select choice node in snippet or move cursor down"; } { mode = [ "i" "s" ]; key = ""; action.__raw = '' function() if require('luasnip').choice_active() then require('luasnip').change_choice(-1) else vim.api.nvim_input("") end end''; options.desc = "Select choice node in snippet or move cursor up"; } { mode = [ "i" "s" ]; key = ""; action.__raw = '' function() if require('luasnip').expand_or_locally_jumpable() then require('luasnip').jump(1) else vim.api.nvim_feedkeys(vim.keycode(""), "i", false) end end''; options.desc = "Go to next snippet or move cursor right"; } { mode = [ "i" "s" ]; key = ""; action.__raw = '' function() if require('luasnip').locally_jumpable() then require('luasnip').jump(-1) else vim.api.nvim_feedkeys(vim.keycode(""), "i", false) end end''; options.desc = "Go to next snippet or move cursor left"; } ]; # ChoiceNode popup in snippet autoGroups.choicepopup.clear = true; autoCmd = [ { event = [ "User" ]; pattern = [ "LuasnipChoiceNodeEnter" "LuasnipChoiceNodeLeave" "LuasnipChangeChoice" ]; group = "choicepopup"; callback.__raw = '' function(arg) local match = arg.match if match == 'LuasnipChoiceNodeEnter' then require('utils').choice_popup(require('luasnip').session.event_node) elseif match == 'LuasnipChoiceNodeLeave' then require('utils').choice_popup_close() elseif match == "LuasnipChangeChoice" then require('utils').update_choice_popup(require('luasnip').session.event_node) end end ''; } ]; }; } ================================================ FILE: home/budiman/config/neovim/plugins/luasnip/lua-snippets/gitcommit.lua ================================================ local ls = require('luasnip') local s = ls.snippet local text = ls.text_node local insert = ls.insert_node local choice = ls.choice_node local nonempty = require('luasnip.extras').nonempty return { s({ trig = 'cc', name = 'conventional commit', dscr = 'Conventional commit', }, { choice(1, { text('feat'), text('fix'), text('doc'), text('chore'), }), nonempty(2, '(', ''), insert(2, 'scope'), nonempty(2, ')', ''), text(': '), insert(3, 'title'), insert(0), }), s({ trig = 'fix', name = 'fix conventional commit', dscr = 'Fix conventional commit', }, { text('fix'), nonempty(1, '(', ''), insert(1, 'scope'), nonempty(1, ')', ''), text(': '), insert(2, 'title'), insert(0), }), s({ trig = 'feat', name = 'feat conventional commit', dscr = 'Feat conventional commit', }, { text('feat'), nonempty(1, '(', ''), insert(1, 'scope'), nonempty(1, ')', ''), text(': '), insert(2, 'title'), insert(0), }), s({ trig = 'chore', name = 'chore conventional commit', dscr = 'Chore conventional commit', }, { text('chore'), nonempty(1, '(', ''), insert(1, 'scope'), nonempty(1, ')', ''), text(': '), insert(2, 'title'), insert(0), }), s({ trig = 'docs', name = 'docs conventional commit', dscr = 'Docs conventional commit', }, { text('docs'), nonempty(1, '(', ''), insert(1, 'scope'), nonempty(1, ')', ''), text(': '), insert(2, 'title'), insert(0), }), s({ trig = 'cc!', name = 'conventional commit breaking', dscr = 'Conventional commit with breaking change(s)', }, { choice(1, { text('feat'), text('fix'), }), nonempty(2, '(', ''), insert(2, 'scope'), nonempty(2, ')', ''), text('!: '), insert(3, 'title'), text({'', '', 'BREAKING CHANGE: '}), insert(0), }), s({ trig = 'feat!', name = 'feat conventional commit breaking', dscr = 'feat conventional commit with breaking change(s)', }, { text('feat'), nonempty(1, '(', ''), insert(1, 'scope'), nonempty(1, ')', ''), text('!: '), insert(2, 'title'), text({'', '', 'BREAKING CHANGE: '}), insert(0), }), s({ trig = 'fix!', name = 'fix conventional commit breaking', dscr = 'fix conventional commit with breaking change(s)', }, { text('fix'), nonempty(1, '(', ''), insert(1, 'scope'), nonempty(1, ')', ''), text('!: '), insert(2, 'title'), text({'', '', 'BREAKING CHANGE: '}), insert(0), }), } ================================================ FILE: home/budiman/config/neovim/plugins/mini/comment.nix ================================================ { plugins.mini.modules.comment = { }; } ================================================ FILE: home/budiman/config/neovim/plugins/mini/default.nix ================================================ { imports = [ ./comment.nix ./icons.nix ./indentscope.nix ./surround.nix ./trailspace.nix ]; config.plugins.mini.enable = true; } ================================================ FILE: home/budiman/config/neovim/plugins/mini/icons.nix ================================================ { plugins.mini = { mockDevIcons = true; modules.icons = { }; }; } ================================================ FILE: home/budiman/config/neovim/plugins/mini/indentscope.nix ================================================ { plugins.mini.modules.indentscope = { draw.delay = 0; symbol = "▎"; options.try_as_border = true; }; } ================================================ FILE: home/budiman/config/neovim/plugins/mini/surround.nix ================================================ { plugins.mini.modules.surround = { }; } ================================================ FILE: home/budiman/config/neovim/plugins/mini/trailspace.nix ================================================ { plugins.mini.modules.trailspace = { }; # custom `trim_trailing_lastline` editorconfig property # that will remove trailing last lines from file # based on the `trim_trailing_whitespace` editorconfig.properties.trim_trailing_lastline = '' function(bufnr, val) assert( val == 'true' or val == 'false', 'trim_trailing_lastline must be either "true" or "false"' ) local group = vim.api.nvim_create_augroup( 'nvim.editorconfig.trim_trailing_ll', {} ) if val == 'true' then vim.api.nvim_create_autocmd('BufWritePre', { group = group, buffer = bufnr, callback = function() MiniTrailspace.trim_last_lines() end, }) else vim.api.nvim_clear_autocmds({ event = 'BufWritePre', group = group, }) end end ''; } ================================================ FILE: home/budiman/config/neovim/plugins/noice/default.nix ================================================ { config = { colorschemes.catppuccin.settings.integrations = { noice = true; notify = true; }; plugins = { # dependencies notify.enable = true; nui.enable = true; noice = { enable = true; settings = { lsp = { signature.enabled = false; # override markdown rendering to use treesitter override = { "vim.lsp.util.convert_input_to_markdown_lines" = true; "vim.lsp.util.stylize_markdown" = true; }; }; messages.view_history = "popup"; popupmenu.enabled = false; routes = [ # use mini notification for :w messages { filter = { event = "msg_show"; kind = ""; find = "written"; }; view = "mini"; } # hide search virtual text { filter = { event = "msg_show"; kind = "search_count"; }; opts.skip = true; } # hide `vim.tbl_islist` is deprecated message { filter = { event = "msg_show"; find = "vim.tbl_islist is deprecated"; }; opts.skip = true; } # route any messages with more than 10 lines to popup view { filter = { event = "msg_show"; min_height = 10; }; view = "popup"; } ]; }; }; }; }; } ================================================ FILE: home/budiman/config/neovim/plugins/none-ls/default.nix ================================================ { pkgs, ... }: { config.plugins.none-ls = { enable = true; settings.diagnostics_format = "[#{c}] #{m} (#{s})"; sources = { formatting = { nixfmt = { enable = true; package = pkgs.nixfmt-rfc-style; }; prettier = { enable = true; disableTsServerFormatter = true; }; shfmt = { enable = true; settings.extra_args = [ "-i" "2" "-ci" ]; }; }; diagnostics = { ansiblelint = { enable = true; # TODO: ansible-lint is currently broken on stable # ref: https://github.com/NixOS/nixpkgs/issues/460422 package = pkgs.unstable.ansible-lint; settings.filetypes = [ "yaml.ansible" ]; }; golangci_lint = { enable = true; package = pkgs.unstable.golangci-lint; }; markdownlint.enable = true; write_good.enable = true; yamllint.enable = true; }; }; }; } ================================================ FILE: home/budiman/config/neovim/plugins/nvim-autopairs/_rules.lua ================================================ local rule = require('nvim-autopairs.rule') local cond = require('nvim-autopairs.conds') local ts_conds = require('nvim-autopairs.ts-conds') local npairs = require('nvim-autopairs') local function mkSpaceBracketExitRule(t) return rule(t[1] .. ' ', ' ' .. t[2]) :with_pair(cond.none()) -- this is needed so the `with_del()` method -- for the rule: "(|) press space => ( | )" works :with_del(cond.none()) :with_move(function(opts) return opts.char == t[2] end) :use_key(t[2]) -- removes the trailing whitespaces on :replace_map_cr(function(_) return '2xiO' end) end npairs.add_rules{ -- (|) press space => ( | ) -- {|} press space => { | } -- [|] press space => [ | ] rule(' ', ' ') :with_pair(function (opts) local pair = opts.line:sub(opts.col - 1, opts.col) return vim.tbl_contains({ '()', '{}', '[]' }, pair) end) -- we only want to delete the pair of spaces :with_del(function(opts) local col = vim.api.nvim_win_get_cursor(0)[2] local context = opts.line:sub(col - 1, col + 2) return vim.tbl_contains({ '( )', '{ }', '[ ]' }, context) end), -- ( | ) press ) => ( )| mkSpaceBracketExitRule({'(', ')'}), -- { | } press } => { }| mkSpaceBracketExitRule({'{', '}'}), -- [ | ] press ] => [ ]| mkSpaceBracketExitRule({'[', ']'}), -- |; press ; => ;| rule('', ';') :with_move(function(opts) return opts.char == ';' end) :with_pair(cond.none()) :with_del(cond.none()) :with_cr(cond.none()) :use_key(';'), -- a =| press => a = |; -- for nix filetype rule('= ', ';', 'nix') :with_pair(function(opts) local prev_char = opts.line:sub(opts.col - 2, opts.col - 2) return prev_char:match('[^=<>!]') ~= nil and ts_conds.is_not_ts_node('source')(opts) end), -- with | press => with |; -- for nix filetype rule('with ', ';', 'nix') :with_pair(function(opts) local prev_char = opts.line:sub(opts.col - 5, opts.col - 5) return prev_char:match('[^%w_-]') ~= nil and ts_conds.is_not_ts_node('source')(opts) end), -- {| press ' => {'|',}, -- for lua filetype rule("'", "',", 'lua') :with_pair(ts_conds.is_ts_node({ 'table_constructor' })), -- {| press " => {"|",}, -- for lua filetype rule('"', '",', 'lua') :with_pair(ts_conds.is_ts_node({ 'table_constructor' })), } ================================================ FILE: home/budiman/config/neovim/plugins/nvim-autopairs/default.nix ================================================ { config = { extraFiles."lua/nvim-autopairs/_rules.lua".source = ./_rules.lua; plugins.nvim-autopairs = { enable = true; luaConfig = { post = "require('nvim-autopairs._rules')"; }; }; }; } ================================================ FILE: home/budiman/config/neovim/plugins/oil/default.nix ================================================ { config = { plugins.oil = { enable = true; settings = { columns = [ "icon" "size" ]; use_default_keymaps = false; keymaps = { "?" = "actions.show_help"; "" = "actions.select"; "" = "actions.parent"; "" = "actions.select"; "`" = "actions.open_cwd"; "" = "actions.select_split"; "" = "actions.select_vsplit"; "q" = "actions.close"; "" = "actions.refresh"; "." = "actions.toggle_hidden"; }; float = { max_width = 50; max_height = 40; border = "rounded"; win_options.winblend = 10; }; }; }; keymaps = [ { mode = "n"; key = ""; action.__raw = "function() return require('oil').toggle_float() end"; options.desc = "Toggle floating file explorer"; } ]; }; } ================================================ FILE: home/budiman/config/neovim/plugins/snacks/default.nix ================================================ { pkgs, ... }: { imports = [ ./picker.nix ./input.nix ]; config = { plugins.snacks = { enable = true; # TODO: wait for the stable version to get commit 9ad5d53 package = pkgs.unstable.vimPlugins.snacks-nvim; }; colorschemes.catppuccin.settings.integrations.snacks.enabled = true; }; } ================================================ FILE: home/budiman/config/neovim/plugins/snacks/input.nix ================================================ { lib, config, ... }: { plugins.snacks.settings = { styles.input = { relative = "cursor"; row = -3; col = 0; b.completion = lib.mkIf config.plugins.blink-cmp.enable true; }; input = { enabled = true; }; }; } ================================================ FILE: home/budiman/config/neovim/plugins/snacks/picker.nix ================================================ { pkgs, ... }: { plugins.snacks.settings.picker = { layout = "telescope"; layouts.telescope = { reverse = false; cycle = true; }; # TODO: very long title is being truncated with defaults layouts.select.layout.min_width = 120; formatters.file.min_width = 60; sources = { files.hidden = true; help.win.preview.wo.wrap = true; buffers.current = false; # don't show current buffer }; win.input.keys = { "" = { __unkeyed-1 = "close"; mode = [ "i" "n" ]; }; "" = { __unkeyed-1 = "toggle_hidden"; mode = [ "i" "n" ]; }; }; }; extraPackages = with pkgs; [ ripgrep fd git ]; keymaps = [ { mode = "n"; key = "fz"; action.__raw = "function() Snacks.picker() end"; options.desc = "Find everything"; } { mode = "n"; key = "ff"; action.__raw = "function() Snacks.picker.files() end"; options.desc = "Find files"; } { mode = "n"; key = "fg"; action.__raw = "function() Snacks.picker.grep() end"; options.desc = "Grep from project"; } { mode = "n"; key = "fc"; action.__raw = "function() Snacks.picker.git_log() end"; options.desc = "Git commits log"; } { mode = "n"; key = "fb"; action.__raw = "function() Snacks.picker.buffers() end"; options.desc = "Find from buffers"; } { mode = "n"; key = "fh"; action.__raw = "function() Snacks.picker.help() end"; options.desc = "Find help pages"; } { mode = "n"; key = "fk"; action.__raw = "function() Snacks.picker.keymaps() end"; options.desc = "Find keymaps"; } { mode = "n"; key = "fe"; action.__raw = "function() Snacks.picker.diagnostics_buffer() end"; options.desc = "Find buffer diagnostics"; } ]; } ================================================ FILE: home/budiman/config/neovim/plugins/toggleterm/default.nix ================================================ { config = { plugins.toggleterm = { enable = true; settings = { open_mapping = "[[]]"; direction = "float"; insert_mapping = false; shade_terminals = false; highlights = { FloatBorder.link = "FloatBorder"; NormalFloat.link = "NormalFloat"; }; float_opts = { border = "curved"; width.__raw = '' function() return math.ceil(vim.o.columns * 0.8) end ''; height.__raw = '' function() return math.ceil((vim.o.lines - 2) * 0.8) end ''; }; }; }; }; } ================================================ FILE: home/budiman/config/neovim/plugins/treesitter/default.nix ================================================ { config.plugins.treesitter = { enable = true; nixvimInjections = true; settings = { highlight = { enable = true; disable.__raw = '' function(_, bufnr) if string.find(vim.bo.filetype, 'chezmoitmpl') then return true end return vim.api.nvim_buf_line_count(bufnr) > 50000 end ''; }; }; }; } ================================================ FILE: home/budiman/default.nix ================================================ { hostname, myPkgs, config, ... }: { # host specific config imports = [ ./hosts/${hostname}.nix ]; # global config applied to all hosts config = { myHome = { editor.neovim = { enable = true; package = myPkgs.neovim; }; shell.git = { enable = true; config = { commit = { template = "${./config/gitcommit-message}"; gpgSign = true; }; user = { name = "budimanjojo"; email = "budimanjojo@gmail.com"; signingKey = "${config.home.homeDirectory}/.ssh/id_rsa.pub"; }; gpg = { format = "ssh"; }; }; }; }; }; } ================================================ FILE: home/budiman/hosts/budimanjojo-firewall.nix ================================================ { ... }: { imports = [ ../profiles/server.nix ]; } ================================================ FILE: home/budiman/hosts/budimanjojo-main.nix ================================================ { pkgs, ... }: { imports = [ ../profiles/workstation-hyprland.nix ../profiles/extra-gaming.nix ../profiles/extra-graphics.nix ../profiles/extra-utilities.nix ]; config = { myHome = { browser.firefox.profiles = { "budiman" = { id = 0; name = "budiman"; isDefault = true; extensions.packages = with pkgs.nur.repos.rycee.firefox-addons; [ bitwarden tokyo-night-v2 ]; }; "lina" = { id = 1; name = "lina"; isDefault = false; extensions.packages = with pkgs.nur.repos.rycee.firefox-addons; [ bitwarden tokyo-night-v2 ]; }; }; programs.qmk.enable = true; }; home.packages = [ pkgs.hugo ]; }; } ================================================ FILE: home/budiman/hosts/budimanjojo-nas.nix ================================================ { ... }: { imports = [ ../profiles/server.nix ]; } ================================================ FILE: home/budiman/hosts/budimanjojo-oracle.nix ================================================ { ... }: { imports = [ ../profiles/server.nix ]; } ================================================ FILE: home/budiman/hosts/budimanjojo-ubuntu.nix ================================================ { pkgs, inputs, ... }: { config = { myHome = { programs = { chezmoi.enable = true; go.enable = true; qmk.enable = true; }; multiplexer.tmux.enable = true; homelab.kubernetes.enable = true; shell.lf.enable = true; shell.fish.enable = true; terminal-emulator.contour.enable = true; terminal-emulator.alacritty.enable = true; }; home.packages = [ pkgs.hugo ]; targets.genericLinux = { # this add nix installed desktop files to be shown in application menu enable = true; nixGL.packages = inputs.nixgl.packages; }; }; } ================================================ FILE: home/budiman/profiles/extra-gaming.nix ================================================ { pkgs, ... }: { config = { home.packages = with pkgs; [ (retroarch.withCores ( cores: with cores; [ mesen fceumm ] )) ]; }; } ================================================ FILE: home/budiman/profiles/extra-graphics.nix ================================================ { pkgs, ... }: { config.home.packages = with pkgs; [ gimp inkscape ]; } ================================================ FILE: home/budiman/profiles/extra-utilities.nix ================================================ { pkgs, ... }: { config.home.packages = with pkgs; [ gparted eog totem rhythmbox jellyfin-media-player ]; } ================================================ FILE: home/budiman/profiles/server.nix ================================================ { ... }: { config = { myHome = { shell = { fish.enable = true; }; multiplexer.tmux.enable = true; }; }; } ================================================ FILE: home/budiman/profiles/workstation-common.nix ================================================ { pkgs, ... }: { config = { myHome = { shell = { fish.enable = true; lf.enable = true; }; multiplexer.tmux.enable = true; browser.firefox.enable = true; services.opencloud-client.enable = true; homelab.kubernetes.enable = true; programs.go.enable = true; }; home.packages = with pkgs; [ geany libreoffice-fresh discord ]; }; } ================================================ FILE: home/budiman/profiles/workstation-hyprland.nix ================================================ { ... }: { imports = [ ./workstation-common.nix ]; config.myHome.windowmanager.hyprland.enable = true; } ================================================ FILE: home/budiman/profiles/workstation-i3.nix ================================================ { ... }: { imports = [ ./workstation-common.nix ]; config.myHome.windowmanager.i3.enable = true; } ================================================ FILE: home/budiman/profiles/workstation-sway.nix ================================================ { ... }: { imports = [ ./workstation-common.nix ]; config.myHome.windowmanager.sway.enable = true; } ================================================ FILE: lib/default.nix ================================================ { lib, ... }: rec { # systemEnabled returns the value of `osConfig.module` # returns false if `module` doesn't exist in the `osConfig` # will panic if `osConfig.module` is not type of bool # I use this to determine if the home module should be enabled depending on the system module # for example: `systemEnabled "programs.hyprland.enable" osConfig` systemEnabled = module: osConfig: if (isNixos osConfig) then let splitted = lib.splitString "." module; val = lib.attrByPath splitted false osConfig; in if (builtins.isBool val) then val else builtins.abort "osConfig.${module} is not type of bool" else false; # copyFromSystem returns the value of `osConfig.module` # returns empty attrset if system is not NixOS # I use this to set shared system and home modules automatically # for example: `config.mySharedModule = copyFromSystem "mySharedModule" osConfig` copyFromSystem = module: osConfig: if (isNixos osConfig) then let splitted = lib.splitString "." module; val = lib.getAttrFromPath splitted osConfig; in val else { }; # isAdminUser returns true if the username is equal to `osConfig.mySystem.adminUser` isAdminUser = username: osConfig: if (isNixos osConfig) then osConfig.mySystem.adminUser == username else false; # isNixos returns true if the provided `osConfig` is not an empty attrset isNixos = set: set != { }; } ================================================ FILE: overlays/default.nix ================================================ { inputs, config, ... }: { # NUR pkgs set (declared in the flake inputs) will be accessible # through `pkgs.nur` nur = inputs.nur.overlays.default; # nixGL pkgs set (declared in the flake inputs) will be accessible # through `pkgs.nixgl` nixgl = inputs.nixgl.overlay; # The unstable nixpkgs set (declared in the flake inputs) will # be accessible through `pkgs.unstable` unstable-packages = final: prev: { unstable = import inputs.nixpkgs-unstable { localSystem = final.stdenv.hostPlatform; config = config; overlays = [ # overlays of unstable packages are declared here ]; }; }; # Your own overlays for stable nixpkgs should be declared here nixpkgs-overlays = final: prev: { # vimPlugins = prev.vimPlugins // { # # this version have fzf integration added # catppuccin-nvim = prev.vimPlugins.catppuccin-nvim.overrideAttrs (oldAttrs: { # version = "2024-08-20"; # src = prev.fetchFromGitHub { # owner = "catppuccin"; # repo = "nvim"; # rev = "4fd72a9ab64b393c2c22b168508fd244877fec96"; # sha256 = "sha256-aNmnn7Ym3+OnuvSgpke6rw4AkoVfNCpbjV71JF1c9rs="; # }; # }); # }; # talosctl = prev.talosctl.override { # buildGoModule = # args: # prev.buildGoModule ( # args # // { # version = "1.5.1"; # src = prev.fetchFromGitHub { # owner = "siderolabs"; # repo = "talos"; # rev = "v1.5.1"; # hash = "sha256-HYIk1oZbtcnHLap+4AMwoQN0k44zjiiwDzGcNW+9qqM="; # }; # vendorHash = "sha256-Aefwa8zdKWV9TE9rwNA4pzKZekTurkD0pTDm3QfKdUQ="; # } # ); # }; # nil = prev.nil.overrideAttrs (old: rec { # pname = "nil"; # version = "2023-03-01"; # src = prev.fetchFromGitHub { # owner = "oxalica"; # repo = "nil"; # rev = "2023-03-01"; # hash = "sha256-HGd/TV8ZHVAVBx+ndrxAfS/Nz+VHOQjNWjtKkkgYkqA="; # }; # CFG_RELEASE = version; # # cargoDeps = old.cargoDeps.overrideAttrs (_: { # name = "${pname}-vendor.tar.gz"; # inherit src CFG_RELEASE; # outputHash = "sha256-0OEX4XxC2lkccFng4bQUoAyFbv1snJT2Ku7tUVbx4BU="; # }); # }); # wlroots_0_16 = prev.wlroots_0_16.overrideAttrs (old: { # src = prev.fetchFromGitLab { # domain = "gitlab.freedesktop.org"; # owner = "wlroots"; # repo = "wlroots"; # rev = "0.16.1"; # hash = "sha256-UyPN7zmytre4emwx/ztZ4JefXHwixPV6UEEqnhSLbIY="; # }; # }); }; } ================================================ FILE: packages/_sources/generated.json ================================================ { "abbreviation-tips": { "cargoLock": null, "date": null, "extract": null, "name": "abbreviation-tips", "passthru": null, "pinned": false, "src": { "deepClone": false, "fetchSubmodules": false, "leaveDotGit": false, "name": null, "owner": "gazorby", "repo": "fish-abbreviation-tips", "rev": "v0.7.0", "sha256": "sha256-F1t81VliD+v6WEWqj1c1ehFBXzqLyumx5vV46s/FZRU=", "sparseCheckout": [], "type": "github" }, "version": "v0.7.0" }, "chezmoi-vim": { "cargoLock": null, "date": "2025-10-31", "extract": null, "name": "chezmoi-vim", "passthru": null, "pinned": false, "src": { "deepClone": false, "fetchSubmodules": false, "leaveDotGit": false, "name": null, "owner": "alker0", "repo": "chezmoi.vim", "rev": "73b30df35c6b645ebd2e6a440eea8463ef3c3f47", "sha256": "sha256-4gnY60CZUrVgqYhmyBdE4mTD7D4iqphpSgKWxl+UlzA=", "sparseCheckout": [], "type": "github" }, "version": "73b30df35c6b645ebd2e6a440eea8463ef3c3f47" }, "fish-completion-sync": { "cargoLock": null, "date": "2025-03-09", "extract": null, "name": "fish-completion-sync", "passthru": null, "pinned": false, "src": { "deepClone": false, "fetchSubmodules": false, "leaveDotGit": false, "name": null, "owner": "iynaix", "repo": "fish-completion-sync", "rev": "4f058ad2986727a5f510e757bc82cbbfca4596f0", "sha256": "sha256-kHpdCQdYcpvi9EFM/uZXv93mZqlk1zCi2DRhWaDyK5g=", "sparseCheckout": [], "type": "github" }, "version": "4f058ad2986727a5f510e757bc82cbbfca4596f0" }, "guihua-lua": { "cargoLock": null, "date": "2026-04-28", "extract": null, "name": "guihua-lua", "passthru": null, "pinned": false, "src": { "deepClone": false, "fetchSubmodules": false, "leaveDotGit": false, "name": null, "owner": "ray-x", "repo": "guihua.lua", "rev": "7c364432c2f9153ed068f4eab1989edd9f3fd302", "sha256": "sha256-rgxqLQf7psUtXwnFOiDBt6CpMyaAMdz2pg3PKj12IzE=", "sparseCheckout": [], "type": "github" }, "version": "7c364432c2f9153ed068f4eab1989edd9f3fd302" }, "k8s-snippets": { "cargoLock": null, "date": "2025-11-13", "extract": null, "name": "k8s-snippets", "passthru": null, "pinned": false, "src": { "deepClone": false, "fetchSubmodules": false, "leaveDotGit": false, "name": null, "owner": "budimanjojo", "repo": "k8s-snippets", "rev": "d253da00664caa43584d8e245d25992650827dee", "sha256": "sha256-cq9ZCFwbMrxkdIrxuZwXN+KLfr2BYX+RdcPBkB7FIRQ=", "sparseCheckout": [], "type": "github" }, "version": "d253da00664caa43584d8e245d25992650827dee" }, "luasnip": { "cargoLock": null, "date": null, "extract": null, "name": "luasnip", "passthru": null, "pinned": false, "src": { "deepClone": false, "fetchSubmodules": false, "leaveDotGit": false, "name": null, "owner": "L3MON4D3", "repo": "LuaSnip", "rev": "v2.5.0", "sha256": "sha256-diZO1on0rlSp6XuNGN2lNa85rhkNe1QQOejJD+LKkZk=", "sparseCheckout": [], "type": "github" }, "version": "v2.5.0" }, "mason-lspconfig-nvim": { "cargoLock": null, "date": "2026-04-23", "extract": null, "name": "mason-lspconfig-nvim", "passthru": null, "pinned": false, "src": { "deepClone": false, "fetchSubmodules": false, "leaveDotGit": false, "name": null, "owner": "williamboman", "repo": "mason-lspconfig.nvim", "rev": "0c2823e0418f3d9230ff8b201c976e84de1cb401", "sha256": "sha256-wWoRUg2nvmqaEWxjYEOk1q+jQyKupgJi2LubhewcVCw=", "sparseCheckout": [], "type": "github" }, "version": "0c2823e0418f3d9230ff8b201c976e84de1cb401" }, "mason-tool-installer-nvim": { "cargoLock": null, "date": "2026-01-22", "extract": null, "name": "mason-tool-installer-nvim", "passthru": null, "pinned": false, "src": { "deepClone": false, "fetchSubmodules": false, "leaveDotGit": false, "name": null, "owner": "WhoIsSethDaniel", "repo": "mason-tool-installer.nvim", "rev": "443f1ef8b5e6bf47045cb2217b6f748a223cf7dc", "sha256": "sha256-OZGq2TKZx1+GSzrQAdk2fAUv3052NMfTkm5QdO+EXXk=", "sparseCheckout": [], "type": "github" }, "version": "443f1ef8b5e6bf47045cb2217b6f748a223cf7dc" }, "oil-nvim": { "cargoLock": null, "date": "2026-02-23", "extract": null, "name": "oil-nvim", "passthru": null, "pinned": false, "src": { "deepClone": false, "fetchSubmodules": false, "leaveDotGit": false, "name": null, "owner": "stevearc", "repo": "oil.nvim", "rev": "0fcc83805ad11cf714a949c98c605ed717e0b83e", "sha256": "sha256-hoTQoNEsCbZ0aZMUUUvgkC9NYjovjUUirw2FN9b9dn0=", "sparseCheckout": [], "type": "github" }, "version": "0fcc83805ad11cf714a949c98c605ed717e0b83e" }, "tmux-fish": { "cargoLock": null, "date": "2025-04-07", "extract": null, "name": "tmux-fish", "passthru": null, "pinned": false, "src": { "deepClone": false, "fetchSubmodules": false, "leaveDotGit": false, "name": null, "owner": "budimanjojo", "repo": "tmux.fish", "rev": "db0030b7f4f78af4053dc5c032c7512406961ea5", "sha256": "sha256-rRibn+FN8VNTSC1HmV05DXEa6+3uOHNx03tprkcjjs8=", "sparseCheckout": [], "type": "github" }, "version": "db0030b7f4f78af4053dc5c032c7512406961ea5" }, "tokyonight-gtk-theme": { "cargoLock": null, "date": "2025-10-23", "extract": null, "name": "tokyonight-gtk-theme", "passthru": null, "pinned": false, "src": { "deepClone": false, "fetchSubmodules": false, "leaveDotGit": false, "name": null, "owner": "Fausto-Korpsvart", "repo": "Tokyo-Night-GTK-Theme", "rev": "6c340e058e84c1975a038a8e5d1e384477225dc0", "sha256": "sha256-7H2n9wTaW8Db1RejWK071ITV1j5KIuzfql0Tx9WT6zM=", "sparseCheckout": [], "type": "github" }, "version": "6c340e058e84c1975a038a8e5d1e384477225dc0" }, "tokyonight-icon-theme": { "cargoLock": null, "date": "2025-10-23", "extract": null, "name": "tokyonight-icon-theme", "passthru": null, "pinned": false, "src": { "deepClone": false, "fetchSubmodules": false, "leaveDotGit": false, "name": null, "owner": "Fausto-Korpsvart", "repo": "Tokyo-Night-GTK-Theme", "rev": "6c340e058e84c1975a038a8e5d1e384477225dc0", "sha256": "sha256-7H2n9wTaW8Db1RejWK071ITV1j5KIuzfql0Tx9WT6zM=", "sparseCheckout": [], "type": "github" }, "version": "6c340e058e84c1975a038a8e5d1e384477225dc0" } } ================================================ FILE: packages/_sources/generated.nix ================================================ # This file was generated by nvfetcher, please do not modify it manually. { fetchgit, fetchurl, fetchFromGitHub, dockerTools, }: { abbreviation-tips = { pname = "abbreviation-tips"; version = "v0.7.0"; src = fetchFromGitHub { owner = "gazorby"; repo = "fish-abbreviation-tips"; rev = "v0.7.0"; fetchSubmodules = false; sha256 = "sha256-F1t81VliD+v6WEWqj1c1ehFBXzqLyumx5vV46s/FZRU="; }; }; chezmoi-vim = { pname = "chezmoi-vim"; version = "73b30df35c6b645ebd2e6a440eea8463ef3c3f47"; src = fetchFromGitHub { owner = "alker0"; repo = "chezmoi.vim"; rev = "73b30df35c6b645ebd2e6a440eea8463ef3c3f47"; fetchSubmodules = false; sha256 = "sha256-4gnY60CZUrVgqYhmyBdE4mTD7D4iqphpSgKWxl+UlzA="; }; date = "2025-10-31"; }; fish-completion-sync = { pname = "fish-completion-sync"; version = "4f058ad2986727a5f510e757bc82cbbfca4596f0"; src = fetchFromGitHub { owner = "iynaix"; repo = "fish-completion-sync"; rev = "4f058ad2986727a5f510e757bc82cbbfca4596f0"; fetchSubmodules = false; sha256 = "sha256-kHpdCQdYcpvi9EFM/uZXv93mZqlk1zCi2DRhWaDyK5g="; }; date = "2025-03-09"; }; guihua-lua = { pname = "guihua-lua"; version = "7c364432c2f9153ed068f4eab1989edd9f3fd302"; src = fetchFromGitHub { owner = "ray-x"; repo = "guihua.lua"; rev = "7c364432c2f9153ed068f4eab1989edd9f3fd302"; fetchSubmodules = false; sha256 = "sha256-rgxqLQf7psUtXwnFOiDBt6CpMyaAMdz2pg3PKj12IzE="; }; date = "2026-04-28"; }; k8s-snippets = { pname = "k8s-snippets"; version = "d253da00664caa43584d8e245d25992650827dee"; src = fetchFromGitHub { owner = "budimanjojo"; repo = "k8s-snippets"; rev = "d253da00664caa43584d8e245d25992650827dee"; fetchSubmodules = false; sha256 = "sha256-cq9ZCFwbMrxkdIrxuZwXN+KLfr2BYX+RdcPBkB7FIRQ="; }; date = "2025-11-13"; }; luasnip = { pname = "luasnip"; version = "v2.5.0"; src = fetchFromGitHub { owner = "L3MON4D3"; repo = "LuaSnip"; rev = "v2.5.0"; fetchSubmodules = false; sha256 = "sha256-diZO1on0rlSp6XuNGN2lNa85rhkNe1QQOejJD+LKkZk="; }; }; mason-lspconfig-nvim = { pname = "mason-lspconfig-nvim"; version = "0c2823e0418f3d9230ff8b201c976e84de1cb401"; src = fetchFromGitHub { owner = "williamboman"; repo = "mason-lspconfig.nvim"; rev = "0c2823e0418f3d9230ff8b201c976e84de1cb401"; fetchSubmodules = false; sha256 = "sha256-wWoRUg2nvmqaEWxjYEOk1q+jQyKupgJi2LubhewcVCw="; }; date = "2026-04-23"; }; mason-tool-installer-nvim = { pname = "mason-tool-installer-nvim"; version = "443f1ef8b5e6bf47045cb2217b6f748a223cf7dc"; src = fetchFromGitHub { owner = "WhoIsSethDaniel"; repo = "mason-tool-installer.nvim"; rev = "443f1ef8b5e6bf47045cb2217b6f748a223cf7dc"; fetchSubmodules = false; sha256 = "sha256-OZGq2TKZx1+GSzrQAdk2fAUv3052NMfTkm5QdO+EXXk="; }; date = "2026-01-22"; }; oil-nvim = { pname = "oil-nvim"; version = "0fcc83805ad11cf714a949c98c605ed717e0b83e"; src = fetchFromGitHub { owner = "stevearc"; repo = "oil.nvim"; rev = "0fcc83805ad11cf714a949c98c605ed717e0b83e"; fetchSubmodules = false; sha256 = "sha256-hoTQoNEsCbZ0aZMUUUvgkC9NYjovjUUirw2FN9b9dn0="; }; date = "2026-02-23"; }; tmux-fish = { pname = "tmux-fish"; version = "db0030b7f4f78af4053dc5c032c7512406961ea5"; src = fetchFromGitHub { owner = "budimanjojo"; repo = "tmux.fish"; rev = "db0030b7f4f78af4053dc5c032c7512406961ea5"; fetchSubmodules = false; sha256 = "sha256-rRibn+FN8VNTSC1HmV05DXEa6+3uOHNx03tprkcjjs8="; }; date = "2025-04-07"; }; tokyonight-gtk-theme = { pname = "tokyonight-gtk-theme"; version = "6c340e058e84c1975a038a8e5d1e384477225dc0"; src = fetchFromGitHub { owner = "Fausto-Korpsvart"; repo = "Tokyo-Night-GTK-Theme"; rev = "6c340e058e84c1975a038a8e5d1e384477225dc0"; fetchSubmodules = false; sha256 = "sha256-7H2n9wTaW8Db1RejWK071ITV1j5KIuzfql0Tx9WT6zM="; }; date = "2025-10-23"; }; tokyonight-icon-theme = { pname = "tokyonight-icon-theme"; version = "6c340e058e84c1975a038a8e5d1e384477225dc0"; src = fetchFromGitHub { owner = "Fausto-Korpsvart"; repo = "Tokyo-Night-GTK-Theme"; rev = "6c340e058e84c1975a038a8e5d1e384477225dc0"; fetchSubmodules = false; sha256 = "sha256-7H2n9wTaW8Db1RejWK071ITV1j5KIuzfql0Tx9WT6zM="; }; date = "2025-10-23"; }; } ================================================ FILE: packages/configure-gtk/default.nix ================================================ { pkgs }: pkgs.writeTextFile { name = "configure-gtk"; destination = "/bin/configure-gtk"; executable = true; text = let schema = pkgs.gsettings-desktop-schemas; datadir = "${schema}/share/gsettings-schemas/${schema.name}"; in " export XDG_DATA_DIRS=${datadir}:$XDG_DATA_DIRS gsettings set org.gnome.desktop.interface gtk-theme 'Tokyonight-Dark-B' gsettings set org.gnome.desktop.interface icon-theme 'Tokyonight-Dark' gsettings set org.gnome.desktop.interface cursor-theme 'Vimix Cursors' gsettings set org.gnome.desktop.interface font-name 'UbuntuMono Nerd Font 12' gsettings set org.gnome.desktop.wm.preferences button-layout ':appmenu' "; } ================================================ FILE: packages/default.nix ================================================ { pkgs, inputs', self', ... }: { talhelper = inputs'.talhelper.packages.default; configure-gtk = pkgs.callPackage ./configure-gtk/default.nix { }; tokyonight-gtk-theme = pkgs.callPackage ./tokyonight-gtk-theme/default.nix { }; tokyonight-icon-theme = pkgs.callPackage ./tokyonight-icon-theme/default.nix { }; nvim-plugins = pkgs.callPackage ./nvim-plugins/default.nix { }; fish-plugins = pkgs.fishPlugins.callPackage ./fish-plugins/default.nix { }; # krr = pkgs.callPackage ./krr/default.nix { }; kubectl-rook-ceph = pkgs.callPackage ./kubectl-rook-ceph/default.nix { }; neovim = inputs'.nixvim.legacyPackages.makeNixvimWithModule { # make nixvim use the same pkgs with my overlays added inherit pkgs; extraSpecialArgs = { myPkgs = self'.legacyPackages; }; module.imports = [ ../home/budiman/config/neovim ]; }; } ================================================ FILE: packages/fish-plugins/default.nix ================================================ { callPackage, buildFishPlugin }: let sourceData = callPackage ../_sources/generated.nix { }; in { abbreviation-tips = buildFishPlugin { inherit (sourceData.abbreviation-tips) pname src version; }; tmux-fish = buildFishPlugin { inherit (sourceData.tmux-fish) pname src; version = sourceData.tmux-fish.date; }; fish-completion-sync = buildFishPlugin { inherit (sourceData.fish-completion-sync) pname src version; }; } ================================================ FILE: packages/krr/about-time.nix ================================================ { lib, python3, callPackage, ... }: let sourceData = callPackage ../_sources/generated.nix { }; in python3.pkgs.buildPythonPackage { inherit (sourceData.about-time) pname version src; doCheck = false; meta = with lib; { description = "Easily measure timing and throughput of code blocks, with beautiful human friendly representations"; homepage = "https://github.com/rsalmei/about-time"; licence = licences.mit; }; } ================================================ FILE: packages/krr/alive-progress.nix ================================================ { lib, python3, pkgs, callPackage, ... }: let sourceData = callPackage ../_sources/generated.nix { }; about-time = callPackage ./about-time.nix { }; in python3.pkgs.buildPythonPackage { inherit (sourceData.alive-progress) pname version src; doCheck = false; propagatedBuildInputs = with pkgs.python3Packages; [ about-time grapheme ]; meta = with lib; { description = "A new kind of Progress Bar, with real-time throughput, ETA, and very cool animations!"; homepage = "https://github.com/rsalmei/alive-progress"; licence = licences.mit; }; } ================================================ FILE: packages/krr/default.nix ================================================ { lib, python3, callPackage, pkgs, }: let sourceData = callPackage ../_sources/generated.nix { }; prometheus-api-client = callPackage ./prometheus-api-client.nix { }; alive-progress = callPackage ./alive-progress.nix { }; in python3.pkgs.buildPythonPackage { inherit (sourceData.krr) pname version src; format = "pyproject"; postPatch = '' substituteInPlace pyproject.toml \ --replace "pydantic = \"1.10.7\"" "pydantic = \">=1.10.7\"" \ --replace "typer = {extras = [\"all\"], version = \"^0.7.0\"}" "typer = {extras = [\"all\"], version = \">=0.7.0\"}" ''; propagatedBuildInputs = with pkgs.python3Packages; [ alive-progress cachetools certifi charset-normalizer click colorama commonmark contourpy cycler dateparser fonttools google-auth httmock idna kiwisolver kubernetes matplotlib numpy oauthlib packaging pandas pillow prometheus-api-client poetry-core pyasn1-modules pyasn1 pydantic pygments pyparsing python-dateutil pytz-deprecation-shim pytz pyyaml regex requests-oauthlib requests rich rsa setuptools shellingham six typer typing-extensions tzdata tzlocal urllib3 websocket-client ]; meta = with lib; { description = "Prometheus-based Kubernetes Resource Recommendations"; homepage = "https://github.com/robusta-dev/krr"; licence = licences.mit; }; } ================================================ FILE: packages/krr/prometheus-api-client.nix ================================================ { lib, python3, pkgs, callPackage, ... }: let sourceData = callPackage ../_sources/generated.nix { }; in python3.pkgs.buildPythonPackage { inherit (sourceData.prometheus-api-client) pname version src; doCheck = false; propagatedBuildInputs = with pkgs.python3Packages; [ matplotlib numpy pandas requests dateparser httmock ]; meta = with lib; { description = "A python wrapper for the prometheus http api"; homepage = "https://github.com/4n4nd/prometheus-api-client-python"; licence = licences.mit; }; } ================================================ FILE: packages/kubectl-rook-ceph/default.nix ================================================ { buildGoModule, fetchFromGitHub, installShellFiles, lib, }: buildGoModule rec { pname = "kubectl-rook-ceph"; version = "v0.5.2"; src = fetchFromGitHub { owner = "rook"; repo = pname; rev = version; sha256 = "sha256-fRkC1rr+jFZ6xp1aU1vxqLqW1OTeZgyJPwC+FIeKFcc="; }; vendorHash = "sha256-D1k4+1PsBMtGwYVDacrLOSKUhWLOIY/KIooIt7Qo/QE="; nativeBuildInputs = [ installShellFiles ]; subPackages = [ "cmd" ]; postInstall = '' mv $out/bin/cmd $out/bin/kubectl-rook_ceph # Shell completion for `kubectl rook-ceph` # Quite ugly but it works, see: https://github.com/kubernetes/kubernetes/pull/105867 cat <$out/bin/kubectl_complete-rook_ceph #!/usr/bin/env sh kubectl rook-ceph __complete "\$@" EOF chmod u+x $out/bin/kubectl_complete-rook_ceph # This is the more elegant way of doing it but it doesn't work # for shell in bash fish zsh; do # $out/bin/kubectl-rook_ceph completion $shell > kubectl-rook_ceph.$shell # installShellCompletion kubectl-rook_ceph.$shell # done ''; meta = with lib; { description = "kubectl plugin to run kubectl commands with rook-ceph"; homepage = "https://github.com/rook/kubectl-rook-ceph"; licence = licences.asl20; }; } ================================================ FILE: packages/nvfetcher.toml ================================================ [tokyonight-gtk-theme] src.git = "https://github.com/Fausto-Korpsvart/Tokyo-Night-GTK-Theme" fetch.github = "Fausto-Korpsvart/Tokyo-Night-GTK-Theme" [tokyonight-icon-theme] src.git = "https://github.com/Fausto-Korpsvart/Tokyo-Night-GTK-Theme" fetch.github = "Fausto-Korpsvart/Tokyo-Night-GTK-Theme" [chezmoi-vim] src.git = "https://github.com/alker0/chezmoi.vim" fetch.github = "alker0/chezmoi.vim" [mason-lspconfig-nvim] src.git = "https://github.com/williamboman/mason-lspconfig.nvim" fetch.github = "williamboman/mason-lspconfig.nvim" [mason-tool-installer-nvim] src.git = "https://github.com/WhoIsSethDaniel/mason-tool-installer.nvim" fetch.github = "WhoIsSethDaniel/mason-tool-installer.nvim" [oil-nvim] src.git = "https://github.com/stevearc/oil.nvim" fetch.github = "stevearc/oil.nvim" [luasnip] src.github = "L3MON4D3/LuaSnip" fetch.github = "L3MON4D3/LuaSnip" [k8s-snippets] src.git = "https://github.com/budimanjojo/k8s-snippets" fetch.github = "budimanjojo/k8s-snippets" [guihua-lua] src.git = "https://github.com/ray-x/guihua.lua" fetch.github = "ray-x/guihua.lua" [abbreviation-tips] src.github_tag = "gazorby/fish-abbreviation-tips" fetch.github = "gazorby/fish-abbreviation-tips" [tmux-fish] src.git = "https://github.com/budimanjojo/tmux.fish" fetch.github = "budimanjojo/tmux.fish" [fish-completion-sync] src.git = "https://github.com/iynaix/fish-completion-sync" fetch.github = "iynaix/fish-completion-sync" ================================================ FILE: packages/nvim-plugins/default.nix ================================================ { fetchFromGitHub, pkgs, callPackage, }: let sourceData = callPackage ../_sources/generated.nix { }; in { chezmoi-vim = pkgs.vimUtils.buildVimPlugin { inherit (sourceData.chezmoi-vim) pname src; version = sourceData.chezmoi-vim.date; }; mason-lspconfig-nvim = pkgs.vimUtils.buildVimPlugin { inherit (sourceData.mason-lspconfig-nvim) pname src; version = sourceData.mason-lspconfig-nvim.date; }; mason-tool-installer-nvim = pkgs.vimUtils.buildVimPlugin { inherit (sourceData.mason-tool-installer-nvim) pname src; version = sourceData.mason-tool-installer-nvim.date; }; oil-nvim = pkgs.vimUtils.buildVimPlugin { inherit (sourceData.oil-nvim) pname src; version = sourceData.oil-nvim.date; }; luasnip = pkgs.vimUtils.buildVimPlugin { inherit (sourceData.luasnip) pname src version; }; k8s-snippets = pkgs.vimUtils.buildVimPlugin { inherit (sourceData.k8s-snippets) pname src; version = sourceData.k8s-snippets.date; }; guihua-lua = pkgs.vimUtils.buildVimPlugin { inherit (sourceData.guihua-lua) pname src; version = sourceData.guihua-lua.date; buildPhase = '' ( cd lua/fzy make ) ''; # TODO: check failing and Idk why nvimSkipModules = [ "fzy.fzy-lua-native" ]; }; } ================================================ FILE: packages/tokyonight-gtk-theme/default.nix ================================================ { stdenvNoCC, lib, callPackage, pkgs, }: let sourceData = callPackage ../_sources/generated.nix { }; in stdenvNoCC.mkDerivation { inherit (sourceData.tokyonight-gtk-theme) pname version src; nativeBuildInputs = [ pkgs.gnome-shell pkgs.sassc ]; builtInputs = [ pkgs.gnome-themes-extra ]; dontBuild = true; postPatch = '' patchShebangs themes/install.sh ''; installPhase = '' runHook preInstall mkdir -p $out/share/themes cd themes ./install.sh --dest $out/share/themes --name Tokyonight runHook postInstall ''; meta = with lib; { description = "A GTK theme based on the Tokyo Night colour palette"; longDescription = '' A GTK theme based on the colours of Folke's great theme: Tokyonight for Neovim, the VinceLiuice's awesome: Magnetic GTK theme and the creativity of Gusbemacbe's: Suru Plus Icon Theme. Great to combine in your Gnome Desktop Environment and TWMs like: XmonadWM, AwesomeWM, BSPWM, etc... With support also for the desktop environments Cinnamon and XFCE. ''; homepage = "https://github.com/Fausto-Korpsvart/Tokyo-Night-GTK-Theme"; license = licenses.gpl3Only; }; } ================================================ FILE: packages/tokyonight-icon-theme/default.nix ================================================ { stdenvNoCC, hicolor-icon-theme, gtk3, lib, callPackage, }: let sourceData = callPackage ../_sources/generated.nix { }; in stdenvNoCC.mkDerivation { inherit (sourceData.tokyonight-icon-theme) pname version src; nativeBuildInputs = [ gtk3 ]; propagatedBuildInputs = [ hicolor-icon-theme ]; dontDropIconThemeCache = true; # These fixup steps are slow and unnecessary for this package dontPatchELF = true; dontRewriteSymlinks = true; installPhase = '' runHook preInstall mkdir -p $out/share/icons cp -r ./icons/Tokyonight-* $out/share/icons/ for theme in $out/share/icons/*; do gtk-update-icon-cache $theme done runHook postInstall ''; meta = with lib; { description = "A GTK theme based on the Tokyo Night colour palette"; longDescription = '' Tokyonight is a GTK theme based on the colour palette of the Tokyonight for Neovim by @Folke, the Graphite GTK theme by @VinceLiuice and the Suru Plus icons by @gusbemacbe. The idea was born from the need for GTK themes that match the most prominent colour palettes of Neovim code editor and Tiling Window Manager, such as Xmonad, Awesome, DWM, etc, which use these colour schemes to give a uniform and unique look to working environments. See on Reddit: r/unixporn. The colour palettes in this series of themes are the ones I have used the most in my setup for Neovim, Xmonad and Gnome DE, so creating themes started as something personal that I then decided to share thanks to several people asking me to share them because they seemed good, I hope you find them useful and make your desktops look good too. ''; homepage = "https://github.com/Fausto-Korpsvart/Tokyo-Night-GTK-Theme"; license = licenses.gpl3Only; }; } ================================================ FILE: shell.nix ================================================ { pkgs, ... }: with pkgs; mkShell { buildInputs = [ pkgs.just ]; } ================================================ FILE: system/_modules/_default/default.nix ================================================ { config, hostname, pkgs, lib, ... }: let mySystem = config.mySystem; in { imports = [ ./nix.nix ./sops.nix ./users.nix ]; config = { networking.hostName = hostname; time.timeZone = "Asia/Jakarta"; security = { sudo.wheelNeedsPassword = false; polkit.enable = true; }; services.displayManager.autoLogin.user = mySystem.adminUser; catppuccin = { flavor = "mocha"; accent = "mauve"; }; # do not change unless you know what you are doing system.stateVersion = "23.11"; documentation.nixos.enable = false; }; } ================================================ FILE: system/_modules/_default/nix.nix ================================================ { inputs, hostname, ... }: { nix = { # make `nix run` and `nix shell` use the same nixpkgs as the one used by this flake # for example, to run `talosctl` from the `unstable` branch, we can run `nix shell unstable#talosctl` registry = { stable.flake = inputs.nixpkgs; unstable.flake = inputs.nixpkgs-unstable; }; channel.enable = false; # remove nix-channel related tools & configs, we use flakes instead settings = { # NIX_PATH is still used by many useful tools, so we set it to the same value as the one used by this flake # make `nix repl ''` use the same nixpkgs as the one used by this flake nix-path = "nixpkgs=${inputs.nixpkgs.outPath}"; experimental-features = [ "nix-command" "flakes" ]; substituters = [ "https://viperml.cachix.org" "https://budimanjojo.cachix.org" ]; trusted-public-keys = [ "viperml.cachix.org-1:qZhKBMTfmcLL+OG6fj/hzsMEedgKvZVFRRAhq7j8Vh8=" "budimanjojo.cachix.org-1:S0gy6IKTFXis9fFqEbVAS2zsvnZw/30NV2bWvGiN1YQ=" ]; auto-optimise-store = true; keep-outputs = true; keep-derivations = false; # this make sure we always check for new commit when fetching source tarball-ttl = 0; trusted-users = [ "@wheel" ]; }; }; } ================================================ FILE: system/_modules/_default/secret.sops.yaml ================================================ adminPassword: ENC[AES256_GCM,data:sjzzXiIU7VbV9kc+f4npI90SFIfY0jAww4j414ZtfAUzwURgr3x0nA3+mou7MGeLKW3ZEl0qs3bEVUxU9mYm137Cyu4HP1wprw==,iv:wXqhbAtvoo7nnH8PKzqWLbEArmPa800Vh00guYb7yuU=,tag:Vsxe8B4CCXn+/5iSNWEHew==,type:str] sops: kms: [] gcp_kms: [] azure_kv: [] hc_vault: [] age: - recipient: age1zeqkpfz7e3s207ynea0z0auc0mrct0pc7w4sh6j3d0c4qac3dahqj9ufdg enc: | -----BEGIN AGE ENCRYPTED FILE----- YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBvMWdiczk5OTlFVEs0Z3Mr UEFvNkhxNDVYVUtCUTJzOTNIakk0M25xZERrCkkxQ1RuYnQ1TDRQRmVEbjVMcTBX OStkb2N2cTRVLzNsMUdHWU8zYUNtV0UKLS0tIEJpWTBrS1lJZ2U4cEdxM3h1N0Rr ZEE4OENBSGo5b280RTQyZTVuVUppWVEKwhWnxqdvHNpEVOc5Wy9kzRcdL7NlIdqp dyQOdgNIRIpv6hg7TOtW5wzMI1FZpZXVil0JE0MIvwUk/oDpAFf3Aw== -----END AGE ENCRYPTED FILE----- - recipient: age1tdwlq9y4jgejkhasqwynw5uaen9xwatcvr52l70trsdxkeyvlesqjnh7l8 enc: | -----BEGIN AGE ENCRYPTED FILE----- YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAzSzNSRzMvRUFRMDE5OVR5 NlR6c2ZYdlk0RmgzL3ZNNFpJZk1RaVFHZFRjCjBMZlh3ZXhKN1lOczM3WGVIUDFD cnhZR3Noc1loQ1NQZGVjaGY3VG1jWlEKLS0tIEVWcUwrTkJWVzBYR2ZnbTVkOWo3 MFNZUTBkS3J3MGNxR0Q3MlVyOVhHVG8KKJQE1nBZeQCktuEYOMDZVe4ybce7b9g/ Qkv23Qtdh5TLIeRiTxSTqktHOHLWKTT4V+Lsf5s9TylL4oYXJ0EngA== -----END AGE ENCRYPTED FILE----- - recipient: age16p3zls5n0jks6amszwcuaqgl5dyuyf8k8wgeyrw562s5s88xtq3qq046fh enc: | -----BEGIN AGE ENCRYPTED FILE----- YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBkZEs4UWJpQXdGbFE0MTQ4 VmZ4T1VHV3VPUkhzcERhbDh5MXlObVJJRTNRClVodlc0TW15cGUvdTA5TGpOZkI0 UzdwOFpBb1VORFJhTUpWRi8wWmppUjQKLS0tIHdxYmFpREtnc2c3cC9MVXhNTXBM d2ZBSVRxTG9icEh3eXh1M1hpR3dKK0EKc7jzKMYIlIobeoeB1JW0M4Jm0pGB/5MV +2RU0tTK6fcgB7lP+E53HG5tG4pauPm87D7g046OxG9AVsEI+f0sBQ== -----END AGE ENCRYPTED FILE----- - recipient: age1v52mx8gs4ephprep0wcc4j3fvvprppvs9vktf2p24yv52sqsf33sd5crk9 enc: | -----BEGIN AGE ENCRYPTED FILE----- YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBlbXJVcWRqa0ROQXNQdG5t aUVEQ0hLbUxsWEg5dWNOZEtxbG90cmMxdVYwCkpoeWVrM2EvbHJuMGFHcFA0MEs0 RWFQWHhFaXdycHZoUzkyeDBFaTRwbm8KLS0tIHNUR2YvS0Q4YkRkOFpSelQ3RkQ0 MlNyMUtpQmk1ekllVTRwMm9sLzlOUGcKcS8D8ni7aiM79ahgr2WZF8wxU5hcX7y3 Jm2XIHfuiaCAptgwr0lsHDfV1iF89l1zK3IUsNvBajmJFNsNPcMBWg== -----END AGE ENCRYPTED FILE----- - recipient: age1k8ufac2s0gs6nh0xsfavafz062vd36petmyv6nwmg00z4a7s4gnsjtd837 enc: | -----BEGIN AGE ENCRYPTED FILE----- YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBXRFRDTk5vTEh6Rm9EWkl1 alYzcmYrazRsOG9hYXJoaWZTUkh0b0N4OHpJCnRIWktFNUVOZFV5L0Q3bFBhemZm UkdESk15YXFJYzJ0cWtObGRzZzJDWkEKLS0tIFBHd2lLNWhTZ1FZWGttWlR4OC9i Tk1PeG1jS3MwRk5jY0YvRFlkMU84T1EKFIzjYAKlUKfLZJNCN+skxOLwU1316dBB RicAuX1l5UTBIbeZGdIG+QdckxC+NoKZ91tZyRNRseVteN7nORgQow== -----END AGE ENCRYPTED FILE----- - recipient: age1dcsm5awz8ekzchk7gsvndkc4kq65n2wzgavxtqe53vxdsfk9k9pqh3whru enc: | -----BEGIN AGE ENCRYPTED FILE----- YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBFcnhVcW5iZ3dGaGFHUlF3 MFpBT3p6Z04rK1hkeWJPOUU3bjlxN0plUkg4Ck8wSlp4YUxCZjhaTDhaekU3NHRP K0psb3ZjZ21nZEwvWmpzK1FGVWRIVUkKLS0tIDZZWUhsTjRkL1V3ZFBrTjV0ckZo WDlhSXphRmFxM2kzWWU0MDBrc2tVdUEKOhJOI+1BfBOkh3HIcAM19d9jp6e7JkEG DR5lZej75bO5R7mvJ2v7rwj074cbWywRiC2kbEo4Cp1CHREsgktjCQ== -----END AGE ENCRYPTED FILE----- lastmodified: "2024-07-07T04:54:05Z" mac: ENC[AES256_GCM,data:9lMToM2IcNVqgaLL+ww+6ZnmuEr0Ve8NCuzevpuli1MhkLOEq/rFw2w6BPvlQYspRha7ovu9mJJsnRxPHIPAJV0lNjmj4oOCliewPtCWDN+tVyYbMwLpcN6CNvwm1/cH5IXeDG2lMzbYDoCcmyavJp9gZclcN6esy0FeSxJnUK0=,iv:9W3NU1DB6BnJhhGplxMY38zmHHVd1sjWQf4qv6vLr94=,tag:zU1oQmYU8UDnb3P+TwJGiA==,type:str] pgp: [] unencrypted_suffix: _unencrypted version: 3.8.1 ================================================ FILE: system/_modules/_default/sops.nix ================================================ { config, pkgs, ... }: let mySystem = config.mySystem; in { config = { environment.systemPackages = [ pkgs.sops pkgs.age ]; sops.age = { keyFile = "${config.users.users.${mySystem.adminUser}.home}/.config/sops/age/keys.txt"; generateKey = true; # generate the key if it doesn't exist }; }; } ================================================ FILE: system/_modules/_default/users.nix ================================================ { config, pkgs, ... }: { sops.secrets = { adminPassword = { sopsFile = ./secret.sops.yaml; neededForUsers = true; }; }; users = { mutableUsers = false; users.${config.mySystem.adminUser} = { isNormalUser = true; uid = 1000; extraGroups = [ "wheel" ]; shell = pkgs.fish; hashedPasswordFile = config.sops.secrets.adminPassword.path; }; }; programs.fish.enable = true; } ================================================ FILE: system/_modules/containers/beeaccounting/default.nix ================================================ { lib, config, ... }: with lib; let mySystem = config.mySystem; cfg = mySystem.containers.beeaccounting; ghcr-login = { username = "budimanjojo"; registry = "ghcr.io"; passwordFile = config.sops.secrets.budimanjojo-ghcr-pull-token.path; }; userCfg = config.users.users.${mySystem.adminUser}; in { options.mySystem.containers.beeaccounting = { enable = mkEnableOption "Beeaccounting"; }; config = mkIf cfg.enable { sops.secrets.budimanjojo-ghcr-pull-token.sopsFile = ./secret.sops.yaml; networking.firewall.allowedTCPPorts = [ 5432 631 5353 ]; users.users.${mySystem.adminUser}.extraGroups = [ "docker" ]; virtualisation = { oci-containers = { ## because https://github.com/NixOS/nixpkgs/issues/259770 backend = "docker"; containers = { beeaccounting-db = { image = "ghcr.io/budimanjojo/beeaccounting/beeplat-database:v1.1.1"; autoStart = true; login = ghcr-login; ports = [ "5432:5432" ]; volumes = [ "beeaccounting-db:/data" ]; }; beeaccounting-app = { image = "ghcr.io/budimanjojo/beeaccounting/beeplat-client:v1.1.1"; autoStart = true; login = ghcr-login; extraOptions = [ "--device=/dev/bus/usb" "--ulimit" "nofile=1024:524288" ]; ports = [ "631:631" "5353:5353/udp" ]; volumes = [ "/tmp/.X11-unix:/tmp/.X11-unix" "beeaccounting-app:/app" "beeaccounting-cups:/etc/cups" "/home/${mySystem.adminUser}/OpenCloud:/opencloud" ]; environment = { ## There's no good way to get $DISPLAY from the host and it's unpure DISPLAY = ":0"; TZ = "Asia/Jakarta"; _JAVA_AWT_WM_NONREPARENTING = "1"; PUID = "${toString userCfg.uid}"; PGID = "${toString config.users.groups.${userCfg.group}.gid}"; TYPE = "server"; }; }; }; }; }; }; } ================================================ FILE: system/_modules/containers/beeaccounting/secret.sops.yaml ================================================ budimanjojo-ghcr-pull-token: ENC[AES256_GCM,data:skSIMsRrCX2qS+ATi6Aip3H8l2ySlipp+JArEmeOWuVbarGVKo/7dQ==,iv:GS7XIYLzttfTiEIrlkE5c6LR5GRkKaxI94VY+SXgNfU=,tag:5vbWMWVeZP5J/rh5qca/EA==,type:str] sops: kms: [] gcp_kms: [] azure_kv: [] hc_vault: [] age: - recipient: age1zeqkpfz7e3s207ynea0z0auc0mrct0pc7w4sh6j3d0c4qac3dahqj9ufdg enc: | -----BEGIN AGE ENCRYPTED FILE----- YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBidWRWZTNZcXlHSllwWmI0 alpVOHB3Y0U5ajJOMFg3ZUFZTnVnQy9TQlNRCitOZXhCK291UE9RNDNpKzdsaHBu L2RFQjgrMWNrMHgzRldTdnRTY1Q3OGMKLS0tIDU0MCtVZFhDdFNNRk1VMVdlOXRO S3g1TWNXTDlWU1orblNyTU9iMzJFdkUKA4mY34QeWtdkApVSyEvLGs1UM5ALcHvz G8T82c92ml+EJ40zPAv/eOB6lsQt0KyWwIC2UuoaEr/ruJVfC/qeMQ== -----END AGE ENCRYPTED FILE----- - recipient: age1tdwlq9y4jgejkhasqwynw5uaen9xwatcvr52l70trsdxkeyvlesqjnh7l8 enc: | -----BEGIN AGE ENCRYPTED FILE----- YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB1WWdsbThVTTJkMHpFSnRk K1JWQ1lGNnk1eVNDSG50RklIdFc0dTBPZ3pFCmhDVEhueEk4SVRkU2xJT0dmNkZC RFhZbm5SY0ZONlRCbytXc2xOV0pNUlUKLS0tIHBBZXZFeG9PVmxMNlFDRWZHMTNB aDlYYUUwVnFJYk5mbEZPOVJuWVpFbW8Kbx65BPMDUOlg9HAsa4cra8nHfv+6rGNK rSCKxvoj3KmBASeeCT5PnHEMJlA3lZAKINScRrCd0NOjuykhbYZvtA== -----END AGE ENCRYPTED FILE----- - recipient: age16p3zls5n0jks6amszwcuaqgl5dyuyf8k8wgeyrw562s5s88xtq3qq046fh enc: | -----BEGIN AGE ENCRYPTED FILE----- YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB1V3hITGIrYlZlZk15dlYz V2RLRFdVTkFabUV0QmpZOWZzcWNZdmUxMnpzCmd2U1REUVJCZWdrNjBGbGJGT2Rm Y09VSk1MTGpzcHFoelJMZ0FyR3U3R28KLS0tIGhGdjFmVS8yYmZsWXdLZjdWYWdH RnZGOGZFT1psZUNiVEMveXc2WUFGMGsKDnf2V+KKrPG+uAPNMDz8YW/HgdbCNoT8 tojEN0ww1gF7EuVpXfZASQRzgAMqpl6f0CtKVbB/VfjezBqF287npg== -----END AGE ENCRYPTED FILE----- - recipient: age1v52mx8gs4ephprep0wcc4j3fvvprppvs9vktf2p24yv52sqsf33sd5crk9 enc: | -----BEGIN AGE ENCRYPTED FILE----- YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBZejNoaUFjcGJCRGVuTWR3 ZG9ZeDBPS3JQa0srdWVNQmpBd2owY1IzYzBzCkpJY0hCajhTS2IzbkNVZ24zbnln ODNiL3BuQUJqalZSNEoxL3RwUjZYVzgKLS0tIHlZMko5eFFGbERTRFk4b2lvV3A4 QzA3L1JTdUE1RVYvUnJMMnl3eWpabVkK4i57i7wlFKDGGlA+LxKreRI7F5d2UVjA wWjR9CkJonzjKs2gYWK+sCaansJ0fWJrIcXxq1rHcpmpbQ7fyqMSEg== -----END AGE ENCRYPTED FILE----- - recipient: age1k8ufac2s0gs6nh0xsfavafz062vd36petmyv6nwmg00z4a7s4gnsjtd837 enc: | -----BEGIN AGE ENCRYPTED FILE----- YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBoN0RvandwNExpOG9IRXRw RW9Oc3AwQ3BYM1VENzdvcE9jUzlLS1lkemc0Cmp2T1ZmYmMrV1ZuSElGdldVbmlG Z2x0ZGduN0tuUnhQL2pFTlAzTW1zdTgKLS0tIDdQQzNGb1h2ck1kVytjRzU0Wngr YVp4Ym5xb0pmcWlkUjlPMDdGaXh4R0kKQ3wCMwIFb1nEsT4iFiFEmYs4KHwd2vxq e8p/+0x9FG+xDxig3nqOS4ybYrbpv5fFj6bWyNOEhzWiE1VKEaOKUg== -----END AGE ENCRYPTED FILE----- - recipient: age1dcsm5awz8ekzchk7gsvndkc4kq65n2wzgavxtqe53vxdsfk9k9pqh3whru enc: | -----BEGIN AGE ENCRYPTED FILE----- YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAzTDZENlVqSm1GeitqamdV Nmd6QXBlTEFMMkRWU0VBeU14RDdpKzYwT1FzCkl1VGxwd1Q4QTNRSHJHbHZDbWQz SXlZWmYrTjNxd2lSRzFQVG8xQXpGanMKLS0tIHRRZi85V1lrUnZuZlpCS29QRWNS dWF5QWVVTmZiWkVwVVhadTN5WkJMSWcKh206JMuZtMm5yNH+bHC1PggmgODgJZHy WaICxUv6QEF4Ye6EKSVDL1pmby/jtieebG1zFzmYnYe8coLPNB7QUg== -----END AGE ENCRYPTED FILE----- lastmodified: "2024-07-06T08:59:54Z" mac: ENC[AES256_GCM,data:bcs6qSknLX7TOJH74y10vC9trINHYaZ57H6OFrFyI+la8/ChzXwv9Hin9G4ynbuL7OHsRL1BBAiTiwFGL6wSt54ryaz1gkeltHXbXBc++qAWIM4YXoHKP2AU6zHOghJqswcEiedHxSCapX59eRylddx/49bWPmXjIgQn8dqMUVI=,iv:kaVtvyjAtFu80IV4Gdu/IETPEZzOWVEn+TABmM+P/Q4=,tag:42iHTAfSFQjodznjXQc8+w==,type:str] pgp: [] unencrypted_suffix: _unencrypted version: 3.8.1 ================================================ FILE: system/_modules/default.nix ================================================ { lib, ... }: { imports = [ # contains config.myHardware options # in a separate file because this module is shared with home-manager ./myHardware.nix # base module enabled for all hosts ./_default ./containers/beeaccounting ./displaymanager/sddm ./monitoring/node-exporter ./monitoring/smartctl-exporter ./programs/adb ./programs/hugo ./programs/msmtp ./programs/nh ./programs/qmk ./services/btrfs-autoscrub ./services/grafana ./services/openssh ./services/prometheus ./services/restic-backup ./system/autoupgrade ./system/bootloader ./system/cpu ./system/font ./system/sound ./system/video ./windowmanager/add-on/blueman ./windowmanager/add-on/gnome-keyring ./windowmanager/add-on/networkmanager ./windowmanager/add-on/polkit-gnome ./windowmanager/add-on/thunar ./windowmanager/hyprland ./windowmanager/i3 ./windowmanager/sway ]; options.mySystem = with lib; { adminUser = mkOption { type = types.str; default = ""; }; isWayland = mkOption { type = types.bool; default = false; }; }; } ================================================ FILE: system/_modules/displaymanager/sddm/default.nix ================================================ { config, pkgs, lib, options, ... }: let mySystem = config.mySystem; myHardware = config.myHardware; cfg = mySystem.displaymanager.sddm; inherit (lib) mkEnableOption mkOption types mkIf concatMapStrings ; in { options.mySystem.displaymanager.sddm = { enable = mkEnableOption "SDDM display manager"; wallpaper = mkOption { type = types.nullOr types.path; default = null; }; defaultSession = mkOption { type = options.services.displayManager.defaultSession.type; default = options.services.displayManager.defaultSession.default; }; }; config = mkIf (cfg.enable) { catppuccin.sddm = { enable = true; background = cfg.wallpaper; loginBackground = true; }; services = { displayManager = { sddm = { enable = true; wayland.enable = mySystem.isWayland; package = pkgs.kdePackages.sddm; }; defaultSession = cfg.defaultSession; }; # turn off non primary monitors on X11 because sddm weirdly displays prompt on all screens xserver.displayManager.setupCommands = mkIf (!mySystem.isWayland) ( "${pkgs.xorg.xrandr}/bin/xrandr" + (concatMapStrings ( mon: " --output ${mon.xname}" + ( if (mon.primary) then " --pos ${toString mon.x}x${toString mon.y} --mode ${toString mon.width}x${toString mon.height}" else " --off" ) ) myHardware.monitors) ); }; }; } ================================================ FILE: system/_modules/monitoring/node-exporter/default.nix ================================================ { config, lib, ... }: let cfg = config.mySystem.monitoring.node-exporter; inherit (lib) mkEnableOption mkIf; in { options.mySystem.monitoring.node-exporter = { enable = mkEnableOption "Prometheus node exporter"; }; config = mkIf (cfg.enable) { services = { prometheus.exporters.node = { enable = true; enabledCollectors = [ "systemd" ]; openFirewall = true; }; }; }; } ================================================ FILE: system/_modules/monitoring/smartctl-exporter/default.nix ================================================ { config, lib, ... }: let cfg = config.mySystem.monitoring.smartctl-exporter; inherit (lib) mkEnableOption mkIf; in { options.mySystem.monitoring.smartctl-exporter = { enable = mkEnableOption "Prometheus smartctl exporter"; }; config = mkIf (cfg.enable) { services = { prometheus.exporters.smartctl = { enable = true; openFirewall = true; }; # `/dev/nvme*` devices are not in `disk` group by default so `smartctl-exporter` won't work on them # workaround until https://github.com/NixOS/nixpkgs/pull/205165/commits/36256aeaa2fded21257127a9c1d17d6299ef9395 is merged udev.extraRules = '' SUBSYSTEM=="nvme", KERNEL=="nvme[0-9]*", GROUP="disk" ''; }; }; } ================================================ FILE: system/_modules/myHardware.nix ================================================ { lib, config, ... }: let inherit (lib) mkOption types; in { options.myHardware = { cpu = mkOption { type = types.nullOr ( types.enum [ "amd" "intel" ] ); default = null; }; gpuDriver = mkOption { type = types.nullOr ( types.enum [ "amd" "intel" "nvidia" "nouveau" ] ); default = null; }; isUEFI = mkOption { type = types.bool; default = true; }; monitors = mkOption { type = types.listOf ( types.submodule ( { config, ... }: { options = { name = mkOption { type = types.str; }; xname = mkOption { type = types.str; default = config.name; description = '' Name of the monitor on X11 session. `xrandr` doesn't use the name reported in `/sys/class/drm`, it's also different depending on graphic driver. ''; }; primary = mkOption { type = types.bool; default = false; }; width = mkOption { type = types.int; default = 1920; }; height = mkOption { type = types.int; default = 1080; }; x = mkOption { type = types.int; default = 0; }; y = mkOption { type = types.int; default = 0; }; wallpaper = mkOption { type = types.nullOr types.path; default = null; }; workspaces = mkOption { type = types.listOf types.int; default = [ ]; }; }; } ) ); default = [ ]; }; }; config = { assertions = [ ( let monitors = config.myHardware.monitors; primary = builtins.filter (m: m.primary) monitors; in { assertion = monitors == [ ] || builtins.length primary == 1; message = "Must have exactly one primary monitor in `config.myHardware.monitors` but found " + toString (builtins.length primary); } ) ]; }; } ================================================ FILE: system/_modules/programs/adb/default.nix ================================================ { config, lib, ... }: let cfg = config.mySystem.programs.adb; mySystem = config.mySystem; in { options.mySystem.programs.adb = { enable = lib.mkEnableOption "adb"; }; config = lib.mkIf (cfg.enable) { programs.adb.enable = true; users.users.${mySystem.adminUser}.extraGroups = [ "adbusers" ]; }; } ================================================ FILE: system/_modules/programs/hugo/default.nix ================================================ { config, lib, ... }: let cfg = config.mySystem.programs.hugo; in { options.mySystem.programs.hugo = { enable = lib.mkEnableOption "Hugo"; }; config = lib.mkIf cfg.enable { networking.firewall.allowedTCPPorts = [ 1313 ]; }; } ================================================ FILE: system/_modules/programs/msmtp/default.nix ================================================ { config, lib, pkgs, ... }: let mySystem = config.mySystem; cfg = mySystem.programs.msmtp; in { options.mySystem.programs.msmtp = { enable = lib.mkEnableOption "msmtp"; }; config = lib.mkIf (cfg.enable) { sops.secrets.gmail-password = { sopsFile = ./secret.sops.yaml; owner = "${mySystem.adminUser}"; group = "${config.users.users.${mySystem.adminUser}.group}"; }; programs.msmtp = { enable = true; accounts = { gmail = { auth = true; host = "smtp.gmail.com"; port = 587; tls = true; tls_starttls = true; from = "budimanjojo@gmail.com"; user = "budimanjojo"; passwordeval = "${pkgs.coreutils}/bin/cat ${config.sops.secrets.gmail-password.path}"; }; }; extraConfig = '' account default: gmail ''; }; }; } ================================================ FILE: system/_modules/programs/msmtp/secret.sops.yaml ================================================ gmail-password: ENC[AES256_GCM,data:CUz7IBEHhXfOanT9aSO5Gw==,iv:/r2hzqsEaqGBtnlzIXUk+ghNEm+0Hx1dRa5RFWqJbNQ=,tag:DcTF0ClRmy3so1lnJk6geA==,type:str] sops: kms: [] gcp_kms: [] azure_kv: [] hc_vault: [] age: - recipient: age1zeqkpfz7e3s207ynea0z0auc0mrct0pc7w4sh6j3d0c4qac3dahqj9ufdg enc: | -----BEGIN AGE ENCRYPTED FILE----- YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBxMkZyeUJ5bFNHVE1Fd3Zp cGwzSHRMeUtiZGU4VzUySFptY3V5RGpJajFFCnJZZFpERFBMeDI3NkpScTVsenJl clNlVWJnQXhQYWUwR1BiSEM0ZWI3aEUKLS0tIDBxL2lYaU9UMyt5ZkhieDFqU2Uv TlBRS0hRbWZFN3pCWHI0Y1IxL0k0Nm8Km0YYRz6cccUooE26iB7zmZTs3r/exCTf Lb/bIRWrST5JEk3QGDBMdvOBcbs5/a77NhLqGrSV0uL0bnpTf+s/aA== -----END AGE ENCRYPTED FILE----- - recipient: age1tdwlq9y4jgejkhasqwynw5uaen9xwatcvr52l70trsdxkeyvlesqjnh7l8 enc: | -----BEGIN AGE ENCRYPTED FILE----- YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAxUWNlS1Zka2xzVXBoT0lt V0tWNDZmRUUxZlJPeWw0eUIzOXlTdkNSVnpnCk1CeVBPbnBZNU9WZFIvNXdYaWFz eDgyRlgvQlY2K0tibGlIQzNtVEJCQnMKLS0tIEd0UmNDMHloRGlqMkZxd1Z0czdu SHZrU3R0TWJTU0Z4eGNCUklTaUFIMm8KwDUFdBohEbjqHDYA4ipruKN9gTJezOLp VR7vmdlCfDxDk/L513uF8NpgBe1WoPG/7caiZJsC1gBk4NvbVY4Ksg== -----END AGE ENCRYPTED FILE----- - recipient: age16p3zls5n0jks6amszwcuaqgl5dyuyf8k8wgeyrw562s5s88xtq3qq046fh enc: | -----BEGIN AGE ENCRYPTED FILE----- YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBFQTB2UllwNFp0V2xqaXBO UmdKUFNOdXM4R2s4MFZLQVIrNmlMdmowQ0ZjCkpLbjRubXpYanU0QXFkM1JsY0Rn RnBocTYycjVzUGVsWFpnNTVtWVI0NVUKLS0tIDBqaURSWGk1aThYUEV6ckI5SHVt Y1ozTnlTSWtUL3JpOXc1WjZEU001QTgK7/8+eTvxxU+pWiJI/MbT3YieClLU25tA l7j7W3EfAI6/uzI511r6ZzNPmnI3o8IY7V+B6MOcz+Td8FAQmjXqAQ== -----END AGE ENCRYPTED FILE----- - recipient: age1v52mx8gs4ephprep0wcc4j3fvvprppvs9vktf2p24yv52sqsf33sd5crk9 enc: | -----BEGIN AGE ENCRYPTED FILE----- YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB6WU5PbU5hTS8ySjBTdEF4 WXprUEV2ZXV6VHdtRld3N292QW9jYjltaUVNCkNSQWlrelF5NVEwc3duVUhYTDhY Q3VFVkora01kME5ock9uREh1N2kyak0KLS0tIEE5R0xLMUMvV2VUUW9UUXpVN3ky dmQ2Zml6dEtxbTFOMS9HZXZGYWRsWVUK3PW6krDcslUCf5Wj0HIFSXaD4znrfn5/ MvUAp9RQ3DRjE1YdjLrzmqadxTG7yPvboCny4ej5Ke93NKxVeCuTbQ== -----END AGE ENCRYPTED FILE----- - recipient: age1k8ufac2s0gs6nh0xsfavafz062vd36petmyv6nwmg00z4a7s4gnsjtd837 enc: | -----BEGIN AGE ENCRYPTED FILE----- YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA0NUhVclk0b1ZteGhkQVAy a0pRVE16NlBINzZ4eUw4WEVUQnBQTlhxaUhjCnNGb0kxeVhpUkNhWUhodldhdllX WDJpdHg5OG1RM3QzRDBoSnpCOHQ3V0UKLS0tIFpXYy9ndFU2Y0Izc1FvcGpuLzFa OG5UWmIxZnhvQ25kdnBPMVRZRlZkalUK02NaG8sfqVq1afKsrsaG76OX0LSAfyCX 1muVgAuT8xUfd/YY8aOeahWgK3N7uRu2Z03uKFwM04OvaH7vevtPrA== -----END AGE ENCRYPTED FILE----- - recipient: age1dcsm5awz8ekzchk7gsvndkc4kq65n2wzgavxtqe53vxdsfk9k9pqh3whru enc: | -----BEGIN AGE ENCRYPTED FILE----- YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA4bXdaVmt1dzhrY2VnNzJi Y1YzeHEyOFdtMEwyVDE1SDBaaFQ3cVVKTFJNCm9MKzMxZklnTUl2c3V1amRmL1NL dE9uaUc2dGgyQ1RZSjd2MHVWREFBVm8KLS0tIElOWjlnQUlCSWRpeWU1ZVVzc2lK SlExSjRlT1JleGdSOHg3Um5mREJ4TWMKR97SREF+I+sBcZthPFaG2x4c641Cz1lc gfEk7fT43eK/vft7wxRrQu7RvUPbZi/Jvt230uqFx67Lwakx8MRKgw== -----END AGE ENCRYPTED FILE----- lastmodified: "2023-05-11T08:51:55Z" mac: ENC[AES256_GCM,data:wPGzbL5U6O/W1BFu58ChwfsvC2x8Tg/+XjoH8MXvYZqzqB1TJbAW+ke6ivQ2HwSOdTXUEhv+Q2G//CXHnR1P4X0gjfdKJfP+puIaxAL8vqG51f5y518JYl9B5XUQtj3oEaC+ZZw2KOTec+PfZqXL8d9HY4030gkbeqloiCbN/R4=,iv:ghpIlpo9AWMRV62syST8F8PvazRZDGy4zpuVrDSNtxQ=,tag:9iPFzhaW7gbfgrwcO4J8NA==,type:str] pgp: [] unencrypted_suffix: _unencrypted version: 3.7.3 ================================================ FILE: system/_modules/programs/nh/default.nix ================================================ { config, lib, options, ... }: let cfg = config.mySystem.programs.nh; inherit (lib) mkEnableOption mkOption mkMerge mkIf ; in { options.mySystem.programs.nh = { enable = mkEnableOption "nh Nix CLI helper"; flake = mkOption { type = options.programs.nh.flake.type; default = options.programs.nh.flake.default; }; }; config = mkMerge [ (mkIf (cfg.enable) { programs.nh = { enable = true; flake = cfg.flake; clean = { enable = true; dates = "weekly"; }; }; programs.fish.shellAbbrs = { nos = "git -C $FLAKE pull; nh os switch"; }; }) (mkIf (!cfg.enable) { nix.gc = { automatic = true; dates = "weekly"; }; }) ]; } ================================================ FILE: system/_modules/programs/qmk/default.nix ================================================ { config, lib, ... }: let mySystem = config.mySystem; cfg = mySystem.programs.qmk; in { options.mySystem.programs.qmk = { enable = lib.mkEnableOption "QMK"; }; config = lib.mkIf (cfg.enable) { # needed for qmk udev rule users = { groups.plugdev = { }; users.${mySystem.adminUser}.extraGroups = [ "plugdev" ]; }; hardware.keyboard.qmk.enable = true; }; } ================================================ FILE: system/_modules/services/btrfs-autoscrub/default.nix ================================================ { config, options, pkgs, lib, utils, ... }: let cfg = config.mySystem.services.btrfs-autoscrub; parentOpt = options.services.btrfs.autoScrub; mail-started = pkgs.writeShellScript "mail.sh" '' DIRNAME=$1 HOSTNAME=${config.networking.fqdnOrHostName} TO=budimanjojo@gmail.com /run/wrappers/bin/sendmail -i -- $TO << EOF From: btrfs on $HOSTNAME To: $TO Subject: BTRFS scrub status BTRFS scrub for "$DIRNAME" on "$HOSTNAME" started. EOF ''; mail-stopped = pkgs.writeShellScript "mail.sh" '' DIRNAME=$1 HOSTNAME=${config.networking.fqdnOrHostName} TO=budimanjojo@gmail.com /run/wrappers/bin/sendmail -i -- $TO << EOF From: btrfs on $HOSTNAME To: $TO Subject: BTRFS scrub status BTRFS scrub for "$DIRNAME" on "$HOSTNAME" unexpectedly stopped. EOF ''; in { options.mySystem.services.btrfs-autoscrub = { enable = lib.mkEnableOption "btrfs auto scrub"; interval = lib.mkOption { type = parentOpt.interval.type; default = parentOpt.interval.default; }; fileSystems = lib.mkOption { type = parentOpt.fileSystems.type; default = parentOpt.fileSystems.default; }; }; config = lib.mkIf (cfg.enable) { mySystem.programs.msmtp.enable = true; services.btrfs.autoScrub = { enable = true; interval = cfg.interval; fileSystems = cfg.fileSystems; }; systemd.services = builtins.listToAttrs ( map ( fs: (lib.nameValuePair "btrfs-scrub-${utils.escapeSystemdPath fs}" { postStart = "${mail-started} ${fs}"; postStop = "(${pkgs.btrfs-progs}/bin/btrfs scrub status ${fs} | ${pkgs.gnugrep}/bin/grep finished) || ${mail-stopped} ${fs}"; }) ) cfg.fileSystems ); }; } ================================================ FILE: system/_modules/services/grafana/default.nix ================================================ { config, lib, ... }: let cfg = config.mySystem.services.grafana; prometheusCfg = config.mySystem.services.prometheus; in { options.mySystem.services.grafana = { enable = lib.mkEnableOption "Grafana"; }; config = lib.mkIf (cfg.enable) { sops.secrets.grafana-password = { sopsFile = ./secret.sops.yaml; owner = config.users.users.grafana.name; }; services = { grafana = { enable = true; settings = { security = { admin_password = "$__file{${config.sops.secrets.grafana-password.path}}"; }; server = { http_addr = "0.0.0.0"; }; }; provision = lib.mkIf prometheusCfg.enable { enable = true; datasources.settings.datasources = [ { name = "Prometheus"; type = "prometheus"; url = "http://localhost:${toString config.services.prometheus.port}"; isDefault = true; } ]; }; }; }; networking.firewall.allowedTCPPorts = [ config.services.grafana.settings.server.http_port ]; }; } ================================================ FILE: system/_modules/services/grafana/secret.sops.yaml ================================================ grafana-password: ENC[AES256_GCM,data:T+AgSaSWbLc+KB8=,iv:UBnSF5xWdSPR/zTCYA2CArvJnQexE7uIKBIFUNTPNY4=,tag:9fUrNlgotTaJTu8pyC+LAQ==,type:str] sops: kms: [] gcp_kms: [] azure_kv: [] hc_vault: [] age: - recipient: age1zeqkpfz7e3s207ynea0z0auc0mrct0pc7w4sh6j3d0c4qac3dahqj9ufdg enc: | -----BEGIN AGE ENCRYPTED FILE----- YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB6T2pVNGZEdWdlaEh4Ty9v dHBIU2g5clJmR0I3cEx3Z256cXN3UjVYZVhVCis2L2hjRHFlTHVkeW5zcGEydDI4 Nktqc3RzU21DWDRwLzgzbDFWcjBLUzQKLS0tIGY2UEtmVklRVlZxZnk3Y2NiRUNy YkFINTdiemQrYXUzNmg2ZDQ5RmdINmcKGAhrDrVzqFNLtwxH//T49XhfRs31kXQA kJFgyd0RplorB+ML2sk9KsDtcu6w3AMtVhhdkLQAi6I/WSOsldnpFA== -----END AGE ENCRYPTED FILE----- - recipient: age1tdwlq9y4jgejkhasqwynw5uaen9xwatcvr52l70trsdxkeyvlesqjnh7l8 enc: | -----BEGIN AGE ENCRYPTED FILE----- YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBOSDA5UHE4NE9vN0IvQTFX OFo2dFNuSFRkWEx2YVhlMXFtU2RVMmJaOVRnCmV0bVRUU1MyYXZlY0o5cFlsOERF WUY1dFBtZWs1Q3B3ZkxZNThwbkhBMDAKLS0tIGEzTUM5dkhqZkJJeExVQmlJVUxs eXFTaGhhOGVZN1BEbDZvcGFseW5FNHcKV8ADjs9O9jS/mkOfkoMgSxz6GgDePa2Z OTR7ax+oLbbjX0zy7UIA62HbULcwCuENlbMwopiZbkSRryXfgljaOg== -----END AGE ENCRYPTED FILE----- - recipient: age16p3zls5n0jks6amszwcuaqgl5dyuyf8k8wgeyrw562s5s88xtq3qq046fh enc: | -----BEGIN AGE ENCRYPTED FILE----- YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBRZ2VUNXQ3aTJ4Z2hPZXVo c1d3dDMzNTh2ZThIQnJ3Mm5zR2ZxMmhKdVRJCjF3Ylg1LzBsa1JabnJ6KytEWGdS b2ZFRlByY1k1a0hJdHpIcWFCbCtzUVUKLS0tIDMrSFI4RFlYREsyQm9LQ1ExdUI0 aEdFZDQ1Mm9jRk5ySGp3d21ZUUVRSzQKkOu3hzEQ0ax1OSP16JFHfOm5mWGi6whL vFmu7zkS7sBbBdX39pUbMbJz8hZ4nVirnTmRJtObxXX+2hTJN9KbHQ== -----END AGE ENCRYPTED FILE----- - recipient: age1v52mx8gs4ephprep0wcc4j3fvvprppvs9vktf2p24yv52sqsf33sd5crk9 enc: | -----BEGIN AGE ENCRYPTED FILE----- YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBkc3VNRmw1enFKVUNaVnRt QXpvSnFrc2NNNWExUkU4eWViei9nT2M4TlhNCjhKbGplL0E5b2lFN1orY1E3c2Qr QmIrT1VKb20ybldWVTEwZDZYNjVtVDQKLS0tIGN2VlZsVlVjKzJkNTVFYnZjMVU5 eFBFWjdnNzFFcGNVUTRiUkhFZlpPbHcKJ/xiu6/EwbOMISliRzRwJpKwxxgZVWiK AkJz9NoRJiMCvVHQywCcKa2R49lY2tjbhxCt0Wvzco6w2t2LYCyZdw== -----END AGE ENCRYPTED FILE----- - recipient: age1k8ufac2s0gs6nh0xsfavafz062vd36petmyv6nwmg00z4a7s4gnsjtd837 enc: | -----BEGIN AGE ENCRYPTED FILE----- YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB3SnA4ME1WUmtsUXpVWWdJ UFNnSkFLWEdoNEFhK3BGb1RuTW43THJmcFE4CnFIU2V6Y3BVNjI0ckh4bUMzUGRK TXVhL2JHYk1CWHJodzFuSEZkQzdRWmsKLS0tIDZYZStsc0NnSFNGYW1sRVdhcWxh YzFSVXdRY1dnOENzWk5rOTBrN085T3MKKuv/Lvv1wxW++WQycnymscJnbZ1v2qfk jmYBLEWMHDqEy1Cd8lUErg1WxASOrgloVBwrsOTbkKfDq3bNNzPIbA== -----END AGE ENCRYPTED FILE----- - recipient: age1dcsm5awz8ekzchk7gsvndkc4kq65n2wzgavxtqe53vxdsfk9k9pqh3whru enc: | -----BEGIN AGE ENCRYPTED FILE----- YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBFc2ZEUElTckdjdnpWN2x3 L2w3cVNXY0ttczBqUkpHTVJxZ1pEUTQyd0gwCnFWN3JuckoxU3Y1Ty9nNmlSU3Jw cXVXd05Vb2RGdDN0TzFKK2ZzZWZtbk0KLS0tIGk1UUdxeUF0WTlNd0VJVzRpSVRi V0FkQk5sWVB2bWFLbjVqSFoxcklrRTAKUfnvSKSkVC2Q0XvW4VqwuWNVDGBpWuz0 DtXLoGOCj6dAhVuZPjV46mqQINbYIlWJq7zC0cmAvP14wQkh/FAI1w== -----END AGE ENCRYPTED FILE----- lastmodified: "2023-05-11T08:44:33Z" mac: ENC[AES256_GCM,data:yZViZY+PSxsli5z9+nScdWqpwwHr9kwkT1/LhOin4ygLVGFDe7SQBvu4rEflpkkIKjm2jHUAZ0i6D5OVH9QXSV8q9sdlqcc+WqhxSYcTUmpLl2KNR2r/DHq3RZNhElX30byLjUAzfHYW8Ld7XtVH2hx/sWF/Qr697Q5Qu4cG0sc=,iv:HNA684Q5Z2jQBlV69njWUrDIl6EGuwA0iZI8nhflcG0=,tag:DSomFZOJIKD2k+EfjtNnnw==,type:str] pgp: [] unencrypted_suffix: _unencrypted version: 3.7.3 ================================================ FILE: system/_modules/services/openssh/default.nix ================================================ { config, lib, ... }: let mySystem = config.mySystem; cfg = mySystem.services.openssh; inherit (lib) mkEnableOption mkOption types mkIf ; in { options.mySystem.services.openssh = { enable = mkEnableOption "openssh"; authorizedKeys = mkOption { type = types.listOf types.singleLineStr; default = [ ]; }; }; config = mkIf (cfg.enable) { services.openssh = { enable = true; settings = { PermitRootLogin = "no"; PasswordAuthentication = false; }; }; users.users.${mySystem.adminUser}.openssh.authorizedKeys.keys = cfg.authorizedKeys; }; } ================================================ FILE: system/_modules/services/prometheus/default.nix ================================================ { config, lib, ... }: let cfg = config.mySystem.services.prometheus; inherit (lib) mkEnableOption mkOption types mkIf ; in { options.mySystem.services.prometheus = { enable = mkEnableOption "Prometheus"; discordWHKey = mkOption { type = types.nullOr types.str; default = null; }; }; config = mkIf (cfg.enable) { sops.secrets.alertmanager-secret = { }; services = { prometheus = { enable = true; ruleFiles = [ ./rules/embedded-exporter.yaml ./rules/node-exporter.yaml ]; exporters = { node = { enable = true; enabledCollectors = [ "systemd" ]; }; }; alertmanager = { enable = true; environmentFile = mkIf (cfg.discordWHKey != null) ( builtins.toFile "env" "DISCORD_WEBHOOK_KEY=${cfg.discordWHKey}" ); configuration = { global = { resolve_timeout = "5m"; }; receivers = [ { name = "discord"; discord_configs = mkIf (cfg.discordWHKey != null) [ { webhook_url = "https://discord.com/api/webhooks/$DISCORD_WEBHOOK_KEY"; title = '' [{{ .Status | toUpper }}:{{ if eq .Status "firing" }}{{ .Alerts.Firing | len }}{{ else }}{{ .Alerts.Resolved | len }}{{ end }}] ''; message = '' {{- range .Alerts }} **{{ .Labels.alertname }} {{ if ne .Labels.severity "" }}({{ .Labels.severity | title }}){{ end }} ** {{- if ne .Annotations.description "" }} **Description:** {{ .Annotations.description }} {{- else if ne .Annotations.summary "" }} **Summary:** {{ .Annotations.summary }} {{- else if ne .Annotations.message "" }} **Message:** {{ .Annotations.message }} {{- else }} **Description:** N/A {{- end }} {{- end }} ''; } ]; } ]; route = { group_by = [ "alertname" ]; group_interval = "5m"; group_wait = "30s"; receiver = "discord"; repeat_interval = "12h"; }; }; }; alertmanagers = [ { static_configs = [ { targets = [ "localhost:${toString config.services.prometheus.alertmanager.port}" ]; } ]; } ]; scrapeConfigs = [ { job_name = "prometheus"; static_configs = [ { targets = [ "localhost:${toString config.services.prometheus.port}" ]; } ]; } { job_name = "node"; static_configs = [ { targets = [ "localhost:${toString config.services.prometheus.exporters.node.port}" ]; } ]; } ]; }; }; networking.firewall.allowedTCPPorts = [ config.services.prometheus.port config.services.prometheus.exporters.node.port config.services.prometheus.alertmanager.port ]; }; } ================================================ FILE: system/_modules/services/prometheus/rules/embedded-exporter.yaml ================================================ groups: - name: EmbeddedExporter rules: - alert: PrometheusJobMissing expr: 'absent(up{job="prometheus"})' for: 0m labels: severity: warning annotations: summary: Prometheus job missing (instance {{ $labels.instance }}) description: "A Prometheus job has disappeared\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" - alert: PrometheusTargetMissing expr: 'up == 0' for: 0m labels: severity: critical annotations: summary: Prometheus target missing (instance {{ $labels.instance }}) description: "A Prometheus target has disappeared. An exporter might be crashed.\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" - alert: PrometheusAllTargetsMissing expr: 'sum by (job) (up) == 0' for: 0m labels: severity: critical annotations: summary: Prometheus all targets missing (instance {{ $labels.instance }}) description: "A Prometheus job does not have living target anymore.\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" - alert: PrometheusTargetMissingWithWarmupTime expr: 'sum by (instance, job) ((up == 0) * on (instance) group_right(job) (node_time_seconds - node_boot_time_seconds > 600))' for: 0m labels: severity: critical annotations: summary: Prometheus target missing with warmup time (instance {{ $labels.instance }}) description: "Allow a job time to start up (10 minutes) before alerting that it's down.\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" - alert: PrometheusConfigurationReloadFailure expr: 'prometheus_config_last_reload_successful != 1' for: 0m labels: severity: warning annotations: summary: Prometheus configuration reload failure (instance {{ $labels.instance }}) description: "Prometheus configuration reload error\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" - alert: PrometheusTooManyRestarts expr: 'changes(process_start_time_seconds{job=~"prometheus|pushgateway|alertmanager"}[15m]) > 2' for: 0m labels: severity: warning annotations: summary: Prometheus too many restarts (instance {{ $labels.instance }}) description: "Prometheus has restarted more than twice in the last 15 minutes. It might be crashlooping.\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" - alert: PrometheusAlertmanagerConfigurationReloadFailure expr: 'alertmanager_config_last_reload_successful != 1' for: 0m labels: severity: warning annotations: summary: Prometheus AlertManager configuration reload failure (instance {{ $labels.instance }}) description: "AlertManager configuration reload error\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" - alert: PrometheusAlertmanagerConfigNotSynced expr: 'count(count_values("config_hash", alertmanager_config_hash)) > 1' for: 0m labels: severity: warning annotations: summary: Prometheus AlertManager config not synced (instance {{ $labels.instance }}) description: "Configurations of AlertManager cluster instances are out of sync\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" - alert: PrometheusNotConnectedToAlertmanager expr: 'prometheus_notifications_alertmanagers_discovered < 1' for: 0m labels: severity: critical annotations: summary: Prometheus not connected to alertmanager (instance {{ $labels.instance }}) description: "Prometheus cannot connect the alertmanager\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" - alert: PrometheusRuleEvaluationFailures expr: 'increase(prometheus_rule_evaluation_failures_total[3m]) > 0' for: 0m labels: severity: critical annotations: summary: Prometheus rule evaluation failures (instance {{ $labels.instance }}) description: "Prometheus encountered {{ $value }} rule evaluation failures, leading to potentially ignored alerts.\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" - alert: PrometheusTemplateTextExpansionFailures expr: 'increase(prometheus_template_text_expansion_failures_total[3m]) > 0' for: 0m labels: severity: critical annotations: summary: Prometheus template text expansion failures (instance {{ $labels.instance }}) description: "Prometheus encountered {{ $value }} template text expansion failures\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" - alert: PrometheusRuleEvaluationSlow expr: 'prometheus_rule_group_last_duration_seconds > prometheus_rule_group_interval_seconds' for: 5m labels: severity: warning annotations: summary: Prometheus rule evaluation slow (instance {{ $labels.instance }}) description: "Prometheus rule evaluation took more time than the scheduled interval. It indicates a slower storage backend access or too complex query.\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" - alert: PrometheusNotificationsBacklog expr: 'min_over_time(prometheus_notifications_queue_length[10m]) > 0' for: 0m labels: severity: warning annotations: summary: Prometheus notifications backlog (instance {{ $labels.instance }}) description: "The Prometheus notification queue has not been empty for 10 minutes\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" - alert: PrometheusAlertmanagerNotificationFailing expr: 'rate(alertmanager_notifications_failed_total[1m]) > 0' for: 0m labels: severity: critical annotations: summary: Prometheus AlertManager notification failing (instance {{ $labels.instance }}) description: "Alertmanager is failing sending notifications\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" - alert: PrometheusTargetEmpty expr: 'prometheus_sd_discovered_targets == 0' for: 0m labels: severity: critical annotations: summary: Prometheus target empty (instance {{ $labels.instance }}) description: "Prometheus has no target in service discovery\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" - alert: PrometheusTargetScrapingSlow expr: 'prometheus_target_interval_length_seconds{quantile="0.9"} / on (interval, instance, job) prometheus_target_interval_length_seconds{quantile="0.5"} > 1.05' for: 5m labels: severity: warning annotations: summary: Prometheus target scraping slow (instance {{ $labels.instance }}) description: "Prometheus is scraping exporters slowly since it exceeded the requested interval time. Your Prometheus server is under-provisioned.\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" - alert: PrometheusLargeScrape expr: 'increase(prometheus_target_scrapes_exceeded_sample_limit_total[10m]) > 10' for: 5m labels: severity: warning annotations: summary: Prometheus large scrape (instance {{ $labels.instance }}) description: "Prometheus has many scrapes that exceed the sample limit\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" - alert: PrometheusTargetScrapeDuplicate expr: 'increase(prometheus_target_scrapes_sample_duplicate_timestamp_total[5m]) > 0' for: 0m labels: severity: warning annotations: summary: Prometheus target scrape duplicate (instance {{ $labels.instance }}) description: "Prometheus has many samples rejected due to duplicate timestamps but different values\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" - alert: PrometheusTsdbCheckpointCreationFailures expr: 'increase(prometheus_tsdb_checkpoint_creations_failed_total[1m]) > 0' for: 0m labels: severity: critical annotations: summary: Prometheus TSDB checkpoint creation failures (instance {{ $labels.instance }}) description: "Prometheus encountered {{ $value }} checkpoint creation failures\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" - alert: PrometheusTsdbCheckpointDeletionFailures expr: 'increase(prometheus_tsdb_checkpoint_deletions_failed_total[1m]) > 0' for: 0m labels: severity: critical annotations: summary: Prometheus TSDB checkpoint deletion failures (instance {{ $labels.instance }}) description: "Prometheus encountered {{ $value }} checkpoint deletion failures\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" - alert: PrometheusTsdbCompactionsFailed expr: 'increase(prometheus_tsdb_compactions_failed_total[1m]) > 0' for: 0m labels: severity: critical annotations: summary: Prometheus TSDB compactions failed (instance {{ $labels.instance }}) description: "Prometheus encountered {{ $value }} TSDB compactions failures\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" - alert: PrometheusTsdbHeadTruncationsFailed expr: 'increase(prometheus_tsdb_head_truncations_failed_total[1m]) > 0' for: 0m labels: severity: critical annotations: summary: Prometheus TSDB head truncations failed (instance {{ $labels.instance }}) description: "Prometheus encountered {{ $value }} TSDB head truncation failures\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" - alert: PrometheusTsdbReloadFailures expr: 'increase(prometheus_tsdb_reloads_failures_total[1m]) > 0' for: 0m labels: severity: critical annotations: summary: Prometheus TSDB reload failures (instance {{ $labels.instance }}) description: "Prometheus encountered {{ $value }} TSDB reload failures\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" - alert: PrometheusTsdbWalCorruptions expr: 'increase(prometheus_tsdb_wal_corruptions_total[1m]) > 0' for: 0m labels: severity: critical annotations: summary: Prometheus TSDB WAL corruptions (instance {{ $labels.instance }}) description: "Prometheus encountered {{ $value }} TSDB WAL corruptions\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" - alert: PrometheusTsdbWalTruncationsFailed expr: 'increase(prometheus_tsdb_wal_truncations_failed_total[1m]) > 0' for: 0m labels: severity: critical annotations: summary: Prometheus TSDB WAL truncations failed (instance {{ $labels.instance }}) description: "Prometheus encountered {{ $value }} TSDB WAL truncation failures\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" - alert: PrometheusTimeserieCardinality expr: 'label_replace(count by(__name__) ({__name__=~".+"}), "name", "$1", "__name__", "(.+)") > 10000' for: 0m labels: severity: warning annotations: summary: Prometheus timeserie cardinality (instance {{ $labels.instance }}) description: "The \"{{ $labels.name }}\" timeserie cardinality is getting very high: {{ $value }}\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" ================================================ FILE: system/_modules/services/prometheus/rules/node-exporter.yaml ================================================ groups: - name: NodeExporter rules: - alert: HostOutOfMemory expr: 'node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes * 100 < 10' for: 2m labels: severity: warning annotations: summary: Host out of memory (instance {{ $labels.instance }}) description: "Node memory is filling up (< 10% left)\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" - alert: HostMemoryUnderMemoryPressure expr: 'rate(node_vmstat_pgmajfault[1m]) > 1000' for: 2m labels: severity: warning annotations: summary: Host memory under memory pressure (instance {{ $labels.instance }}) description: "The node is under heavy memory pressure. High rate of major page faults\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" - alert: HostMemoryIsUnderUtilized expr: '100 - (rate(node_memory_MemAvailable_bytes[30m]) / node_memory_MemTotal_bytes * 100) < 20' for: 1w labels: severity: info annotations: summary: Host Memory is under utilized (instance {{ $labels.instance }}) description: "Node memory is < 20% for 1 week. Consider reducing memory space.\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" - alert: HostUnusualNetworkThroughputIn expr: 'sum by (instance) (rate(node_network_receive_bytes_total[2m])) / 1024 / 1024 > 100' for: 5m labels: severity: warning annotations: summary: Host unusual network throughput in (instance {{ $labels.instance }}) description: "Host network interfaces are probably receiving too much data (> 100 MB/s)\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" - alert: HostUnusualNetworkThroughputOut expr: 'sum by (instance) (rate(node_network_transmit_bytes_total[2m])) / 1024 / 1024 > 100' for: 5m labels: severity: warning annotations: summary: Host unusual network throughput out (instance {{ $labels.instance }}) description: "Host network interfaces are probably sending too much data (> 100 MB/s)\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" - alert: HostUnusualDiskReadRate expr: 'sum by (instance) (rate(node_disk_read_bytes_total[2m])) / 1024 / 1024 > 50' for: 5m labels: severity: warning annotations: summary: Host unusual disk read rate (instance {{ $labels.instance }}) description: "Disk is probably reading too much data (> 50 MB/s)\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" - alert: HostUnusualDiskWriteRate expr: 'sum by (instance) (rate(node_disk_written_bytes_total[2m])) / 1024 / 1024 > 50' for: 2m labels: severity: warning annotations: summary: Host unusual disk write rate (instance {{ $labels.instance }}) description: "Disk is probably writing too much data (> 50 MB/s)\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" - alert: HostOutOfDiskSpace expr: '(node_filesystem_avail_bytes * 100) / node_filesystem_size_bytes < 10 and ON (instance, device, mountpoint) node_filesystem_readonly == 0' for: 2m labels: severity: warning annotations: summary: Host out of disk space (instance {{ $labels.instance }}) description: "Disk is almost full (< 10% left)\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" - alert: HostDiskWillFillIn24Hours expr: '(node_filesystem_avail_bytes * 100) / node_filesystem_size_bytes < 10 and ON (instance, device, mountpoint) predict_linear(node_filesystem_avail_bytes{fstype!~"tmpfs"}[1h], 24 * 3600) < 0 and ON (instance, device, mountpoint) node_filesystem_readonly == 0' for: 2m labels: severity: warning annotations: summary: Host disk will fill in 24 hours (instance {{ $labels.instance }}) description: "Filesystem is predicted to run out of space within the next 24 hours at current write rate\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" - alert: HostOutOfInodes expr: 'node_filesystem_files_free / node_filesystem_files * 100 < 10 and ON (instance, device, mountpoint) node_filesystem_readonly == 0' for: 2m labels: severity: warning annotations: summary: Host out of inodes (instance {{ $labels.instance }}) description: "Disk is almost running out of available inodes (< 10% left)\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" - alert: HostInodesWillFillIn24Hours expr: 'node_filesystem_files_free / node_filesystem_files * 100 < 10 and predict_linear(node_filesystem_files_free[1h], 24 * 3600) < 0 and ON (instance, device, mountpoint) node_filesystem_readonly == 0' for: 2m labels: severity: warning annotations: summary: Host inodes will fill in 24 hours (instance {{ $labels.instance }}) description: "Filesystem is predicted to run out of inodes within the next 24 hours at current write rate\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" - alert: HostUnusualDiskReadLatency expr: 'rate(node_disk_read_time_seconds_total[1m]) / rate(node_disk_reads_completed_total[1m]) > 0.1 and rate(node_disk_reads_completed_total[1m]) > 0' for: 2m labels: severity: warning annotations: summary: Host unusual disk read latency (instance {{ $labels.instance }}) description: "Disk latency is growing (read operations > 100ms)\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" - alert: HostUnusualDiskWriteLatency expr: 'rate(node_disk_write_time_seconds_total[1m]) / rate(node_disk_writes_completed_total[1m]) > 0.1 and rate(node_disk_writes_completed_total[1m]) > 0' for: 2m labels: severity: warning annotations: summary: Host unusual disk write latency (instance {{ $labels.instance }}) description: "Disk latency is growing (write operations > 100ms)\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" - alert: HostHighCpuLoad expr: 'sum by (instance) (avg by (mode, instance) (rate(node_cpu_seconds_total{mode!="idle"}[2m]))) > 0.8' for: 0m labels: severity: warning annotations: summary: Host high CPU load (instance {{ $labels.instance }}) description: "CPU load is > 80%\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" - alert: HostCpuIsUnderUtilized expr: '100 - (rate(node_cpu_seconds_total{mode="idle"}[30m]) * 100) < 20' for: 1w labels: severity: info annotations: summary: Host CPU is under utilized (instance {{ $labels.instance }}) description: "CPU load is < 20% for 1 week. Consider reducing the number of CPUs.\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" - alert: HostCpuStealNoisyNeighbor expr: 'avg by(instance) (rate(node_cpu_seconds_total{mode="steal"}[5m])) * 100 > 10' for: 0m labels: severity: warning annotations: summary: Host CPU steal noisy neighbor (instance {{ $labels.instance }}) description: "CPU steal is > 10%. A noisy neighbor is killing VM performances or a spot instance may be out of credit.\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" - alert: HostCpuHighIowait expr: 'avg by (instance) (rate(node_cpu_seconds_total{mode="iowait"}[5m])) * 100 > 10' for: 0m labels: severity: warning annotations: summary: Host CPU high iowait (instance {{ $labels.instance }}) description: "CPU iowait > 10%. A high iowait means that you are disk or network bound.\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" - alert: HostUnusualDiskIo expr: 'rate(node_disk_io_time_seconds_total[1m]) > 0.5' for: 5m labels: severity: warning annotations: summary: Host unusual disk IO (instance {{ $labels.instance }}) description: "Time spent in IO is too high on {{ $labels.instance }}. Check storage for issues.\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" - alert: HostContextSwitching expr: '(rate(node_context_switches_total[5m])) / (count without(cpu, mode) (node_cpu_seconds_total{mode="idle"})) > 1000' for: 0m labels: severity: warning annotations: summary: Host context switching (instance {{ $labels.instance }}) description: "Context switching is growing on node (> 1000 / s)\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" - alert: HostSwapIsFillingUp expr: '(1 - (node_memory_SwapFree_bytes / node_memory_SwapTotal_bytes)) * 100 > 80' for: 2m labels: severity: warning annotations: summary: Host swap is filling up (instance {{ $labels.instance }}) description: "Swap is filling up (>80%)\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" - alert: HostSystemdServiceCrashed expr: 'node_systemd_unit_state{state="failed"} == 1' for: 0m labels: severity: warning annotations: summary: Host systemd service crashed (instance {{ $labels.instance }}) description: "systemd service crashed\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" - alert: HostPhysicalComponentTooHot expr: 'node_hwmon_temp_celsius * ignoring(label) group_left(instance, job, node, sensor) node_hwmon_sensor_label{label!="tctl"} > 75' for: 5m labels: severity: warning annotations: summary: Host physical component too hot (instance {{ $labels.instance }}) description: "Physical hardware component too hot\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" - alert: HostNodeOvertemperatureAlarm expr: 'node_hwmon_temp_crit_alarm_celsius == 1' for: 0m labels: severity: critical annotations: summary: Host node overtemperature alarm (instance {{ $labels.instance }}) description: "Physical node temperature alarm triggered\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" - alert: HostRaidArrayGotInactive expr: 'node_md_state{state="inactive"} > 0' for: 0m labels: severity: critical annotations: summary: Host RAID array got inactive (instance {{ $labels.instance }}) description: "RAID array {{ $labels.device }} is in degraded state due to one or more disks failures. Number of spare drives is insufficient to fix issue automatically.\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" - alert: HostRaidDiskFailure expr: 'node_md_disks{state="failed"} > 0' for: 2m labels: severity: warning annotations: summary: Host RAID disk failure (instance {{ $labels.instance }}) description: "At least one device in RAID array on {{ $labels.instance }} failed. Array {{ $labels.md_device }} needs attention and possibly a disk swap\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" - alert: HostKernelVersionDeviations expr: 'count(sum(label_replace(node_uname_info, "kernel", "$1", "release", "([0-9]+.[0-9]+.[0-9]+).*")) by (kernel)) > 1' for: 6h labels: severity: warning annotations: summary: Host kernel version deviations (instance {{ $labels.instance }}) description: "Different kernel versions are running\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" - alert: HostOomKillDetected expr: 'increase(node_vmstat_oom_kill[1m]) > 0' for: 0m labels: severity: warning annotations: summary: Host OOM kill detected (instance {{ $labels.instance }}) description: "OOM kill detected\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" - alert: HostEdacCorrectableErrorsDetected expr: 'increase(node_edac_correctable_errors_total[1m]) > 0' for: 0m labels: severity: info annotations: summary: Host EDAC Correctable Errors detected (instance {{ $labels.instance }}) description: "Host {{ $labels.instance }} has had {{ printf \"%.0f\" $value }} correctable memory errors reported by EDAC in the last 5 minutes.\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" - alert: HostEdacUncorrectableErrorsDetected expr: 'node_edac_uncorrectable_errors_total > 0' for: 0m labels: severity: warning annotations: summary: Host EDAC Uncorrectable Errors detected (instance {{ $labels.instance }}) description: "Host {{ $labels.instance }} has had {{ printf \"%.0f\" $value }} uncorrectable memory errors reported by EDAC in the last 5 minutes.\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" - alert: HostNetworkReceiveErrors expr: 'rate(node_network_receive_errs_total[2m]) / rate(node_network_receive_packets_total[2m]) > 0.01' for: 2m labels: severity: warning annotations: summary: Host Network Receive Errors (instance {{ $labels.instance }}) description: "Host {{ $labels.instance }} interface {{ $labels.device }} has encountered {{ printf \"%.0f\" $value }} receive errors in the last two minutes.\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" - alert: HostNetworkTransmitErrors expr: 'rate(node_network_transmit_errs_total[2m]) / rate(node_network_transmit_packets_total[2m]) > 0.01' for: 2m labels: severity: warning annotations: summary: Host Network Transmit Errors (instance {{ $labels.instance }}) description: "Host {{ $labels.instance }} interface {{ $labels.device }} has encountered {{ printf \"%.0f\" $value }} transmit errors in the last two minutes.\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" - alert: HostNetworkInterfaceSaturated expr: '(rate(node_network_receive_bytes_total{device!~"^tap.*|^vnet.*|^veth.*|^tun.*"}[1m]) + rate(node_network_transmit_bytes_total{device!~"^tap.*|^vnet.*|^veth.*|^tun.*"}[1m])) / node_network_speed_bytes{device!~"^tap.*|^vnet.*|^veth.*|^tun.*"} > 0.8 < 10000' for: 1m labels: severity: warning annotations: summary: Host Network Interface Saturated (instance {{ $labels.instance }}) description: "The network interface \"{{ $labels.device }}\" on \"{{ $labels.instance }}\" is getting overloaded.\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" - alert: HostNetworkBondDegraded expr: '(node_bonding_active - node_bonding_slaves) != 0' for: 2m labels: severity: warning annotations: summary: Host Network Bond Degraded (instance {{ $labels.instance }}) description: "Bond \"{{ $labels.device }}\" degraded on \"{{ $labels.instance }}\".\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" - alert: HostConntrackLimit expr: 'node_nf_conntrack_entries / node_nf_conntrack_entries_limit > 0.8' for: 5m labels: severity: warning annotations: summary: Host conntrack limit (instance {{ $labels.instance }}) description: "The number of conntrack is approaching limit\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" - alert: HostClockSkew expr: '(node_timex_offset_seconds > 0.05 and deriv(node_timex_offset_seconds[5m]) >= 0) or (node_timex_offset_seconds < -0.05 and deriv(node_timex_offset_seconds[5m]) <= 0)' for: 2m labels: severity: warning annotations: summary: Host clock skew (instance {{ $labels.instance }}) description: "Clock skew detected. Clock is out of sync. Ensure NTP is configured correctly on this host.\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" - alert: HostClockNotSynchronising expr: 'min_over_time(node_timex_sync_status[1m]) == 0 and node_timex_maxerror_seconds >= 16' for: 2m labels: severity: warning annotations: summary: Host clock not synchronising (instance {{ $labels.instance }}) description: "Clock not synchronising. Ensure NTP is configured on this host.\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" - alert: HostRequiresReboot expr: 'node_reboot_required > 0' for: 4h labels: severity: info annotations: summary: Host requires reboot (instance {{ $labels.instance }}) description: "{{ $labels.instance }} requires a reboot.\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" ================================================ FILE: system/_modules/services/restic-backup/default.nix ================================================ { config, lib, pkgs, ... }: let cfg = config.mySystem.services.restic-backup; inherit (lib) mkEnableOption mkOption types mkIf ; in { options.mySystem.services.restic-backup = { enable = mkEnableOption "restic-backup"; location = mkOption { type = types.str; description = "restic repository location"; default = "s3:http://192.168.15.15:9000/restic-repository"; }; }; config = mkIf (cfg.enable) { # function to create attrset that can be consumed by `services.restic.backups` module # options needs to be an attrset that has `app`, `paths`, and optional `excludePaths` keys lib.mySystem.mkRestic = options: { "${options.app}" = { paths = options.paths; initialize = true; repository = "${cfg.location}/${options.app}"; passwordFile = config.sops.secrets."restic/password".path; environmentFile = config.sops.secrets."restic/env".path; exclude = if builtins.hasAttr "excludePaths" options then options.excludePaths else [ ]; timerConfig = { OnCalendar = "02:00"; Persistent = true; RandomizedDelaySec = "3h"; }; pruneOpts = [ "--keep-daily 10" "--keep-monthly 2" ]; backupPrepareCommand = '' # remove stale locks - this avoids some occasional annoyance ${pkgs.restic}/bin/restic unlock --remove-all || true ''; }; }; sops.secrets = { "restic/password".sopsFile = ./secret.sops.yaml; "restic/env".sopsFile = ./secret.sops.yaml; }; }; } ================================================ FILE: system/_modules/services/restic-backup/secret.sops.yaml ================================================ restic: password: ENC[AES256_GCM,data:vcAJ+V2A2FHalb38LAFj3A==,iv:kvWV7+dOD9q0LpxuaYSePRH1HUiBwMvSVvhtUUguUkw=,tag:FLa0efRhc/EIT2fTSNIk4A==,type:str] env: ENC[AES256_GCM,data:vUxWjYVXFRpXxfxwGvu0qbKRX7MpyiJCLPb2Lb0t3Qt9bntXv0BeKipZXTiPDx/MrAd3ackVdJc/+B7/YzzkC8FH7IOeq04Q/KgCcGp2gC+A/PJNdD9VttUmZNMIMHVmYJcjJH8u,iv:nhGTDrwQVDYDQSVq9gWpTYLqOjje2DCzWLhp9DbECJI=,tag:FngpwkPC+HTbSucoIFXnuQ==,type:str] sops: kms: [] gcp_kms: [] azure_kv: [] hc_vault: [] age: - recipient: age1zeqkpfz7e3s207ynea0z0auc0mrct0pc7w4sh6j3d0c4qac3dahqj9ufdg enc: | -----BEGIN AGE ENCRYPTED FILE----- YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB4RlNweTFLNnJmdGdCdzl4 WFNFaUxXVE5tb2dENFFkOVd4REZGeTU4MkZBCjA0Uit0L2Jzckl6cklOQkw2dzhO cFZvMW9oUnVFTHVNSFJYLzRkbGxUbm8KLS0tIGVKZzNVcnJDRWNGZGY2UG5oT1pu WHA3a2lGQ2xOU3ZGankzSzFpTFIvWm8KNulntM1qdaitQx54lQb3bUXyTQsFWstz 4z3zH+6LltnVmNMXBgJMIVWvpof4IwaFrIIRdN15zzYrVjqrz3awaA== -----END AGE ENCRYPTED FILE----- - recipient: age1tdwlq9y4jgejkhasqwynw5uaen9xwatcvr52l70trsdxkeyvlesqjnh7l8 enc: | -----BEGIN AGE ENCRYPTED FILE----- YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBYSzdmdzd1UHI0aDlWWHht UmwrcmJieStTckJhMWNFdTYvNDRDNnRoWmk0CkpLYTRGZ0g0WDA3RjR5bVhvQjFQ U1NOVlU5WEZFTFZGMWlhaUl0MjV6NVUKLS0tIGxMWnI0QnVWMit2MElIV20yK1BF enRWSE85d1dTVWFJYyt5aWJmUytTMzAKGD9croAIzxIEFjYDY6jiyVrbZqnG7rVA 3jV1Q00xc/kzpZ48GLH4plHEDsFphJARagYViRvA57MVvgGImhXplw== -----END AGE ENCRYPTED FILE----- - recipient: age16p3zls5n0jks6amszwcuaqgl5dyuyf8k8wgeyrw562s5s88xtq3qq046fh enc: | -----BEGIN AGE ENCRYPTED FILE----- YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB5QXdOSWQwOU9qK3ppZllL SjVjSGNJb1ZyTjE3aW10R3hHQUFVdHdUSjNrCklzcjlMWFRxSDdVVHQzck1hQXdP WkVtTlFOUTdFbHBOdmhMcTB1M2FHa3cKLS0tIElQejkrWTc2MGxjd3lrdWN2cnBV YnRLc2ZRNFRSOVFYcGRxckx5WUNxRFEKNZNy2cqpNURcjvyGxsTisPcIs4Mqpn25 ao2TpzARPNv5wg5fHwao5lJQG8vTuMqBvrwtVvxwznlqrl7G1wI/kQ== -----END AGE ENCRYPTED FILE----- - recipient: age1v52mx8gs4ephprep0wcc4j3fvvprppvs9vktf2p24yv52sqsf33sd5crk9 enc: | -----BEGIN AGE ENCRYPTED FILE----- YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBiWDRubzhKTFlaVzFCSnZH eTR2aTVJSTNybUhLSkpBQ2swczRIQzkzTURnCjJseVlUZk5hcDg0SDF0YkpIdUxL KzRwQzJZWHdqMlJaZ1kwQW1ZRWNzbE0KLS0tIFhVYU9XTzVhNWNad1BxdUx3U0Fp STJUbGp4ZHkzOXhiT2ZGQkpTdHpheUUKASN7GU6nImNIw4YknBFhjCIAQnnlqKv3 X6w9Jqsx/lIjFP5Yue+QpeXaeNPR+Spw1QGqtTYjlhLmyfTc2IDSLQ== -----END AGE ENCRYPTED FILE----- - recipient: age1k8ufac2s0gs6nh0xsfavafz062vd36petmyv6nwmg00z4a7s4gnsjtd837 enc: | -----BEGIN AGE ENCRYPTED FILE----- YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAyN0hJREJsaTNDaDB2b2Vs ZUl2U3BmNzkyV2dNRlpIejB3ckZZeXFaSldFCm5KbURUaWxFN251ZVR0dmQwWEJ3 K2lHWUhZZFNPUUJ1NzEzSTJ6b0FpU1EKLS0tIFNtQTc3NjJ4OEVrQ1oxQVltdG00 a05pcEZJVkJkbDU1K3lZNzdIY1VxdUEKZ12N9I7PJ6a49SvrpsAL8OSY4G0GEXhC yw0+xfdR84EjSezgxFteqHbE6MhmFkLzCBZpAnlvk2rv+CUriCg70Q== -----END AGE ENCRYPTED FILE----- - recipient: age1dcsm5awz8ekzchk7gsvndkc4kq65n2wzgavxtqe53vxdsfk9k9pqh3whru enc: | -----BEGIN AGE ENCRYPTED FILE----- YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBZc28xYlE2Sy9OY1ZHK3RS RXVkd09obHp4aStSaWhtUUVrT1RXTzVnUFM4CksrN1FwZHdxckRER3RaRUFucEJO Yk1EYWxPajdVQi82SDBnTzBOOG1Ca00KLS0tIGpjUGFjQW5JejNZOXVkQThrVjFm dEI1dkdWRkhTNDExOTZYakoyRlJkU28Kzk4oPwyfgSQsOtkrXAkn0kTCjJuK7RWj KhG2R/HRpDIwX5M0dlDhlpbmwgZFibNbokeEHdF73sNVqQfzd45ExQ== -----END AGE ENCRYPTED FILE----- lastmodified: "2024-12-06T04:37:50Z" mac: ENC[AES256_GCM,data:TqtEz8eA8WkAwKD7Ju1KNRGHZEK5Lod5ihKL9EjvM9SxiXxLb1Lyzi9IxfKjoA1dnIHpQYl7Pn0WOg7mz0tOg+UxLRjQJCj+3vzQhyF+CmStqKRyoPeA84vUMv2dIFhHHP0w5+wFK+P/uXX52qRuUizhRNCu4u/cvX0iy8NUZfs=,iv:qW1CvW1M7Lv/9O8ISfhaMlMeaJ//zqRe31BPwFaJgOg=,tag:IUImY5FadTgPYdJMHzljUg==,type:str] pgp: [] unencrypted_suffix: _unencrypted version: 3.8.1 ================================================ FILE: system/_modules/system/autoupgrade/default.nix ================================================ { config, lib, ... }: let cfg = config.mySystem.system.autoupgrade; inherit (lib) mkEnableOption mkOption types mkIf ; in { options.mySystem.system.autoupgrade = { enable = mkEnableOption "system autoUpgrade"; dates = mkOption { type = types.str; default = "Sun 13:00"; }; }; config.system.autoUpgrade = mkIf (cfg.enable) { enable = true; flake = "github:budimanjojo/nix-config"; flags = [ "-L" # print build logs ]; dates = cfg.dates; }; } ================================================ FILE: system/_modules/system/bootloader/default.nix ================================================ { config, lib, pkgs, ... }: let myHardware = config.myHardware; in { config = lib.mkMerge [ { boot = { initrd.verbose = false; consoleLogLevel = 0; kernelParams = [ "quiet" "rd.udev.log_level=3" ]; loader.timeout = 1; }; } (lib.mkIf (myHardware.isUEFI) { boot.loader = { systemd-boot = { enable = true; # memtest86 is not available in aarch64-linux memtest86.enable = lib.mkIf (pkgs.stdenv.hostPlatform.system != "aarch64-linux") true; configurationLimit = 10; }; efi.canTouchEfiVariables = true; }; }) (lib.mkIf (!myHardware.isUEFI) { boot.loader = { grub = { enable = true; memtest86.enable = true; configurationLimit = 10; }; }; }) ]; } ================================================ FILE: system/_modules/system/cpu/default.nix ================================================ { config, lib, ... }: let myHardware = config.myHardware; in { config = lib.mkMerge [ (lib.mkIf (myHardware.cpu == "amd") { hardware.cpu.amd.updateMicrocode = true; boot.kernelModules = [ "kvm-amd" ]; }) (lib.mkIf (myHardware.cpu == "intel") { hardware.cpu.intel.updateMicrocode = true; boot.kernelModules = [ "kvm-intel" ]; }) ]; } ================================================ FILE: system/_modules/system/font/default.nix ================================================ { config, lib, pkgs, ... }: let cfg = config.mySystem.system.font; in { options.mySystem.system.font = { enable = lib.mkEnableOption "font"; }; config = lib.mkIf (cfg.enable) { fonts.packages = with pkgs; [ nerd-fonts.ubuntu-mono unifont ]; }; } ================================================ FILE: system/_modules/system/sound/default.nix ================================================ { lib, config, pkgs, ... }: let cfg = config.mySystem.system.sound; in { options.mySystem.system.sound = { enable = lib.mkEnableOption "sound"; }; config = lib.mkIf (cfg.enable) { # security.rtkit.enable = true; services.pipewire = { enable = true; alsa.enable = true; alsa.support32Bit = true; pulse.enable = true; wireplumber.configPackages = [ (pkgs.writeTextDir "share/wireplumber/bluetooth.lua.d/51-bluez-config.lua" '' bluez_monitor.properties = { ["bluez5.enable-sbc-xq"] = true, ["bluez5.enable-msbc"] = true, ["bluez5.enable-hw-volume"] = true, ["bluez5.headset-roles"] = "[ hsp_hs hsp_ag hfp_hf hfp_ag ]" } '') ]; }; }; } ================================================ FILE: system/_modules/system/video/default.nix ================================================ { pkgs, lib, config, ... }: let cfg = config.mySystem.system.video; myHardware = config.myHardware; mySystem = config.mySystem; inherit (lib) mkEnableOption mkIf mkMerge; in { options.mySystem.system.video = { enable = mkEnableOption "video"; }; config = mkIf (cfg.enable) (mkMerge [ { hardware = { graphics = { enable = true; enable32Bit = true; }; }; users.users.${mySystem.adminUser}.extraGroups = [ "video" ]; } (mkIf (myHardware.gpuDriver == "nouveau") { # enable the nouveau kernel module boot.initrd.kernelModules = [ "nouveau" ]; services.xserver.videoDrivers = [ "nouveau" ]; environment.variables = { VDPAU_DRIVER = "nouveau"; LIBVA_DRIVER_NAME = "nouveau"; }; }) (mkIf (myHardware.gpuDriver == "nvidia") { # enable nvidia kernel module boot.initrd.kernelModules = [ "nvidia" ]; boot.extraModulePackages = [ config.boot.kernelPackages.nvidia_x11 ]; services.xserver.videoDrivers = [ "nvidia" ]; # hardware acceleration hardware.graphics.extraPackages = [ pkgs.libva-vdpau-driver pkgs.libvdpau-va-gl ]; hardware.nvidia = { open = false; modesetting.enable = true; # enable Nvidia settings menu nvidiaSettings = true; }; environment.variables = { VDPAU_DRIVER = "va_gl"; LIBVA_DRIVER_NAME = "nvidia"; }; }) (mkIf (myHardware.gpuDriver == "amd") { # enable amdgpu kernel module boot.initrd.kernelModules = [ "amdgpu" ]; services.xserver.videoDrivers = [ "amdgpu" ]; # enables AMDVLK & OpenCL support hardware.graphics.extraPackages = with pkgs; [ amdvlk rocm-opencl-icd rocm-opencl-runtime ]; hardware.graphics.extraPackages32 = with pkgs; [ driversi686Linux.amdvlk ]; # force use of RADV, can be unset if AMDVLK should be used environment.variables.AMD_VULKAN_ICD = "RADV"; }) (mkIf (myHardware.gpuDriver == "intel") { # enable the i915 kernel module boot.initrd.kernelModules = [ "i915" ]; # better performance than the actual Intel driver services.xserver.videoDrivers = [ "modesetting" ]; # OpenCL support and VAAPI hardware.graphics.extraPackages = [ pkgs.intel-compute-runtime pkgs.intel-media-driver pkgs.vaapiIntel pkgs.vaapiVdpau pkgs.libvdpau-va-gl ]; environment.variables.VDPAU_DRIVER = "va_gl"; }) ]); } ================================================ FILE: system/_modules/windowmanager/add-on/blueman/default.nix ================================================ { lib, config, ... }: let mySystem = config.mySystem; cfg = mySystem.windowmanager.add-on.blueman; in { options.mySystem.windowmanager.add-on.blueman = { enable = lib.mkEnableOption "Blueman"; }; config = lib.mkIf (cfg.enable) { services.blueman.enable = true; }; } ================================================ FILE: system/_modules/windowmanager/add-on/gnome-keyring/default.nix ================================================ { config, lib, ... }: let cfg = config.mySystem.windowmanager.add-on.gnome-keyring; in { options.mySystem.windowmanager.add-on.gnome-keyring = { enable = lib.mkEnableOption "gnome-keyring"; }; config = lib.mkIf (cfg.enable) { services.gnome.gnome-keyring.enable = true; }; } ================================================ FILE: system/_modules/windowmanager/add-on/networkmanager/default.nix ================================================ { config, lib, ... }: let cfg = config.mySystem.windowmanager.add-on.networkmanager; mySystem = config.mySystem; in { options.mySystem.windowmanager.add-on.networkmanager = { enable = lib.mkEnableOption "Network Manager"; }; config = lib.mkIf (cfg.enable) { networking.networkmanager.enable = true; users.users.${mySystem.adminUser}.extraGroups = [ "networkmanager" ]; }; } ================================================ FILE: system/_modules/windowmanager/add-on/polkit-gnome/default.nix ================================================ { lib, config, pkgs, ... }: let cfg = config.mySystem.windowmanager.add-on.polkit-gnome; in { options.mySystem.windowmanager.add-on.polkit-gnome = { enable = lib.mkEnableOption "GNOME Polkit"; }; config = lib.mkIf (cfg.enable) { security.polkit.enable = true; systemd.user.services.polkit-gnome-authentication-agent-1 = { description = "polkit-gnome-authentication-agent-1"; wantedBy = [ "graphical-session.target" ]; wants = [ "graphical-session.target" ]; after = [ "graphical-session.target" ]; serviceConfig = { Type = "simple"; ExecStart = "${pkgs.polkit_gnome}/libexec/polkit-gnome-authentication-agent-1"; Restart = "on-failure"; RestartSec = 1; TimeoutStopSec = 10; }; }; }; } ================================================ FILE: system/_modules/windowmanager/add-on/thunar/default.nix ================================================ { lib, config, pkgs, ... }: let cfg = config.mySystem.windowmanager.add-on.thunar; in { options.mySystem.windowmanager.add-on.thunar = { enable = lib.mkEnableOption "Thunar file manager"; }; config = lib.mkIf (cfg.enable) { programs = { thunar = { enable = true; plugins = with pkgs.xfce; [ thunar-volman thunar-archive-plugin thunar-media-tags-plugin ]; }; }; services = { tumbler.enable = true; gvfs = { enable = true; package = pkgs.gvfs; }; }; environment.systemPackages = with pkgs; [ ffmpegthumbnailer file-roller ]; }; } ================================================ FILE: system/_modules/windowmanager/hyprland/default.nix ================================================ { pkgs, lib, config, myPkgs, ... }: let cfg = config.mySystem.windowmanager.hyprland; inherit (lib) mkEnableOption mkOption types mkIf ; in { options.mySystem.windowmanager.hyprland = { enable = mkEnableOption "Hyprland window manager"; isDefaultSession = mkOption { type = types.bool; default = true; }; }; config = mkIf (cfg.enable) { mySystem = { isWayland = true; windowmanager.add-on = { blueman.enable = true; gnome-keyring.enable = true; networkmanager.enable = true; polkit-gnome.enable = true; thunar.enable = true; }; displaymanager.sddm = { enable = true; defaultSession = mkIf (cfg.isDefaultSession) "hyprland"; }; system = { font.enable = true; sound.enable = true; video.enable = true; }; }; environment.systemPackages = with pkgs; [ at-spi2-core libappindicator-gtk3 libappindicator-gtk2 libappindicator wl-clipboard xdg-utils myPkgs.configure-gtk ]; programs.hyprland = { enable = true; xwayland.enable = true; }; services.dbus.enable = true; networking.networkmanager.enable = true; # Needed for swaylock to work security.pam.services.swaylock = { }; }; } ================================================ FILE: system/_modules/windowmanager/i3/default.nix ================================================ { config, lib, pkgs, ... }: let cfg = config.mySystem.windowmanager.i3; inherit (lib) mkEnableOption mkOption types mkIf ; in { options.mySystem.windowmanager.i3 = { enable = mkEnableOption "i3 window manager"; isDefaultSession = mkOption { type = types.bool; default = true; }; }; config = mkIf (cfg.enable) { mySystem = { isWayland = false; windowmanager.add-on = { blueman.enable = true; networkmanager.enable = true; polkit-gnome.enable = true; thunar.enable = true; }; system = { font.enable = true; sound.enable = true; video.enable = true; }; displaymanager.sddm = { enable = true; defaultSession = mkIf (cfg.isDefaultSession) "none+i3"; }; }; services.xserver = { enable = true; desktopManager = { xterm.enable = false; runXdgAutostartIfNone = true; }; windowManager.i3 = { enable = true; package = pkgs.i3-gaps; extraPackages = with pkgs; [ picom (python3Packages.py3status.overrideAttrs (oldAttrs: { propagatedBuildInputs = with python3Packages; [ pytz tzlocal ] ++ oldAttrs.propagatedBuildInputs; })) xdg-utils ]; }; xrandrHeads = builtins.map (mon: { output = mon.xname; primary = mon.primary; monitorConfig = '' Option "PreferredMode" "${toString mon.width}x${toString mon.height}" ''; }) config.myHardware.monitors; exportConfiguration = true; }; programs.dconf.enable = true; }; } ================================================ FILE: system/_modules/windowmanager/sway/default.nix ================================================ { pkgs, lib, config, myPkgs, ... }: let cfg = config.mySystem.windowmanager.sway; myHardware = config.myHardware; inherit (lib) mkEnableOption mkOption types mkIf ; in { options.mySystem.windowmanager.sway = { enable = mkEnableOption "Sway window manager"; isDefaultSession = mkOption { type = types.bool; default = true; }; }; config = mkIf (cfg.enable) { mySystem = { isWayland = true; windowmanager.add-on = { blueman.enable = true; gnome-keyring.enable = true; networkmanager.enable = true; polkit-gnome.enable = true; thunar.enable = true; }; system = { font.enable = true; sound.enable = true; video.enable = true; }; displaymanager.sddm = { enable = true; defaultSession = mkIf (cfg.isDefaultSession) "sway"; }; }; environment.systemPackages = with pkgs; [ at-spi2-core libappindicator-gtk3 libappindicator-gtk2 libappindicator wl-clipboard xdg-utils myPkgs.configure-gtk ]; programs.sway = { enable = true; wrapperFeatures.gtk = true; extraOptions = mkIf (myHardware.gpuDriver == "nvidia") [ "--unsupported-gpu" ]; extraSessionCommands = mkIf (myHardware.gpuDriver == "nvidia") '' export LIBVA_DRIVER_NAME=nvidia export XDG_SESSION_TYPE=wayland export GBM_BACKEND=nvidia-drm export __GLX_VENDOR_LIBRARY_NAME=nvidia export WLR_NO_HARDWARE_CURSORS=1 ''; }; services.dbus.enable = true; xdg.portal = { enable = true; wlr.enable = true; extraPortals = [ pkgs.xdg-desktop-portal-wlr pkgs.xdg-desktop-portal-gtk ]; }; networking.networkmanager.enable = true; programs.dconf.enable = true; programs.xwayland.enable = true; }; } ================================================ FILE: system/hosts/budimanjojo-firewall/_modules/default.nix ================================================ { ... }: { imports = [ ./firewall ./services/adguardhome ./services/chrony ./services/fireqos ./services/frr ./services/kea ./services/omada-controller ./services/powerdns ./services/rsyslogd ./services/tdarr ./network.nix ./podman.nix ./wireguard.nix ]; } ================================================ FILE: system/hosts/budimanjojo-firewall/_modules/firewall/config/sets.nft ================================================ set broadlink_plugs { type ipv4_addr elements = { 192.168.69.10 } } set android_livingroom_tv { type ipv4_addr elements = { 192.168.69.30 } } set dns_server { type ipv4_addr elements = { 192.168.10.1 } } set k8s_api { type ipv4_addr elements = { 192.168.200.20 } } set k8s_ingress { type ipv4_addr elements = { 192.168.15.1, 192.168.15.20, 192.168.15.21 } } set k8s_nodes { type ipv4_addr flags interval auto-merge elements = { 192.168.200.20-192.168.200.23 } } set k8s_minio { type ipv4_addr elements = { 192.168.15.15 } } set k8s_services { type ipv4_addr flags interval auto-merge elements = { 192.168.10.200-192.168.10.210, 192.168.15.0-192.168.15.254, 192.168.200.200-192.168.200.210 } } set k8s_vector_aggregator { type ipv4_addr elements = { 192.168.15.5 } } set omada_controller { type ipv4_addr elements = { 10.5.0.10 } } set trusted_devices { type ipv4_addr flags interval auto-merge elements = { 192.168.50.10, 192.168.50.11, 192.168.50.49, 10.10.10.12, 10.10.10.13 } } set wall_displays { type ipv4_addr elements = { 192.168.50.40 } } set omada_tcp { type inet_service flags interval auto-merge elements = { 443, 8043, 29811-29817 } } set omada_udp { type inet_service elements = { 29810 } } ================================================ FILE: system/hosts/budimanjojo-firewall/_modules/firewall/config/zone-directions.nft ================================================ chain ZONE_INPUT { type filter hook input priority filter + 1; policy accept; jump STATE_POLICY counter jump ZONE_TO_LOCAL } chain ZONE_FORWARD { type filter hook forward priority filter + 1; policy accept; jump STATE_POLICY oifname "lan0" counter jump ZONE_TO_LAN0 oifname "lan0.50" counter jump ZONE_TO_HOME oifname "lan0.69" counter jump ZONE_TO_IOT oifname "lan0.200" counter jump ZONE_TO_SERVER oifname "lan0.250" counter jump ZONE_TO_GUEST oifname "ctr0" counter jump ZONE_TO_CONTAINERS oifname "wg0" counter jump ZONE_TO_WIREGUARD oifname "wan0" counter jump ZONE_TO_WAN } chain ZONE_OUTPUT { type filter hook output priority filter + 1; policy accept; jump STATE_POLICY counter jump ZONE_FROM_LOCAL } chain ZONE_TO_LOCAL { iifname "lo" counter return iifname "lan0" counter jump LAN0-LOCAL iifname "lan0" counter return iifname "lan0.50" counter jump HOME-LOCAL iifname "lan0.50" counter return iifname "lan0.69" counter jump IOT-LOCAL iifname "lan0.69" counter return iifname "lan0.200" counter jump SERVER-LOCAL iifname "lan0.200" counter return iifname "lan0.250" counter jump GUEST-LOCAL iifname "lan0.250" counter return iifname "ctr0" counter jump CONTAINERS-LOCAL iifname "ctr0" counter return iifname "wg0" counter jump WIREGUARD-LOCAL iifname "wg0" counter return iifname "wan0" counter jump WAN-LOCAL iifname "wan0" counter return counter drop comment "ZONE_TO_LOCAL default-action drop" } chain ZONE_TO_LAN0 { iifname "lan0" counter return iifname "lan0.50" counter jump HOME-LAN0 iifname "lan0.50" counter return iifname "lan0.69" counter jump IOT-LAN0 iifname "lan0.69" counter return iifname "lan0.200" counter jump SERVER-LAN0 iifname "lan0.200" counter return iifname "lan0.250" counter jump GUEST-LAN0 iifname "lan0.250" counter return iifname "ctr0" counter jump CONTAINERS-LAN0 iifname "ctr0" counter return iifname "wg0" counter jump WIREGUARD-LAN0 iifname "wg0" counter return iifname "wan0" counter jump WAN-LAN0 iifname "wan0" counter return counter drop comment "ZONE_TO_LAN0 default-action drop" } chain ZONE_TO_HOME { iifname "lan0.50" counter return iifname "lan0" counter jump LAN0-HOME iifname "lan0" counter return iifname "lan0.69" counter jump IOT-HOME iifname "lan0.69" counter return iifname "lan0.200" counter jump SERVER-HOME iifname "lan0.200" counter return iifname "lan0.250" counter jump GUEST-HOME iifname "lan0.250" counter return iifname "ctr0" counter jump CONTAINERS-HOME iifname "ctr0" counter return iifname "wg0" counter jump WIREGUARD-HOME iifname "wg0" counter return iifname "wan0" counter jump WAN-HOME iifname "wan0" counter return counter drop comment "ZONE_TO_HOME default-action drop" } chain ZONE_TO_IOT { iifname "lan0.69" counter return iifname "lan0" counter jump LAN0-IOT iifname "lan0" counter return iifname "lan0.50" counter jump HOME-IOT iifname "lan0.50" counter return iifname "lan0.200" counter jump SERVER-IOT iifname "lan0.200" counter return iifname "lan0.250" counter jump GUEST-IOT iifname "lan0.250" counter return iifname "ctr0" counter jump CONTAINERS-IOT iifname "ctr0" counter return iifname "wg0" counter jump WIREGUARD-IOT iifname "wg0" counter return iifname "wan0" counter jump WAN-IOT iifname "wan0" counter return counter drop comment "ZONE_TO_IOT default-action drop" } chain ZONE_TO_SERVER { iifname "lan0.200" counter return iifname "lan0" counter jump LAN0-SERVER iifname "lan0" counter return iifname "lan0.50" counter jump HOME-SERVER iifname "lan0.50" counter return iifname "lan0.69" counter jump IOT-SERVER iifname "lan0.69" counter return iifname "lan0.250" counter jump GUEST-SERVER iifname "lan0.250" counter return iifname "ctr0" counter jump CONTAINERS-SERVER iifname "ctr0" counter return iifname "wg0" counter jump WIREGUARD-SERVER iifname "wg0" counter return iifname "wan0" counter jump WAN-SERVER iifname "wan0" counter return counter drop comment "ZONE_TO_SERVER default-action drop" } chain ZONE_TO_GUEST { iifname "lan0.250" counter return iifname "lan0" counter jump LAN0-GUEST iifname "lan0" counter return iifname "lan0.50" counter jump HOME-GUEST iifname "lan0.50" counter return iifname "lan0.69" counter jump IOT-GUEST iifname "lan0.69" counter return iifname "lan0.200" counter jump SERVER-GUEST iifname "lan0.200" counter return iifname "ctr0" counter jump CONTAINERS-GUEST iifname "ctr0" counter return iifname "wg0" counter jump WIREGUARD-GUEST iifname "wg0" counter return iifname "wan0" counter jump WAN-GUEST iifname "wan0" counter return counter drop comment "ZONE_TO_GUEST default-action drop" } chain ZONE_TO_CONTAINERS { iifname "ctr0" counter return iifname "lan0" counter jump LAN0-CONTAINERS iifname "lan0" counter return iifname "lan0.50" counter jump HOME-CONTAINERS iifname "lan0.50" counter return iifname "lan0.69" counter jump IOT-CONTAINERS iifname "lan0.69" counter return iifname "lan0.200" counter jump SERVER-CONTAINERS iifname "lan0.200" counter return iifname "lan0.250" counter jump GUEST-CONTAINERS iifname "lan0.250" counter return iifname "wg0" counter jump WIREGUARD-CONTAINERS iifname "wg0" counter return iifname "wan0" counter jump WAN-CONTAINERS iifname "wan0" counter return counter drop comment "ZONE_TO_CONTAINERS default-action drop" } chain ZONE_TO_WIREGUARD { iifname "wg0" counter return iifname "lan0" counter jump LAN0-WIREGUARD iifname "lan0" counter return iifname "lan0.50" counter jump HOME-WIREGUARD iifname "lan0.50" counter return iifname "lan0.69" counter jump IOT-WIREGUARD iifname "lan0.69" counter return iifname "lan0.200" counter jump SERVER-WIREGUARD iifname "lan0.200" counter return iifname "lan0.250" counter jump GUEST-WIREGUARD iifname "lan0.250" counter return iifname "ctr0" counter jump CONTAINERS-WIREGUARD iifname "ctr0" counter return iifname "wan0" counter jump WAN-WIREGUARD iifname "wan0" counter return counter drop comment "ZONE_TO_WIREGUARD default-action drop" } chain ZONE_TO_WAN { iifname "wan0" counter return iifname "lan0" counter jump LAN0-WAN iifname "lan0" counter return iifname "lan0.50" counter jump HOME-WAN iifname "lan0.50" counter return iifname "lan0.69" counter jump IOT-WAN iifname "lan0.69" counter return iifname "lan0.200" counter jump SERVER-WAN iifname "lan0.200" counter return iifname "lan0.250" counter jump GUEST-WAN iifname "lan0.250" counter return iifname "ctr0" counter jump CONTAINERS-WAN iifname "ctr0" counter return iifname "wg0" counter jump WIREGUARD-WAN iifname "wg0" counter return counter drop comment "ZONE_TO_WAN default-action drop" } chain ZONE_FROM_LOCAL { oifname "lo" counter return oifname "lan0" counter jump LOCAL-LAN0 oifname "lan0" counter return oifname "lan0.50" counter jump LOCAL-HOME oifname "lan0.50" counter return oifname "lan0.69" counter jump LOCAL-IOT oifname "lan0.69" counter return oifname "lan0.200" counter jump LOCAL-SERVER oifname "lan0.200" counter return oifname "lan0.250" counter jump LOCAL-GUEST oifname "lan0.250" counter return oifname "ctr0" counter jump LOCAL-CONTAINERS oifname "ctr0" counter return oifname "wg0" counter jump LOCAL-WIREGUARD oifname "wg0" counter return oifname "wan0" counter jump LOCAL-WAN oifname "wan0" counter return counter drop comment "ZONE_FROM_LOCAL default-action drop" } ================================================ FILE: system/hosts/budimanjojo-firewall/_modules/firewall/config/zone-rules.nft ================================================ ################# # ZONE_TO_LOCAL # ################# chain LAN0-LOCAL { udp dport 123 counter return comment "allow access to ntp server" meta l4proto { tcp, udp } th dport { 53, 853 } ip daddr @dns_server counter return comment "allow access to dns server" ct state invalid counter log prefix "[LAN0-LOCAL-invalid-D]" drop comment "drop invalid" counter log prefix "[LAN0-LOCAL-default-D]" drop comment "default-action drop" } chain HOME-LOCAL { udp dport 123 counter return comment "allow access to ntp server" meta l4proto { tcp, udp } th dport { 53, 853 } ip daddr @dns_server counter return comment "allow access to dns server" ip saddr @trusted_devices tcp dport 22 counter return comment "allow access to ssh server from trusted_devices" ip saddr @trusted_devices meta l4proto icmp counter return comment "allow icmp request from trusted_devices" ct state invalid counter log prefix "[HOME-LOCAL-invalid-D]" drop comment "drop invalid" counter log prefix "[HOME-LOCAL-default-D]" drop comment "default-action drop" } chain IOT-LOCAL { udp dport 123 counter return comment "allow access to ntp server" meta l4proto { tcp, udp } th dport { 53, 853 } ip daddr @dns_server counter return comment "allow access to dns server" ct state invalid counter log prefix "[IOT-LOCAL-invalid-D]" drop comment "drop invalid" counter log prefix "[IOT-LOCAL-default-D]" drop comment "default-action drop" } chain SERVER-LOCAL { udp dport 123 counter return comment "allow access to ntp server" meta l4proto { tcp, udp } th dport { 53, 853 } ip daddr @dns_server counter return comment "allow access to dns server" tcp dport 179 counter return comment "allow access to bgp server" udp dport 69 counter return comment "allow access to tftp server" ip saddr @k8s_nodes tcp dport { 80, 3000 } counter return comment "allow access to adguardhome from k8s_nodes" ip saddr @k8s_nodes meta l4proto icmp counter return comment "allow icmp request from k8s_nodes" ip saddr @k8s_nodes tcp dport 9100 counter return comment "allow access to node-exporter from k8s_nodes" ip saddr @k8s_nodes tcp dport 9633 counter return comment "allow access to smartctl-exporter from k8s_nodes" ct state invalid counter log prefix "[SERVER-LOCAL-invalid-D]" drop comment "drop invalid" counter log prefix "[SERVER-LOCAL-default-D]" drop comment "default-action drop" } chain GUEST-LOCAL { udp dport 123 counter return comment "allow access to ntp server" meta l4proto { tcp, udp } th dport { 53, 853 } ip daddr @dns_server counter return comment "allow access to dns server" ct state invalid counter log prefix "[GUEST-LOCAL-invalid-D]" drop comment "drop invalid" counter log prefix "[GUEST-LOCAL-default-D]" drop comment "default-action drop" } chain CONTAINERS-LOCAL { udp dport 123 counter return comment "allow access to ntp server" meta l4proto { tcp, udp } th dport { 53, 853 } ip daddr @dns_server counter return comment "allow access to dns server" ct state invalid counter log prefix "[CONTAINERS-LOCAL-invalid-D]" drop comment "drop invalid" counter log prefix "[CONTAINERS-LOCAL-default-D]" drop comment "default-action drop" } chain WIREGUARD-LOCAL { udp dport 123 counter return comment "allow access to ntp server" meta l4proto { tcp, udp } th dport { 53, 853 } ip daddr @dns_server counter return comment "allow access to dns server" ip saddr @trusted_devices tcp dport 22 counter return comment "allow access to ssh server from trusted_devices" ct state invalid counter log prefix "[WIREGUARD-LOCAL-invalid-D]" drop comment "drop invalid" counter log prefix "[WIREGUARD-LOCAL-default-D]" drop comment "default-action drop" } chain WAN-LOCAL { ct state invalid counter log prefix "[WAN-LOCAL-invalid-D]" drop comment "drop invalid" counter log prefix "[WAN-LOCAL-default-D]" drop comment "default-action drop" } ################ # ZONE_TO_LAN0 # ################ chain HOME-LAN0 { ip saddr @trusted_devices counter return comment "accept from trusted_devices" ct state invalid counter log prefix "[HOME-LAN0-invalid-D]" drop comment "drop invalid" counter log prefix "[HOME-LAN0-default-D]" drop comment "default-action drop" } chain IOT-LAN0 { ct state invalid counter log prefix "[IOT-LAN0-invalid-D]" drop comment "drop invalid" counter log prefix "[IOT-LAN0-default-D]" drop comment "default-action drop" } chain SERVER-LAN0 { ct state invalid counter log prefix "[SERVER-LAN0-invalid-D]" drop comment "drop invalid" counter log prefix "[SERVER-LAN0-default-D]" drop comment "default-action drop" } chain GUEST-LAN0 { ct state invalid counter log prefix "[GUEST-LAN0-invalid-D]" drop comment "drop invalid" counter log prefix "[GUEST-LAN0-default-D]" drop comment "default-action drop" } chain CONTAINERS-LAN0 { ct state invalid counter log prefix "[CONTAINERS-LAN0-invalid-D]" drop comment "drop invalid" counter log prefix "[CONTAINERS-LAN0-default-D]" drop comment "default-action drop" } chain WIREGUARD-LAN0 { ct state invalid counter log prefix "[WIREGUARD-LAN0-invalid-D]" drop comment "drop invalid" counter log prefix "[WIREGUARD-LAN0-default-D]" drop comment "default-action drop" } chain WAN-LAN0 { ct state invalid counter log prefix "[WAN-LAN0-invalid-D]" drop comment "drop invalid" counter log prefix "[WAN-LAN0-default-D]" drop comment "default-action drop" } ################ # ZONE_TO_HOME # ################ chain LAN0-HOME { ct state invalid counter log prefix "[LAN0-HOME-invalid-D]" drop comment "drop invalid" counter log prefix "[LAN0-HOME-default-D]" drop comment "default-action drop" } chain IOT-HOME { ct state invalid counter log prefix "[IOT-HOME-invalid-D]" drop comment "drop invalid" counter log prefix "[IOT-HOME-default-D]" drop comment "default-action drop" } chain SERVER-HOME { ip saddr @k8s_nodes ip daddr @wall_displays tcp dport 2323 counter return comment "allow access to fullykioskbrowser from k8s_nodes" ip saddr @k8s_nodes tcp dport 9100 counter return comment "allow access to node-exporter from k8s_nodes" ip saddr @k8s_nodes tcp dport 9633 counter return comment "allow access to smartctl-exporter from k8s_nodes" ct state invalid counter log prefix "[SERVER-HOME-invalid-D]" drop comment "drop invalid" counter log prefix "[SERVER-HOME-default-D]" drop comment "default-action drop" } chain GUEST-HOME { ct state invalid counter log prefix "[GUEST-HOME-invalid-D]" drop comment "drop invalid" counter log prefix "[GUEST-HOME-default-D]" drop comment "default-action drop" } chain CONTAINERS-HOME { ct state invalid counter log prefix "[CONTAINERS-HOME-invalid-D]" drop comment "drop invalid" counter log prefix "[CONTAINERS-HOME-default-D]" drop comment "default-action drop" } chain WIREGUARD-HOME { ip saddr @trusted_devices return comment "allow access from trusted_devices" ct state invalid counter log prefix "[WIREGUARD-HOME-invalid-D]" drop comment "drop invalid" counter log prefix "[WIREGUARD-HOME-default-D]" drop comment "default-action drop" } chain WAN-HOME { ct state invalid counter log prefix "[WAN-HOME-invalid-D]" drop comment "drop invalid" counter log prefix "[WAN-HOME-default-D]" drop comment "default-action drop" } ############### # ZONE_TO_IOT # ############### chain LAN0-IOT { ct state invalid counter log prefix "[LAN0-IOT-invalid-D]" drop comment "drop invalid" counter log prefix "[LAN0-IOT-default-D]" drop comment "default-action drop" } chain HOME-IOT { ct state invalid counter log prefix "[HOME-IOT-invalid-D]" drop comment "drop invalid" counter log prefix "[HOME-IOT-default-A]" return comment "default-action accept" } chain SERVER-IOT { ct state invalid counter log prefix "[SERVER-IOT-invalid-D]" drop comment "drop invalid" counter log prefix "[SERVER-IOT-default-A]" return comment "default-action accept" } chain GUEST-IOT { ct state invalid counter log prefix "[GUEST-IOT-invalid-D]" drop comment "drop invalid" counter log prefix "[GUEST-IOT-default-D]" drop comment "default-action drop" } chain CONTAINERS-IOT { ct state invalid counter log prefix "[CONTAINERS-IOT-invalid-D]" drop comment "drop invalid" counter log prefix "[CONTAINERS-IOT-default-D]" drop comment "default-action drop" } chain WIREGUARD-IOT { ip saddr @trusted_devices return comment "allow access from trusted_devices" ct state invalid counter log prefix "[WIREGUARD-IOT-invalid-D]" drop comment "drop invalid" counter log prefix "[WIREGUARD-IOT-default-D]" drop comment "default-action drop" } chain WAN-IOT { ct state invalid counter log prefix "[WAN-IOT-invalid-D]" drop comment "drop invalid" counter log prefix "[WAN-IOT-default-D]" drop comment "default-action drop" } ################## # ZONE_TO_SERVER # ################## chain LAN0-SERVER { tcp dport { 80, 443 } ip daddr @k8s_ingress counter return comment "allow access to k8s_ingress" ct state invalid counter log prefix "[LAN0-SERVER-invalid-D]" drop comment "drop invalid" counter log prefix "[LAN0-SERVER-default-D]" drop comment "default-action drop" } chain HOME-SERVER { tcp dport { 80, 443 } ip daddr @k8s_ingress counter return comment "allow access to k8s_ingress" ip saddr @trusted_devices tcp dport 22 counter return comment "allow access to ssh from trusted_devices" ip saddr @trusted_devices ip daddr @k8s_nodes counter return comment "allow access to k8s nodes from trusted_devices" ip saddr @trusted_devices tcp dport 50000 counter return comment "allow access to talos api port from trusted_devices" ct state invalid counter log prefix "[HOME-SERVER-invalid-D]" drop comment "drop invalid" counter log prefix "[HOME-SERVER-default-D]" drop comment "default-action drop" } chain IOT-SERVER { ip saddr @broadlink_plugs ip daddr @k8s_nodes counter return comment "allow broadlink_plugs to access k8s_nodes" ip saddr @android_livingroom_tv tcp dport { 80, 443 } ip daddr @k8s_ingress return comment "allow access to k8s_ingress from android_livingroom_tv" ct state invalid counter log prefix "[IOT-SERVER-invalid-D]" drop comment "drop invalid" counter log prefix "[IOT-SERVER-default-D]" drop comment "default-action drop" } chain GUEST-SERVER { ct state invalid counter log prefix "[GUEST-SERVER-invalid-D]" drop comment "drop invalid" counter log prefix "[GUEST-SERVER-default-D]" drop comment "default-action drop" } chain CONTAINERS-SERVER { ct state invalid counter log prefix "[CONTAINERS-SERVER-invalid-D]" drop comment "drop invalid" counter log prefix "[CONTAINERS-SERVER-default-A]" accept comment "default-action accept" } chain WIREGUARD-SERVER { tcp dport { 80, 443 } ip daddr @k8s_ingress counter return comment "allow access to k8s_ingress" ip saddr @trusted_devices return comment "allow access from trusted_devices" ct state invalid counter log prefix "[WIREGUARD-SERVER-invalid-D]" drop comment "drop invalid" counter log prefix "[WIREGUARD-SERVER-default-D]" drop comment "default-action drop" } chain WAN-SERVER { ct state invalid counter log prefix "[WAN-SERVER-invalid-D]" drop comment "drop invalid" counter log prefix "[WAN-SERVER-default-D]" drop comment "default-action drop" } ################# # ZONE_TO_GUEST # ################# chain LAN0-GUEST { ct state invalid counter log prefix "[LAN0-GUEST-invalid-D]" drop comment "drop invalid" counter log prefix "[LAN0-GUEST-default-D]" drop comment "default-action drop" } chain HOME-GUEST { ct state invalid counter log prefix "[HOME-GUEST-invalid-D]" drop comment "drop invalid" counter log prefix "[HOME-GUEST-default-D]" drop comment "default-action drop" } chain IOT-GUEST { ct state invalid counter log prefix "[IOT-GUEST-invalid-D]" drop comment "drop invalid" counter log prefix "[IOT-GUEST-default-D]" drop comment "default-action drop" } chain SERVER-GUEST { ct state invalid counter log prefix "[SERVER-GUEST-invalid-D]" drop comment "drop invalid" counter log prefix "[SERVER-GUEST-default-D]" drop comment "default-action drop" } chain CONTAINERS-GUEST { ct state invalid counter log prefix "[CONTAINERS-GUEST-invalid-D]" drop comment "drop invalid" counter log prefix "[CONTAINERS-GUEST-default-D]" drop comment "default-action drop" } chain WIREGUARD-GUEST { ct state invalid counter log prefix "[WIREGUARD-GUEST-invalid-D]" drop comment "drop invalid" counter log prefix "[WIREGUARD-GUEST-default-D]" drop comment "default-action drop" } chain WAN-GUEST { ct state invalid counter log prefix "[WAN-GUEST-invalid-D]" drop comment "drop invalid" counter log prefix "[WAN-GUEST-default-D]" drop comment "default-action drop" } ###################### # ZONE_TO_CONTAINERS # ###################### chain LAN0-CONTAINERS { ip daddr @omada_controller udp dport @omada_udp counter return comment "accept omada_controller udp ports" ip daddr @omada_controller tcp dport @omada_tcp counter return comment "accept omada_controller tcp ports" ct state invalid counter log prefix "[LAN0-CONTAINERS-invalid-D]" drop comment "drop invalid" counter log prefix "[LAN0-CONTAINERS-default-A]" return comment "default-action accept" } chain HOME-CONTAINERS { ip saddr @trusted_devices counter return comment "allow access from trusted_devices" ct state invalid counter log prefix "[HOME-CONTAINERS-invalid-D]" drop comment "drop invalid" counter log prefix "[HOME-CONTAINERS-default-D]" drop comment "default-action drop" } chain IOT-CONTAINERS { ct state invalid counter log prefix "[IOT-CONTAINERS-invalid-D]" drop comment "drop invalid" counter log prefix "[IOT-CONTAINERS-default-D]" drop comment "default-action drop" } chain SERVER-CONTAINERS { ct state invalid counter log prefix "[SERVER-CONTAINERS-invalid-D]" drop comment "drop invalid" counter log prefix "[SERVER-CONTAINERS-default-A]" return comment "default-action accept" } chain GUEST-CONTAINERS { tcp dport 8088 ip daddr @omada_controller counter return comment "allow access to guest portal" ct state invalid counter log prefix "[GUEST-CONTAINERS-invalid-D]" drop comment "drop invalid" counter log prefix "[GUEST-CONTAINERS-default-D]" drop comment "default-action drop" } chain WIREGUARD-CONTAINERS { ip saddr @trusted_devices return comment "allow access from trusted_devices" ct state invalid counter log prefix "[WIREGUARD-CONTAINERS-invalid-D]" drop comment "drop invalid" counter log prefix "[WIREGUARD-CONTAINERS-default-D]" drop comment "default-action drop" } chain WAN-CONTAINERS { ct state invalid counter log prefix "[WAN-CONTAINERS-invalid-D]" drop comment "drop invalid" counter log prefix "[WAN-CONTAINERS-default-D]" drop comment "default-action drop" } ##################### # ZONE_TO_WIREGUARD # ##################### chain LAN0-WIREGUARD { ct state invalid counter log prefix "[LAN0-WIREGUARD-invalid-D]" drop comment "drop invalid" counter log prefix "[LAN0-WIREGUARD-default-D]" drop comment "default-action drop" } chain HOME-WIREGUARD { ip saddr @trusted_devices tcp dport 22 counter return comment "allow access to ssh from trusted_devices" ip saddr @trusted_devices ip daddr 10.10.10.10 tcp dport 8080 counter return comment "allow access to seedbox webuiPort from trusted_devices" tcp dport 5432 ip daddr 10.10.10.13 counter return comment "allow access to postgres database on work-pc" tcp dport 631 ip daddr 10.10.10.13 counter return comment "allow access to printer on work-pc" ct state invalid counter log prefix "[HOME-WIREGUARD-invalid-D]" drop comment "drop invalid" counter log prefix "[HOME-WIREGUARD-default-D]" drop comment "default-action drop" } chain IOT-WIREGUARD { ct state invalid counter log prefix "[IOT-WIREGUARD-invalid-D]" drop comment "drop invalid" counter log prefix "[IOT-WIREGUARD-default-D]" drop comment "default-action drop" } chain SERVER-WIREGUARD { meta l4proto icmp counter return comment "allow icmp request" tcp dport 22 counter return comment "allow access to ssh" ct state invalid counter log prefix "[SERVER-WIREGUARD-invalid-D]" drop comment "drop invalid" counter log prefix "[SERVER-WIREGUARD-default-D]" drop comment "default-action drop" } chain GUEST-WIREGUARD { ct state invalid counter log prefix "[GUEST-WIREGUARD-invalid-D]" drop comment "drop invalid" counter log prefix "[GUEST-WIREGUARD-default-D]" drop comment "default-action drop" } chain CONTAINERS-WIREGUARD { ct state invalid counter log prefix "[CONTAINERS-WIREGUARD-invalid-D]" drop comment "drop invalid" counter log prefix "[CONTAINERS-WIREGUARD-default-D]" drop comment "default-action drop" } chain WAN-WIREGUARD { ct state invalid counter log prefix "[WAN-WIREGUARD-invalid-D]" drop comment "drop invalid" counter log prefix "[WAN-WIREGUARD-default-D]" drop comment "default-action drop" } ############### # ZONE_TO_WAN # ############### chain LAN0-WAN { counter return comment "default-action accept" } chain HOME-WAN { counter return comment "default-action accept" } chain IOT-WAN { counter return comment "default-action accept" } chain SERVER-WAN { counter return comment "default-action accept" } chain GUEST-WAN { counter return comment "default-action accept" } chain CONTAINERS-WAN { counter return comment "default-action accept" } chain WIREGUARD-WAN { counter return comment "default-action accept" } ################### # ZONE_FROM_LOCAL # ################### chain LOCAL-LAN0 { ct state invalid counter log prefix "[LOCAL-LAN0-invalid-D]" drop comment "drop invalid" counter log prefix "[LOCAL-LAN0-default-D]" drop comment "default-action drop" } chain LOCAL-HOME { ct state invalid counter log prefix "[LOCAL-HOME-invalid-D]" drop comment "drop invalid" counter log prefix "[LOCAL-HOME-default-D]" drop comment "default-action drop" } chain LOCAL-IOT { ct state invalid counter log prefix "[LOCAL-IOT-invalid-D]" drop comment "drop invalid" counter log prefix "[LOCAL-IOT-default-D]" drop comment "default-action drop" } chain LOCAL-SERVER { tcp dport 179 counter return comment "allow access to bgp server" tcp dport 6000 ip daddr @k8s_vector_aggregator counter return comment "allow access to k8s_vector_aggregator" tcp dport 9000 ip daddr @k8s_minio counter return comment "allow access to k8s_minio" tcp dport 2049 ip daddr 192.168.200.30 counter return comment "allow access to NFS server" ct state invalid counter log prefix "[LOCAL-SERVER-invalid-D]" drop comment "drop invalid" counter log prefix "[LOCAL-SERVER-default-D]" drop comment "default-action drop" } chain LOCAL-GUEST { ct state invalid counter log prefix "[LOCAL-GUEST-invalid-D]" drop comment "drop invalid" counter log prefix "[LOCAL-GUEST-default-D]" drop comment "default-action drop" } chain LOCAL-CONTAINERS { ct state invalid counter log prefix "[LOCAL-CONTAINERS-invalid-D]" drop comment "drop invalid" counter log prefix "[LOCAL-CONTAINERS-default-D]" drop comment "default-action drop" } chain LOCAL-WIREGUARD { ct state invalid counter log prefix "[LOCAL-WIREGUARD-invalid-D]" drop comment "drop invalid" counter log prefix "[LOCAL-WIREGUARD-default-D]" drop comment "default-action drop" } chain LOCAL-WAN { counter return comment "default-action accept" } ================================================ FILE: system/hosts/budimanjojo-firewall/_modules/firewall/default.nix ================================================ { ... }: { boot.kernel.sysctl = { "net.ipv4.conf.all.forwarding" = true; "net.ipv6.conf.all.forwarding" = false; }; networking = { firewall.enable = false; nat.enable = false; nftables = { enable = true; flushRuleset = true; tables = { home_nat = { family = "ip"; content = '' chain PREROUTING { type nat hook prerouting priority dstnat; policy accept; iifname { lan0.69, lan0.250 } meta l4proto { tcp, udp } th dport 53 ip daddr != 192.168.10.1 counter dnat to 192.168.10.1:53 comment "force DNS for IOT and GUEST VLAN" iifname { lan0, lan0.50, lan0.69, lan0.200, lan0,250, wg0 } udp dport 123 ip daddr != 192.168.10.1 counter dnat to 192.168.10.1:123 comment "force NTP for all interfaces" } chain POSTROUTING { type nat hook postrouting priority srcnat; policy accept; oifname "wan0" ip daddr 0.0.0.0/0 counter masquerade comment "outbound will use the public IP so I can browse internet" } ''; }; home_ip_filter = { family = "ip"; content = '' chain STATE_POLICY { ct state established counter accept ct state related counter accept return } ${builtins.readFile ./config/sets.nft} ${builtins.readFile ./config/zone-rules.nft} ${builtins.readFile ./config/zone-directions.nft} ''; }; home_ip6_filter = { family = "ip6"; content = '' chain STATE_POLICY { ct state established counter accept ct state related counter accept return } chain ZONE_INPUT { type filter hook input priority filter + 1; policy accept; jump STATE_POLICY iifname "lo" counter return counter drop comment "default-action drop" } # ZONE_FORWARD is disabled for ipv6 in sysctl above chain ZONE_OUTPUT { type filter hook output priority filter + 1; policy accept; jump STATE_POLICY oifname "lo" counter return counter drop comment "default-action drop" } ''; }; }; }; }; } ================================================ FILE: system/hosts/budimanjojo-firewall/_modules/network.nix ================================================ { ... }: { networking = { domain = "home.arpa"; nameservers = [ "1.1.1.1" "8.8.8.8" ]; useDHCP = false; }; systemd.network = { enable = true; wait-online = { anyInterface = false; ignoredInterfaces = [ "wan0" "wan1" "lan1" "ctr0" "wg0" ]; }; links = { # rename all interface names to be easier to identify "10-wan0" = { matchConfig.Path = "pci-0000:01:00.0"; linkConfig.Name = "wan0"; }; "10-wan1" = { matchConfig.Path = "pci-0000:02:00.0"; linkConfig.Name = "wan1"; }; "10-lan0" = { matchConfig.Path = "pci-0000:03:00.0"; linkConfig.Name = "lan0"; }; "10-lan1" = { matchConfig.Path = "pci-0000:04:00.0"; linkConfig.Name = "lan1"; }; }; netdevs = { # VLANs "20-lan0.50" = { netdevConfig = { Name = "lan0.50"; Description = "HOME"; Kind = "vlan"; }; vlanConfig.Id = 50; }; "20-lan0.69" = { netdevConfig = { Name = "lan0.69"; Description = "IOT"; Kind = "vlan"; }; vlanConfig.Id = 69; }; "20-lan0.200" = { netdevConfig = { Name = "lan0.200"; Description = "SERVER"; Kind = "vlan"; }; vlanConfig.Id = 200; }; "20-lan0.250" = { netdevConfig = { Name = "lan0.250"; Description = "GUEST"; Kind = "vlan"; }; vlanConfig.Id = 250; }; }; networks = { # Disabled interfaces "30-wan1" = { matchConfig.Name = "wan1"; networkConfig.ConfigureWithoutCarrier = true; linkConfig.ActivationPolicy = "always-down"; }; "30-lan1" = { matchConfig.Name = "lan1"; networkConfig.ConfigureWithoutCarrier = true; linkConfig.ActivationPolicy = "always-down"; }; # WAN0 "30-wan0" = { matchConfig.Name = "wan0"; networkConfig.DHCP = "yes"; linkConfig = { MTUBytes = "1500"; RequiredForOnline = "routable"; }; }; # LAN0 "30-lan0" = { matchConfig.Name = "lan0"; address = [ "192.168.10.1/24" ]; linkConfig.RequiredForOnline = "carrier"; vlan = [ "lan0.50" # HOME "lan0.69" # IOT "lan0.200" # SERVER "lan0.250" # GUEST ]; }; # HOME VLAN "30-lan0.50" = { matchConfig.Name = "lan0.50"; address = [ "192.168.50.1/24" ]; linkConfig.RequiredForOnline = "routable"; }; # IOT VLAN "30-lan0.69" = { matchConfig.Name = "lan0.69"; address = [ "192.168.69.1/24" ]; linkConfig.RequiredForOnline = "routable"; }; # SERVER VLAN "30-lan0.200" = { matchConfig.Name = "lan0.200"; address = [ "192.168.200.1/24" ]; linkConfig.RequiredForOnline = "routable"; }; # GUEST VLAN "30-lan0.250" = { matchConfig.Name = "lan0.250"; address = [ "192.168.250.1/24" ]; linkConfig.RequiredForOnline = "routable"; }; }; }; } ================================================ FILE: system/hosts/budimanjojo-firewall/_modules/podman.nix ================================================ { ... }: { virtualisation = { containers.enable = true; oci-containers.backend = "podman"; podman = { enable = true; autoPrune.enable = true; defaultNetwork.settings = { network_interface = "ctr0"; # setting this to true ironically breaks the dns because dns server is not running in the gateway ip # even setting `dns_name_servers` here doesn't work with this set to true dns_enabled = false; subnets = [ { subnet = "10.5.0.0/24"; gateway = "10.5.0.1"; lease_range = { start_ip = "10.5.0.50"; end_ip = "10.5.0.200"; }; } ]; }; }; }; } ================================================ FILE: system/hosts/budimanjojo-firewall/_modules/secret.sops.yaml ================================================ wireguard: privatekey: ENC[AES256_GCM,data:kfz+WjdwfDu158B8IvbsfFFC9XxEhf5e4Y5iwbWSuXHZeYXKo4vfAklndho=,iv:JmgiY1+xBY6em0IFdV0X6fciac7sAFMuGtkNVICqPWc=,tag:axHQxloK++lEH8RFjkK4qw==,type:str] sops: kms: [] gcp_kms: [] azure_kv: [] hc_vault: [] age: - recipient: age1zeqkpfz7e3s207ynea0z0auc0mrct0pc7w4sh6j3d0c4qac3dahqj9ufdg enc: | -----BEGIN AGE ENCRYPTED FILE----- YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBEZFFESHd4aXJQbG9kTVNN ekhpYmRnUWJuSk5PZ3N2S1dYSVl6U1UxbndJCmFmdzVqVjZGVzdhdHozOUpNVFFV eWZyYlByRDFUZk9KalRvSjZPWTBPYlEKLS0tIGtTZTNLUVNpOTdHLzdISFlpMFU5 SHl3ZVF4T0NmdmxqeWp4a2N0SjFGVEkKFrRXfAPARjvFhcYN2IQqeidxGQAOrbMi iY5qnbzfGuOlp78Z5QXmktFe2SkzNh+gxMvt7SzvjsZVpHQoOgrCKQ== -----END AGE ENCRYPTED FILE----- - recipient: age1tdwlq9y4jgejkhasqwynw5uaen9xwatcvr52l70trsdxkeyvlesqjnh7l8 enc: | -----BEGIN AGE ENCRYPTED FILE----- YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB3Uis1M2QvOUkvcmpyNXlW VWRWL1RnUlVGS3liWFltSUk0bVpHdkkzblU4CmpxU3Y1bmtzTlpvdDF0RGh2U3VQ RHRydnZDUmhiZkhKN1o2MWxOQlhPancKLS0tIEJJT0tWYWNFUTl1WkhKSElRcm5T SUJGOUVSZW9ENkRCcTRWZjI2c2FOQUEKlm2abH3M0cRJCN5wuhXBKjmmMLp/OLPR PkOxTPzKZtBvnYpZ7ODorLV+0TXekJYYbd43J1phyIlSmPsSlCzI5w== -----END AGE ENCRYPTED FILE----- - recipient: age16p3zls5n0jks6amszwcuaqgl5dyuyf8k8wgeyrw562s5s88xtq3qq046fh enc: | -----BEGIN AGE ENCRYPTED FILE----- YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBUamx1dUh4V2pUSjVyNnRu Q21WM3NDaU95MFRHSlFmaVp0WEVOc3NSK1JVClA4RHAyZ3ZuZjhBMk5hY05yYnZX N3VJcW5iRnk0OWgxVXArb0RGOGZaMTgKLS0tIGg5aW0zNkNud3ROemVwbnA3NndI cVk2T1ZXTGtOVEJUb0JVQXowNlNoa0EK87Zdn7rEGTRl291rLxqe655gGgYJOEJ9 Fr0q0i0fq5NyrQtXFAisYOCvtLnfXT5dsCf5wAEMyLg3wpG1gSr3lQ== -----END AGE ENCRYPTED FILE----- - recipient: age1v52mx8gs4ephprep0wcc4j3fvvprppvs9vktf2p24yv52sqsf33sd5crk9 enc: | -----BEGIN AGE ENCRYPTED FILE----- YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBMdkIwaDB0VEg3RlFTTi82 QW9KQ2JUSVo3dElwUldnNnVISFo1QkVsK0JjCm1YUldLOFpRTmJKNy9wNDBrc3Nz RXo0V2FVM3d0dFF3WGVuSjRSMDBBTm8KLS0tIDVQVnFod3pJWVd2bHY5NjBacjJu d3lUZ3FlNXluYXR3TTN2Y3NSK0JQZjgKVM87AZrFUatOBODRpzRhE2V5AZp/wTOK 3rD6lPRw19JPazNWFBL7GMTIjGfyVNINcSMzpYHbL3Hzzi5z4/TM7w== -----END AGE ENCRYPTED FILE----- - recipient: age1k8ufac2s0gs6nh0xsfavafz062vd36petmyv6nwmg00z4a7s4gnsjtd837 enc: | -----BEGIN AGE ENCRYPTED FILE----- YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA3c0RKMU93SjcyNndYcjZo eVJJS2JYVDB0MElOa0IwTVVaeVN1blJrcml3Ci9LSy9HUUg1UUVyNDRGTkFKQkVs OE84cXdTUi8vTXk4N3BGWCtPQXJ1aDQKLS0tIEFGNVVxUzNLMUFyOTVndGNLSjVy V243ZUdBT1BNQmUvdEdtc080UVlLVG8KXbW/9iga/O9Sbl2zPWW8fvA1Bf4vBNk5 7RjJmCn+8hdAb1xT5D8+91HvNUMVLl8JtQDb/VbVf27Ait8F5SmZeA== -----END AGE ENCRYPTED FILE----- - recipient: age1dcsm5awz8ekzchk7gsvndkc4kq65n2wzgavxtqe53vxdsfk9k9pqh3whru enc: | -----BEGIN AGE ENCRYPTED FILE----- YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBqb0hDakdXc2x5MU8wMUhs cnByQ0tmZkdrN25odG5DRXc5STRJUEc0WlRRCkN6NjhnNjdoWDR2VGQ2OUNvcjg1 UFFmM0tHbUovZm4veC96ZzIyZi9tcFkKLS0tIGlLeXNzekw4YnhYZWFpVDhRa0dL Y2hZSjk1VHNTV3lBeFFUTExKMTZWcFUK64I4wjtFm2V4egss9dF0IxVIfhCXUcGL L3iOS1c1M8/lbvLls3EeOP1UDiiy8i4rmddYCRUwXIsbsFKU8LBaug== -----END AGE ENCRYPTED FILE----- lastmodified: "2024-11-19T05:20:23Z" mac: ENC[AES256_GCM,data:etRAnN6AQibybMNEXiQ/v86/gm5YcStDiQSMw5b3XQ07w54llUklz0X7ALp65byPC48LFMgWpOF8w/YTWoJuAd7byxxx/iUgBZUMsHXT/c47Se/cgr0I4x5lZS8bsI3tmyNeEpmg2qndVgt+XgU3gFL/60mrUwNyzkDTJKIaFC0=,iv:g3n9TNg7QD1DmMH1oPoQm7ae9m0PKbCEIx/Xa2tPPv4=,tag:osIKhCCp+3QZ03Hzpo83LQ==,type:str] pgp: [] unencrypted_suffix: _unencrypted version: 3.8.1 ================================================ FILE: system/hosts/budimanjojo-firewall/_modules/services/adguardhome/default.nix ================================================ { config, lib, pkgs, ... }: let adguardUser = "adguardhome"; in { sops.secrets = { "adguardhome/password" = { sopsFile = ./secret.sops.yaml; owner = adguardUser; restartUnits = [ "adguardhome.service" ]; }; "adguardhome/env" = { sopsFile = ./secret.sops.yaml; owner = adguardUser; restartUnits = [ "adguardhome.service" ]; }; }; # add user, needed to access the secret users = { users.${adguardUser} = { isSystemUser = true; group = adguardUser; }; groups.${adguardUser} = { }; }; systemd.services.adguardhome = { # see: https://github.com/AdguardTeam/AdGuardHome/issues/4880 wants = [ "time-sync.target" ]; after = [ "time-sync.target" ]; serviceConfig = { EnvironmentFile = "${config.sops.secrets."adguardhome/env".path}"; User = adguardUser; }; # we do `envsubst` to substitute string like ${VAR} using the environment secrets # and also `bcrypt` the substituted unencrypted password with `htpasswd` as per config requirement preStart = lib.mkAfter '' ${pkgs.envsubst}/bin/envsubst -no-unset -i "$STATE_DIRECTORY/AdGuardHome.yaml" -o "$STATE_DIRECTORY/AdGuardHome.yaml" PASSWORD=$(cat ${config.sops.secrets."adguardhome/password".path}) HASHED_PASSWORD=$(${pkgs.apacheHttpd}/bin/htpasswd -nbB "" $PASSWORD | cut -c 2-) ${pkgs.gnused}/bin/sed -i "s,ADGUARDHOMEPASSWORD,$HASHED_PASSWORD," "$STATE_DIRECTORY/AdGuardHome.yaml" ''; }; services.adguardhome = { enable = true; host = "192.168.10.1"; port = 3000; mutableSettings = true; settings = { schema_version = 29; # the default is pkgs.adguardhome.schema_version users = [ { name = "budiman"; password = "ADGUARDHOMEPASSWORD"; # placeholder } ]; auth_attempts = 5; block_auth_min = 15; theme = "auto"; http = { pprof.enabled = false; session_ttl = "720h"; }; dhcp.enabled = false; dns = { bind_hosts = [ "192.168.10.1" ]; port = 53; anonymize_client_ip = false; ratelimit = 20; refuse_any = true; upstream_dns = [ "[/home.arpa/]192.168.10.1:8853" "[/internal.\${SECRET_DOMAIN_1}/]192.168.10.1" "[/external.\${SECRET_DOMAIN_1}/]192.168.10.1" "quic://dns.adguard-dns.com" ]; bootstrap_dns = [ "1.1.1.1" "8.8.8.8" "2606:4700:4700::1111" "2001:4860:4860::8888" ]; upstream_mode = "parallel"; blocked_hosts = [ "version.bind" "id.server" "hostname.bind" ]; trusted_proxies = [ "127.0.0.0/8" "::1/128" ]; enable_dnssec = true; edns_client_subnet = { custom_ip = ""; enabled = true; use_custom = false; }; handle_ddr = true; use_private_ptr_resolvers = true; local_ptr_upstreams = [ "192.168.10.1:8853" ]; }; filtering = { blocking_mode = "default"; protection_enabled = true; safe_search.enabled = false; safebrowsing_enabled = true; parental_enabled = false; filtering_enabled = true; filters_update_interval = 24; }; tls.enabled = false; querylog = { interval = "168h"; enabled = true; }; statistics = { enabled = true; interval = "24h"; }; filters = let urls = [ { name = "AdGuard DNS filter"; url = "https://adguardteam.github.io/AdGuardSDNSFilter/Filters/filter.txt"; } { name = "IDN: ABPindo"; url = "https://raw.githubusercontent.com/ABPindo/indonesianadblockrules/master/subscriptions/abpindo.txt"; } { name = "GoodbyAds"; url = "https://raw.githubusercontent.com/jerryn70/GoodbyeAds/master/Hosts/GoodbyeAds.txt"; } ]; buildList = id: url: { enabled = true; inherit id; inherit (url) name url; }; in lib.imap1 buildList urls; user_rules = [ "@@||s2.youtube.com^" "@@||graph.facebook.com^" "@@||i.instagram.com^" "@@||fonts.gstatic.com^" "@@||click.redditmail.com^" "@@||aypbpr.tokopedia.com^" "@@||plenty.vidio.com^" "@@||i.sgsnssdk.com^" ]; clients = { runtime_sources = { whois = true; arp = true; rdns = true; dhcp = true; hosts = true; }; persistent = [ { name = "GUEST"; uid = "2e755b04-5463-4f69-93a2-6d620f42c6e1"; tags = [ ]; ids = [ "192.168.250.0/24" ]; use_global_settings = true; use_global_blocked_services = true; blocked_services.ids = [ ]; upstreams = [ "quic://dns.adguard-dns.com" ]; } { name = "IOT"; uid = "7a8b3ec5-5915-4bdc-b368-92c1c123bfb1"; tags = [ ]; ids = [ "192.168.69.0/24" ]; use_global_settings = false; use_global_blocked_services = true; blocked_services.ids = [ ]; upstreams = [ "1.1.1.1" "8.8.8.8" ]; } { name = "Android TV"; uid = "3635575c-a9bb-4303-a2e2-75ffd1d0a967"; tags = [ "device_tv" ]; ids = [ "192.168.69.30" ]; use_global_settings = true; use_global_blocked_services = true; blocked_services.ids = [ ]; } { name = "Children"; uid = "27337a9f-d874-497c-9037-39519f1384a5"; tags = [ "user_child" ]; ids = [ "192.168.10.12" "192.168.10.13" "10.10.0.4" ]; use_global_settings = false; blocked_services.ids = [ "reddit" "tiktok" "facebook" "instagram" "snapchat" "tinder" ]; upstreams = [ ]; safe_search = { enabled = true; bing = true; duckduckgo = true; google = true; pixabay = true; yandex = true; youtube = true; }; filtering_enabled = true; parental_enabled = true; use_global_blocked_services = false; ignore_querylog = false; ignore_statistics = false; safebrowsing_enabled = true; } ]; }; }; }; } ================================================ FILE: system/hosts/budimanjojo-firewall/_modules/services/adguardhome/secret.sops.yaml ================================================ adguardhome: password: ENC[AES256_GCM,data:2Ksc+o8/FtAP4fk=,iv:+IqBK/hH9/Mdok3D6MZ6IywchOUx1VOv0nHpgTsBGj4=,tag:HqA+KyhdLAwLBtU8u1Uchw==,type:str] env: ENC[AES256_GCM,data:r94YfggYU+nSfKCbMWSPeJ5mMM5zE2oIluEx32QvcOX8Yx2kU5Be0vHDZ2TBQZzb/NJ4OTX1ef2Txtbhp7dI/t1qWzWwQ2o6FOO+MgNdTBTcCjNekZvxpPoiw8mFPSWx3yaFaPkLabjn7+XjYNGcXAlzSWPq28o=,iv:pHL7YBPAy4pEzKz9FULZSl62To7dONbunCyzvI9LCiQ=,tag:p6FVLQZZMZVbmOzGJ/adwA==,type:str] sops: kms: [] gcp_kms: [] azure_kv: [] hc_vault: [] age: - recipient: age1zeqkpfz7e3s207ynea0z0auc0mrct0pc7w4sh6j3d0c4qac3dahqj9ufdg enc: | -----BEGIN AGE ENCRYPTED FILE----- YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAwczVkQzlNaWl2MFBXdHE2 M2RDajVqTGh6ZXpNZE50VlBuaUlvVllBQWlZCklXMFVHVTBXVnNpYklmaTNqSTF5 R3lSa21OWnE3TVMwNHY4Vzk0QVdkUDgKLS0tIFZ2cElLV2ExNjcxbTBkaDZzcTVm Yi9mNURPV05hVC96dzZhSUoxMlB1dWsKwLOTlyTnQzIkZPL3gZ36GyMGxM3v12vJ 0qGnpEtsA2zjU9brGNW9n71NsuROCCohumaH9rNXJv4N0YJNPuDc+A== -----END AGE ENCRYPTED FILE----- - recipient: age1tdwlq9y4jgejkhasqwynw5uaen9xwatcvr52l70trsdxkeyvlesqjnh7l8 enc: | -----BEGIN AGE ENCRYPTED FILE----- YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBMczRYNzlpMUJ2eW5tMDF6 UnlPMUNlTmZ1YnZSMWZlQVYrNG5TNnJOUGdNClVjSzdVOXpWOURCem9aNVMwRmdL bUZLVjEwaWlINGxLT01MYVJzeHRqdWcKLS0tIHhEZUVUOEV6cVl3alpjK25KK3RX S0QyQkhtdXZHZTkzRzRLdUd0dG1UdHcKikeZfd2zZCYmbqdCumNcR2VTRt0UPp2j qoOcRyqh/BAiKD7paD6LGtSltAYm+J7eFJMCb5J3+YuL9mhT+oUnXA== -----END AGE ENCRYPTED FILE----- - recipient: age16p3zls5n0jks6amszwcuaqgl5dyuyf8k8wgeyrw562s5s88xtq3qq046fh enc: | -----BEGIN AGE ENCRYPTED FILE----- YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBGaVluV0xZdS9lMGNGbEVD Yjl5blJUUEtNNjNkSmpySnFXcFJla2tRYzFjCmkxMUZnQmQvNVNZa2g3Yy9qcWpG VFoxQ3JqTUs2RHgvbjFvdEwrY0dKU0kKLS0tIHY0MTh4d2tvY1FFKzNtTjJabUJN Z0lUMk5vcFlDVHM0ZXFkYzA1aU00a3MKUnBHocxa4bCTma8ML7Mssd/ItyW5lOGq Dnt/o1EWtKOE9Z6XptUJ2mfb+lsvWPrhbMchEn59fdNV7xxyncrT4g== -----END AGE ENCRYPTED FILE----- - recipient: age1v52mx8gs4ephprep0wcc4j3fvvprppvs9vktf2p24yv52sqsf33sd5crk9 enc: | -----BEGIN AGE ENCRYPTED FILE----- YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBUbFA0a1RxYVpMQXl5ME1z cXpoWDNTWU5PQnpZalJxTFliaFJId2hlU0g0CkFtTVNiTWp2eFJtdk5oQ3ByNGRJ ZEJZVWU0VjhSNERGR0E0RVRoY2xic2cKLS0tIHpoWVlpU0x4NkRIbjVHWUVpWTFx TUNKbytGODlBSk54MnFwRUp4Z3l1T2sKw5PUFIVLlQLfD/YPG9A2EvQ/Qu1VGijm eypGBUCKsgzPTt0CjkasVZdNod3GLaFLXTyzlYL0DB3UMbU75c4Jag== -----END AGE ENCRYPTED FILE----- - recipient: age1k8ufac2s0gs6nh0xsfavafz062vd36petmyv6nwmg00z4a7s4gnsjtd837 enc: | -----BEGIN AGE ENCRYPTED FILE----- YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBQbHlmeHYzK0F0c2VLR09l VDBOalRLOUVHZEEvUnVkazV0bnN6V3dSOUhnCmlMQzhFWjNIbFROOWQ1L2I5ZjM3 MSs1SThaUjBWbHRVODJKMm1jd3BiemMKLS0tICtaOHQvb3hQdFVIdHdxVzdMRzFu WVljaG9KZnJiaDBVaUhJWWRDaEhMdEUKjOulyHID1DeXZlOkaEpwxoi7bVBO1I6B Y9U0z2+sVu8v8B4JcBAO0uvje+FQ7vJsifhgs+NMpuzDvwCWHQu+lA== -----END AGE ENCRYPTED FILE----- - recipient: age1dcsm5awz8ekzchk7gsvndkc4kq65n2wzgavxtqe53vxdsfk9k9pqh3whru enc: | -----BEGIN AGE ENCRYPTED FILE----- YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBjTm1NSEpyUnhMTHA3WWM3 K0tHR3g3Z1crdjBpcVFVZWh2ZXlvZUkrRjNNCjRYSVFsckw1a1k5UFN4K2NZRXJv YzBNdzM5anprL2l5dnQvRFBOZnpHNTgKLS0tIGdHbWRsU1ZVUUlDbm5nSm9WaDhU Qi82SHIzUFdYS2FBWEJZY3VobDZUaW8K6JEiqewLluyyfHaT3BT1Pl35uNlFaGws +vuwgSkOeGrJYH1zzJ9OIJvSL/c3ee3GTg1Bz1r64nfnYjFRwAiJpw== -----END AGE ENCRYPTED FILE----- lastmodified: "2024-11-10T05:38:21Z" mac: ENC[AES256_GCM,data:0xgKqW7SWB8CVkxWcWlxIzibNk9DyUv+owdPFw56QNxo5c1Lvuf38/o4uWJOuXS1nnJZuP3oVPcq8dxoBnw2Jlsn4E07lKoZMwC5kdaZIiaBOMmp447E81mh4CrbuOhni94KGaEnSDhzOie5cvlpfqo6Fcpe+sWOosJVZ3b+yPU=,iv:gqgq3S7ps/FaFw7oCVIB+K//uimBzPYYxgH+cH0WPd8=,tag:3Jz0qtuef0lg0zDDnm8RVg==,type:str] pgp: [] unencrypted_suffix: _unencrypted version: 3.8.1 ================================================ FILE: system/hosts/budimanjojo-firewall/_modules/services/chrony/default.nix ================================================ { ... }: { services.chrony = { enable = true; servers = [ "id.pool.ntp.org" ]; serverOption = "iburst"; extraConfig = '' allow 127.0.0.0/8 allow 10.0.0.0/8 allow 172.16.0.0/12 allow 192.168.0.0/16 ''; }; } ================================================ FILE: system/hosts/budimanjojo-firewall/_modules/services/fireqos/default.nix ================================================ { ... }: { services.fireqos = { enable = true; config = '' interface wan0 world-in input rate 200mbit class calls commit 300kbit match ports 3478:3497,16384:16387,16393:16402 class default class torrents match port 50413 interface wan0 world-out output rate 65mbit class calls commit 300kbit match ports 3478:3497,16384:16387,16393:16402 class default class torrents match port 50413 ''; }; systemd.services.fireqos = { after = [ "network-online.target" ]; wants = [ "network-online.target" ]; }; } ================================================ FILE: system/hosts/budimanjojo-firewall/_modules/services/frr/default.nix ================================================ { ... }: { # TODO: everytime networkd is being reconfigured, frr will be broken because networkd deletes nexthops it doesn't manage systemd.network.config.networkConfig.ManageForeignNextHops = false; services.frr = { bgpd.enable = true; config = '' router bgp 65400 bgp router-id 192.168.200.1 no bgp ebgp-requires-policy no bgp network import-check neighbor 192.168.200.21 remote-as 65401 neighbor 192.168.200.21 description kmaster1 neighbor 192.168.200.21 update-source lan0.200 neighbor 192.168.200.22 remote-as 65401 neighbor 192.168.200.22 description kmaster2 neighbor 192.168.200.22 update-source lan0.200 neighbor 192.168.200.23 remote-as 65401 neighbor 192.168.200.23 description kmaster3 neighbor 192.168.200.23 update-source lan0.200 exit ''; }; } ================================================ FILE: system/hosts/budimanjojo-firewall/_modules/services/kea/ddns.nix ================================================ { config, ... }: let keaddnsUser = "kea"; in { # add user, needed to access the secret users = { users.${keaddnsUser} = { isSystemUser = true; group = keaddnsUser; }; groups.${keaddnsUser} = { }; }; sops.secrets."kea/tsig-key" = { sopsFile = ./secret.sops.yaml; owner = keaddnsUser; group = keaddnsUser; }; services.kea = { dhcp4.settings = { dhcp-ddns.enable-updates = true; ddns-replace-client-name = "when-not-present"; ddns-update-on-renew = true; # always update when a lease is renewed, in case I lost the DNS server database ddns-override-client-update = true; # always generate ddns update request ignoring the client's wishes not to ddns-override-no-update = true; # same as above but for different client's wishes ddns-qualifying-suffix = "home.arpa"; }; dhcp-ddns = { enable = true; settings = let pdnsServer = [ { ip-address = "192.168.10.1"; port = 8853; } ]; in { tsig-keys = [ { name = "kea"; algorithm = "hmac-sha512"; secret-file = "${config.sops.secrets."kea/tsig-key".path}"; } ]; forward-ddns = { ddns-domains = [ { name = "home.arpa."; key-name = "kea"; dns-servers = pdnsServer; } ]; }; reverse-ddns = { ddns-domains = [ { name = "168.192.in-addr.arpa."; key-name = "kea"; dns-servers = pdnsServer; } { name = "10.in-addr.arpa"; key-name = "kea"; dns-servers = pdnsServer; } ]; }; }; }; }; } ================================================ FILE: system/hosts/budimanjojo-firewall/_modules/services/kea/default.nix ================================================ { ... }: { imports = [ ./ddns.nix ./dhcp.nix ]; } ================================================ FILE: system/hosts/budimanjojo-firewall/_modules/services/kea/dhcp.nix ================================================ { ... }: let leaseOption = { valid-lifetime = 86400; renew-timer = 43200; # 50% of valid lifetime rebind-timer = 75600; # 87.5% of valid lifetime }; commonDhcpOptions = [ { name = "domain-name-servers"; data = "192.168.10.1"; } { name = "time-servers"; data = "192.168.10.1"; } { name = "domain-name"; data = "home.arpa"; } { name = "domain-search"; data = "home.arpa"; } ]; in { services.kea.dhcp4 = { enable = true; settings = { interfaces-config = { interfaces = [ "lan0" "lan0.50" "lan0.69" "lan0.200" "lan0.250" ]; }; subnet4 = [ ( { id = 1; interface = "lan0"; subnet = "192.168.10.0/24"; pools = [ { pool = "192.168.10.50 - 192.168.10.199"; } ]; option-data = [ { name = "routers"; data = "192.168.10.1"; } { # this allows clients to be discovered by omada-controller name = "capwap-ac-v4"; data = "10.5.0.10"; } ] ++ commonDhcpOptions; } // leaseOption ) ( { id = 2; interface = "lan0.50"; subnet = "192.168.50.0/24"; pools = [ { pool = "192.168.50.50 - 192.168.50.199"; } ]; option-data = [ { name = "routers"; data = "192.168.10.1"; } ] ++ commonDhcpOptions; reservations = [ { hostname = "jojo-poco"; ip-address = "192.168.50.10"; hw-address = "e8:5f:b4:2d:ce:5d"; } { hostname = "lina-samsung"; ip-address = "192.168.50.11"; hw-address = "ae:9c:e8:65:f4:76"; } { hostname = "eunice-tablet"; ip-address = "192.168.50.12"; hw-address = "00:03:f7:bd:70:d0"; } { hostname = "eugene-oneplus"; ip-address = "192.168.50.13"; hw-address = "98:09:cf:0c:70:af"; } { hostname = "firehd-8-livingroom"; ip-address = "192.168.50.40"; hw-address = "40:a9:cf:3b:a3:7d"; } { hostname = "budimanjojo-main"; ip-address = "192.168.50.49"; hw-address = "b4:2e:99:62:8d:06"; } ]; } // leaseOption ) ( { id = 3; interface = "lan0.69"; subnet = "192.168.69.0/24"; pools = [ { pool = "192.168.69.50 - 192.168.69.199"; } ]; option-data = [ { name = "routers"; data = "192.168.10.1"; } ] ++ commonDhcpOptions; reservations = [ { hostname = "broadlink-livingroom-plug"; ip-address = "192.168.69.10"; hw-address = "34:ea:34:79:f0:91"; } { hostname = "broadlink-bedroom-rm4c"; ip-address = "192.168.69.11"; hw-address = "24:df:a7:4f:9a:8e"; } { hostname = "ezviz-bedroom-camera"; ip-address = "192.168.69.21"; hw-address = "a0:ff:0c:9f:A7:7a"; } { hostname = "android-livingroom-tv"; ip-address = "192.168.69.30"; hw-address = "8c:90:2d:cd:00:56"; } ]; } // leaseOption ) ( { id = 4; interface = "lan0.200"; subnet = "192.168.200.0/24"; pools = [ { pool = "192.168.200.50 - 192.168.200.199"; } ]; option-data = [ { name = "routers"; data = "192.168.10.1"; } ] ++ commonDhcpOptions; reservations = [ { hostname = "budimanjojo-nas"; ip-address = "192.168.200.30"; hw-address = "d0:50:99:25:88:91"; } ]; } // leaseOption ) ( { id = 5; interface = "lan0.250"; subnet = "192.168.250.0/24"; pools = [ { pool = "192.168.250.50 - 192.168.250.199"; } ]; option-data = [ { name = "routers"; data = "192.168.10.1"; } ] ++ commonDhcpOptions; } // leaseOption ) ]; }; }; } ================================================ FILE: system/hosts/budimanjojo-firewall/_modules/services/kea/secret.sops.yaml ================================================ kea: tsig-key: ENC[AES256_GCM,data:2nJqnNww0MinOpvJnmYrQuJTM1bQbU1LaohkjpoIRZ64f7Szzr6rFHXaRfVuOmi6w5aVSzQxlG+w23v4Oowjgx6fdkzbrJ5vtEkQS/nDdoQJY8EWPsE8vw==,iv:1eOakicUjRrG3D7HoVlqsuHPO8R+UGqBN3I8kSCUSso=,tag:0IMYOaMzBi32xM1MAptOBA==,type:str] sops: kms: [] gcp_kms: [] azure_kv: [] hc_vault: [] age: - recipient: age1zeqkpfz7e3s207ynea0z0auc0mrct0pc7w4sh6j3d0c4qac3dahqj9ufdg enc: | -----BEGIN AGE ENCRYPTED FILE----- YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBaSWtWK2ZqODNlemxodFRx UHlRVXZGUUlRaUVaR3IvNkhXbk1TRVhJc21rCk9qTC9QQkxFNW1Hd3RIanBvbjdK SVZQZmFiRjcwS3BpQjdEWWJ2b2wwTUkKLS0tIGFRL3ZIdS9CMkVPOUlERENDWDl5 SkNSc0RSdmdOWUdocWxBS3d6VnF5c1UKgRZZXve1WWVGJAylQJ79d7Ousql/8kGM 5IKDkz1ouT/Eipq/x1HSOjxy9fBEdr6RFBgX0quKZdLUTH4yzHUCwA== -----END AGE ENCRYPTED FILE----- - recipient: age1tdwlq9y4jgejkhasqwynw5uaen9xwatcvr52l70trsdxkeyvlesqjnh7l8 enc: | -----BEGIN AGE ENCRYPTED FILE----- YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBoQjk4VFV1MTAzaHBXRk15 cTF1dXNXVmQxY2J4SWpDa0xndXNqblZhRG1RCmRyY2VWaGc0eGppTlB1b3llbDd1 b2lQeU96aWNpT0dUTTJJK2YvaE5qQnMKLS0tIFdtS0p1dmpZRnBQRmlrMWpXamJt WXB2ZlNSVDYyNVJPNWZzSzJIR3llV28KsXEK+i4VRs6qzPQPsQek9Epv8XZFWRub VqiwxICvwHxQZnfTcqGP5+kJTeDKK9xZfU3jAedWN9Cbd7SRvC16nw== -----END AGE ENCRYPTED FILE----- - recipient: age16p3zls5n0jks6amszwcuaqgl5dyuyf8k8wgeyrw562s5s88xtq3qq046fh enc: | -----BEGIN AGE ENCRYPTED FILE----- YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBGZGZTek9BQUFzMkZwcE1V R29VVU5hVjU3TUFmbjU1NUx2cXlnL1krdndFCk5lNWZFWUJSdVluN0dHU3o3dkZy NVlKaEcwR0pjRGJtdEhrUm1CbFk2cUUKLS0tIDYxSXdOL2hHMm11cGc1ZTJXTlhQ R2piZVpIVHcyVkRmK0NGSktOcmNtaTQK7ntSeO3mmidDCTcvFuu4aqfZmbUPPBy6 sXP9ORhec1q17x3xpsjjrQaUu0zvUqWqF8rh7P+G3THeeEnAM/Ph8Q== -----END AGE ENCRYPTED FILE----- - recipient: age1v52mx8gs4ephprep0wcc4j3fvvprppvs9vktf2p24yv52sqsf33sd5crk9 enc: | -----BEGIN AGE ENCRYPTED FILE----- YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBKZ2krdUd2UWJ4Y3J3cm1D dlBJcmZSbHhrTEZzUTJQVnliWU9pOGo5T2c0CktMOTdmcFd3ZHJLRElpVnNCSVJZ RDlPZ21Rd2psb0MvLzFaMmlkQ2wzWGcKLS0tIFZTZW1ZRUx0VndBeGtMQnpZYlB2 a2dPUlVsZkoyNzZpV3o1cmszRmNIbEkK2+bQ1+8VtIAm6dhzwFO57AOW+ibHVawI dliB0wO7/Ec/dFI9ZibdoON9zAHUB0a0+FnYsPAocdsd+881OIUfcQ== -----END AGE ENCRYPTED FILE----- - recipient: age1k8ufac2s0gs6nh0xsfavafz062vd36petmyv6nwmg00z4a7s4gnsjtd837 enc: | -----BEGIN AGE ENCRYPTED FILE----- YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAwK0Y2MFFoL3dBZkVHNXY4 dExkR1pCbnlIdFlLTEFNcW9ZOTVCcGxGMng0Cldnd0VVR09GckFrVTVaYUZWTklQ czdEUFJ4ajQyOVdJKzI1QWtxd1hXVTAKLS0tIEZ3c2xZbGN3Zlc0L05LeG11YzdL ODB2a1RiaitXVDRuUWJCbzJIeWFNSzAKDJoMC4WayAESZrBNraVMSJZYP3uk/aSv sNypRghLSX/H7YDcopHoUeQ8nzOtAxWj6ylNhXFHaRjw3eNp2TqLqw== -----END AGE ENCRYPTED FILE----- - recipient: age1dcsm5awz8ekzchk7gsvndkc4kq65n2wzgavxtqe53vxdsfk9k9pqh3whru enc: | -----BEGIN AGE ENCRYPTED FILE----- YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBsOVdaSncyMmhUQmkzZmdx bzQ4cmU3bWVaLzZJRTRJMDIyR2UrbmxKbnhZCnJTZ3ZubE41SExQZ3NUWUF5cmxn SjZGTnErRUlCL2RDOUFRb1pibW94UU0KLS0tIDd3RDJQMEFIV3JFRVFVa3A5VGZP ZEo0OVFpRGFJT3Z6aXNkTXIvMjVtRncKc3/C2eeN/zfTzVXhTFeg7vTJTTlH+HVm jLa8HYNuSfAIrNiaAiQTBP5RGvedthas8u40nkjcDXTNqBpaPm8y5g== -----END AGE ENCRYPTED FILE----- lastmodified: "2024-12-10T03:10:14Z" mac: ENC[AES256_GCM,data:yLPGIAwDdYBTOvuDveTUowX9A9u3nQ5TaHStY5q4zxL0Z7ZOOHbir9bPlDjSYhCHroMR3yCrqZDGG85OuQhfLf4RnP3AU8nXO0gOz2fXNOfpe4xMuSvklKjncEAXSOBtZ/KPy+xYbU4W2DpuCl4o5NmWvsn3u3+I0O0niuWttH8=,iv:05WNFpUjEaXBsXmmOXze8qDkfEAs7PACZOVO8imt+rE=,tag:brMF5pxGC45ofH4Q59Fj7w==,type:str] pgp: [] unencrypted_suffix: _unencrypted version: 3.8.1 ================================================ FILE: system/hosts/budimanjojo-firewall/_modules/services/omada-controller/default.nix ================================================ { config, ... }: let app = "omada-controller"; dataDir = "/var/lib/omada-controller-data"; logsDir = "/var/lib/omada-controller-logs"; in { sops.secrets."omada-controller/sslkey" = { sopsFile = ./secret.sops.yaml; restartUnits = [ "podman-omada-controller.service" ]; }; systemd.tmpfiles.rules = [ "d ${dataDir} 0755 508 508" "d ${logsDir} 0755 508 508" ]; virtualisation.oci-containers.containers.omada-controller = { image = "docker.io/mbentley/omada-controller:6.2"; extraOptions = [ "--ip=10.5.0.10" "--stop-timeout=30" ]; volumes = [ "${dataDir}:/opt/tplink/EAPController/data:rw" "${logsDir}:/opt/tplink/EAPController/logs:rw" "${config.sops.secrets."omada-controller/sslkey".path}:/cert/tls.key:ro" "${./ssl.crt}:/cert/tls.crt:ro" ]; environment = { TZ = "Asia/Jakarta"; }; }; services.restic.backups = config.lib.mySystem.mkRestic { inherit app; paths = [ "${dataDir}" "${logsDir}" ]; excludePaths = [ "autobackup" ]; }; } ================================================ FILE: system/hosts/budimanjojo-firewall/_modules/services/omada-controller/secret.sops.yaml ================================================ omada-controller: sslkey: ENC[AES256_GCM,data:Rv9uowvCXyB/tNKcdDFyckaiVr0IIi/UZBU6T3/qeM3ZmjdJJcPtlmu2UhsspXp8mfWUhOzHLlRUpAhBm7N+htwVyZXDQfGGpWMwgebgW61fTf63FCm1Tik77vrhNf7iSfKo7PnV+CIwnbrbQgBo54r6ob56RQ5R8qjbFY/8DJitx3JxjPuwMhU8dphlA2R4OtAdYzBdt6Td1eofOiIdIbAhtSdDoy+IIvdzknXS3xy7bBJ/ItrU/PhGD2g3ePErZSvVDXbv+fOBwbfFI33iJfze1TuMk4q0yhNrBRppwJB8SuCM8K4xxmhqOxwxdgomEoyPiHQjAflwJiELtxfsdDzCO4Bs5Z2RWO9jc95OiP3QYAncTvG7nn/Xe/qxezku7P0DnXCG7Nzfu66IrQ//Swr8MTf6tFzAKn58ZNJV5s6+xoWh6PzNwaIsXNRnrOZPF5ul5fh9L8D27Em+4IyGLH1H97QvLKKJK5eFs8P/Nyk4JJOqUmZObAgnPC1lNHr4ZVM6HoUZglFLHwR8ul2LpJW42WNWLjW0Hfn9R87Vmrangv8nNvsSVV/m4ABr6TEvbzXUraWYe3Cl4SY4Q1aHkW3vwBLM1vskO1LYEoTlNNIs0TB6YjEXsZmbbh2+HPcysQ+qLWU9szQfMmy74CKfH7RjLT5nZufINsl8yrzIwhtp6cHfbaK5hB9hmg4lO4rj9Fdqni3cPvaVbC5OpQRL2l5SdYjQyG1CF2kYVPGf7la5P21hdEf9DxM2iOjh8j7wa7lC0IoHen90Z1psrVnzPVq/Hw+++516SA7zzOvdEFvzGWetz8Ir57RalznITVVQ+h2qcMhDOupO26URbC3PyjmQwjB5pnVgfTqUbnN/fc52OSFSGHoU1v9iJNxNvyGvVg0DFlBO5sg8VMCIusrn+qwmMj94gWuwlIWdSMBS3rn7lE/X0kmmWK16FBoMn33zD8by+rUDgqCZzJXdQ5rJk8ryVni/7/KXs0foZXNJCptfXH7rM4WhvywesTQ/mfRlbo1Fy5oQnT2nzoXYYkFcUIpYoqCT/1T7nATL44kDY704EQOC0ydhW14mWS8qhza3dlQOabgIaros27FgIDYGaV6lsGGx1VItV5zVSs/KlWtbPgsJ5nLLQ3Z3GK1YKn3QxZ3P/34BZ3+zxbZXN59Hc2C7WGZ5Qmpg7LiUe9tcKWz6Zo0vbFRHpou1aTLWsbjgzB0dfQ89NvMTItfqpVQSe+nT/z9QeOfAk9Lj5qsxQvMYcN1CATDLhNB3upNw1tfcnAiFmYmF0T8umU+bcX7O0qKeh702DuDb5KojUrJUty3YcYV4bVJ16O7BR2riiOpgqLgwiTQSPZA1CPB/aUQ57OhZXBuptHsixn0iVBFnPP9K2tIaTVwdWz92JIZZLMD5aSF82c/7+N2JuLXqMj/rp2xyceXddz0CJHrKRLuC/W3ni0plfhfUg7jJuRQ+ZdW9rSpHS+zv+maeuSGYDyS5tg2OM9/d7rI5kPzEuwC9KM2RqpaLwzrVAlxfAC9bJjfJqF2AjUnTZqimT3/ScXijxZOGFFnK0JBZCsQmAjDmDwZ5qIvl0FEVeuEKO4smXSjbqh5Yb9Rt2usfP5pfd32gSRwN5aCAjYQWLSOytJlfyQu88Y543pea5pICUnjXT8ghR2w4fPB/GFPOaMRfGVNJPityPYo8P+tluyhX+0nbBPgll2ppP91dCdWCcy04jPQVTqjCg9EccZrPtFVTaVK1eK4H2DEgs1cu4pEOnYlheyUeFYAzuAqeiR/YSRN1vsjW4hqp2ALnJdgs08ta/Ec4bikKPJc18CPxOAJ6DZGoh5sE5AAzP8mtilxBjUgHdEssTrLsV8U9sF6q6osqyN5ueK4hbJU6Vq7eF8gKDY9QMJlmAGHutUpMZE+RNO2AQ+oqJk9lFMaaCdryQePKYHFHYGKyG41HSMKH0qC1GPPRQ8z8N/N7lllQP1y/i7Bsq0VrgzXkzksDuZwcPHU4cX2YIGy9kHmpY4UDOCZaEqTtcQLTuBEuIKPaDso5Tm3QO7vnAT/4/8gf5qO65qPw+/Dn9CDuvElrlI8PqW98gOxfY5oLBUVEPjU7WoHyn97cvT5AB9ZlYFEmYtURaGfpBuTUljAtfX080u2+Zrwh4aD2EHSr4gY0NLpSEhMScgrZ+RR1rVC7UknLe7b7XXK1L/GoCRrutsgGJe6ZsF57e+cbx3dspeo9zK0NWk0br1zPV8MN9dJ9orNS9UB2iRyRrHcA3URiXfASvl9aimxYTUyY2btIFW/2SUEaLgF006/gE8FcVXKm14areOyDwfXt7opLMxGy56YSPnWVP2r15Z3nPbJi6Ea/XDhHGSqMdwK+wNFiVb/y/dwuSSjVDOJD8QAkAbR+QRLO8bsA+8FaBeV9TJoonqpeloQLPGS4+hbygjQvS8Tgirj1o+XwgzIMRfLblkgUbQ+6d+9IpjB4fcN+F1sPAGuk2/Ek6Oees/HVHd+2oaYkEClbGC7RtFlsE5K6Yqgs6YLnkVbvlZccTWkYtSIV0mHpeR2kwcHIodcs+NfsgYgP/sqoR2TNi3v9uzV7H821kLR1RTjs7FhcPz1fyv2vluFYcswb4UWrdiLUBMe58s740NLrYVLN8n5l6AkcfW/vXSssijQSnPsCF9yJfS5wnCMXeiMF8iq1XOSzatzRT20LyFABpnykW5gD2Q+AhbbrYtL5wZNZA4mGLmAm5dIE0gpfqpcaHqxjHs488rgQCd4Y1PP4Tld/oZ4h3Pjc2PgZ9fUg9RXbn72GbaPhQ9AJOIAo53vr8PEJs5cfVUjaLOV/A/ZhZ+swfvyIkXR+4o5zCB5t7M5B0DamWZQ4bBiFoC3r9777gYJJRm8uViIhqVvGDv4I0OHGeQC3AAh9H3Fo/Sf5y+dqk5a1kAZdqNiPp+moYEtSFlrWyd6GGyIkKndV/MnXye/nhuq8UF50MPsQ8J1YlZhhdO+UCFoyetb4AW2txeAV4Oy6grvRTh8TonHX948jbyzTfnkzAnXpjVUlSRjH40va3MT/SRutmRnbP1E8sDU2UtfUKy43DoqEJp4JPB8aEr+uys+sVqc8XK3GWJuwOXV53+flZTma3LIU3OAFbFUWTn7C+x8FOI2WWkIcxP+iitmDHIStFVany6je1eBeXEwqmttP1AcPxEj8zeuit1uk3uZHrN2Tn6wyNBEuh1xl3h5kLalP9urs5NL9lo095/Iwa6gm2Zf5t4A4arrgkCiDXkPofMPNNQGkc11s81k5Dsri29gPk/stYpPAL2YWnhXHGHmq1kzO8fGmBi2ykkv6QPT1ml28xVB4xuB9VwS7069CzHQRXzV341Vg93kphz9beNcFLZG3HZyYWkbKqCMyzeheVpPVFOh7SgO8VFFnKYclQebxbDvAj8udc3uw1CPLknX01YLbjBSL7XGMoE+pdhllrVycVpT+/G5IM9tgrMY5YIFZMVUpyi4cckDsTh2LOyPi+T5bRuDZHniu11h5zicglyKAJVMLavPzYjNE3fUgKCL5VtiQIBPecAq7u2WsRwE2Q269o4ervYTyeulvr6B7wcQ/bb5ZGgxTtE96FWScqoy12fWrKyvqAjI2q9r8AAWIMxrdvIvXfgl20j6WBtgeaMw0purkwFTL5nT1jMBweD2HE4tgbGWOqcsdX6yRvRGmPuTH7Rwn4cJsSol+umfwig4RKWVYhe9dxxquGYe5WqZnmoT+VK7LcTgaNKzLDyeWtZzR/2dbS0/lBfEfsK9/2dKO5qs1wvagJfWHT5KP5+APZqCWUOnuwN1/HDTWVEst+IikvqQsN3Fs0fyIRF3DQBPd1W9dH1+qHkfgIk9T8TRUZZVDz9mj7NL9LbDrMOE8mwr32IAQX9SWGm/Weo0i4ZniotmHSyAid27iUTeKlholCltnadud3I8Cl7wUZLPJxPjRTua7b1Ih8HJ9bOEA0Kbfyzwcc8r8hXX99iiXsY8YO98Qj13HDlCXnxmWP7Z1GSJuNkvXun4qyRAtfJGR84QgXkI3EqURmyWRD+Ykh2VnFa7xfESPKwRSn6wMGQXR+7udE4toXwNKV/wqwBbCTD67iZrkZ3shv+lBCei6OIXlnJilajuTnXoJz6sXjcsNOs9PAgShbXYhg9uuhMYddSSuKXCr5AcosKvibmSzMOJrJER0CQ53MO3h2r4jWarfsO/IiJDraBl2x31XN37MtdpUT5tvrSqgKV+21GN5cg13JoPI9u03d3N6zdRUKoKXKG9n31W/nQxNdjhSuUY+lCPZ2n1KxtbUEtSqKSLS7DieSLSmkDxkZHir3zCazv3QYo1Q/uP/RmoljxaV5D/poYwDrjzUe94ewSwRyg==,iv:vEOj5rAyPSrCSrTazDnOF3Hl5jEAjJ24Ex09lf9kBIo=,tag:j96Vhj1wUmXnld25MLgObw==,type:str] sops: kms: [] gcp_kms: [] azure_kv: [] hc_vault: [] age: - recipient: age1zeqkpfz7e3s207ynea0z0auc0mrct0pc7w4sh6j3d0c4qac3dahqj9ufdg enc: | -----BEGIN AGE ENCRYPTED FILE----- YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAwR01PSVg0UkxDcm1HMzdO d3dlSm4rWVVyMGpnSWI2NDFsM3lYNkxMWjBNCndJbm80b3NSakFubjdMQmQzVlMr UTlKZjdIYnV2NmtBMEMxWDVVVXFRZnMKLS0tIERxcDJIUkVqSGg3VDJRZC9SZ2hP dHBRWXZjZHJQckYrV1FaOXI3YmVkd0EKRfvy+yU0IoXp7sGY8QjW86ZESc4dzhQd 5Y+iCRYr03D8qJIEFYiL2IbaezNpOKgeCaf+96ceU9pvxqyrKfH3Ug== -----END AGE ENCRYPTED FILE----- - recipient: age1tdwlq9y4jgejkhasqwynw5uaen9xwatcvr52l70trsdxkeyvlesqjnh7l8 enc: | -----BEGIN AGE ENCRYPTED FILE----- YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBjK3F0eTk4YnZ3UFFzdGVD elBVQmgvODYyMDRoY2lSOGdlWGcxZndiTGhzCm9wd0dYOUtlRHpUdjhiYUxiTlNK WTV5MGovL3pNZ29PK216YzBGT3JKMGsKLS0tIFc4a3lGSFlueStNSjFEZGhXZ3E1 MUQrTmo4Y3BZS1NLTC93dlgzU1ZiOTQKOLs3xXiPGEtcyc+Y0/QkoQvofIMnbBDc kjS81Wyaor46ZevIgu2g1zAyk2GIjItflyqZIMfxC3IZmm+bPzuOWg== -----END AGE ENCRYPTED FILE----- - recipient: age16p3zls5n0jks6amszwcuaqgl5dyuyf8k8wgeyrw562s5s88xtq3qq046fh enc: | -----BEGIN AGE ENCRYPTED FILE----- YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBHYlZUS0IzUVM1MDA4MTd6 c0cyaGV1UUJIMStkZWIvcFlXbWNHVzE1aUQ0CmZvdjZ2Z3dIRkMrcTFYTmFvRWtD cDhBUmN0NEczMnYzdVpOZkZObG8wMEUKLS0tIFVXYy9yZDhqNS90SFgwUmtnSnh2 VytreXNRRDkwSG5KNUhaY2k0bndHSEUKGykRA6HLXItCoxcx69EuzGyAOtsqlPcd yHx+7MiVfvZvnotEftKyhDDQhqTA4NJB2gDK1JG7e+zJD6qeNa8QDA== -----END AGE ENCRYPTED FILE----- - recipient: age1v52mx8gs4ephprep0wcc4j3fvvprppvs9vktf2p24yv52sqsf33sd5crk9 enc: | -----BEGIN AGE ENCRYPTED FILE----- YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBJS3VFdEFGSWpZQUdkK1VC czBYYXJrNE1SSXhYc3diYXNPWGJwT1ZTT0hVCjNiSGU3WFdvVjl5WEorY3NMa2ht bWVIYy9SUkt6V3lrUUZWZDhqNHVqc0kKLS0tIGRGWEg4Sm1xTlFONStuSFl3NG40 VGhtUlY1dmE4cnlwcU4vVUkyRGJERXcKPFzN+o5MO7OVvVv8cEBUWsFV4YlKxHhx 3BPWBcMgnT6v89BdmB1k69FrgOKzPVCU7V1UdBcNp2dwqgeD3Ddayw== -----END AGE ENCRYPTED FILE----- - recipient: age1k8ufac2s0gs6nh0xsfavafz062vd36petmyv6nwmg00z4a7s4gnsjtd837 enc: | -----BEGIN AGE ENCRYPTED FILE----- YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBRRXlUUXFkUHBGSjhaRkVK azMvMjM1ZXdpMVlOWGZaaUlLbzRBM1hjMUNnCjl6SisyODArYmJxaE5iaEFydXpN UG02a2dWVzlBZjk0Wnh6bHlGSnlPZWcKLS0tIERkaG1TSS8vY0I2cWJEWkl6QzlL NmZKNTR4K1gwT082Mmt4aWpJT3ZlYzgKCQPEjXhRi2SH8jK03R7qc5ADaGwLH/ik oCxW6pnBI1Ql56vJhyMufuRodyYjxebqBDnTDi12m83IBR8w4IzwYA== -----END AGE ENCRYPTED FILE----- - recipient: age1dcsm5awz8ekzchk7gsvndkc4kq65n2wzgavxtqe53vxdsfk9k9pqh3whru enc: | -----BEGIN AGE ENCRYPTED FILE----- YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBkK1F2U204Zjl0QWc5SGFI TjY1bmRJVVVKT2czTURyajFuTjFhQnF6TUZRCndiSzZqSzUyQyswMXppZFAvREFD cHY3RXk1RnJUSUxOWkVMVjdHR0ZRS1UKLS0tIEJ5MDlpRjNXVnN5WHFzL3F6Y0pV RVEvL0M3ZGtURTg4R09XZXFZN0p4OVUKArbhntaN2b/U/QyseGEETldosAi1OgzL AFcxfso0usyMBPhr5WUHeq4vr2PX3sVbEM2t9SPIgAM0NbFD/OwqbQ== -----END AGE ENCRYPTED FILE----- lastmodified: "2025-04-28T05:02:22Z" mac: ENC[AES256_GCM,data:zBpUQjd5y6NK6WdRw2O+sat+fQD+4HDY+XrouAuZIPg7xwzvY3latIxA7qRFSsNCzkjzANTRAgGerSGSRxd6H2GRO2FQYA2BWL2FgdVLvh9VKjeHfza42OaSAJl3/BrE4U3g41+iGxD4SVTevbw9m34klzFhXOykm7ofXcTPVsQ=,iv:CBWPLhK9wQGt06GwGsZUXfFk0Hnf7GTVbXTXrXBD104=,tag:K8bpIFXqS5Fu+XSlqmRh9A==,type:str] pgp: [] unencrypted_suffix: _unencrypted version: 3.9.4 ================================================ FILE: system/hosts/budimanjojo-firewall/_modules/services/omada-controller/ssl.crt ================================================ -----BEGIN CERTIFICATE----- MIIFkzCCA3ugAwIBAgIUXIiKRTlvV52W0XJaom+pAOYCiCAwDQYJKoZIhvcNAQEL BQAwQzEdMBsGA1UEAwwUYnVkaW1hbmpvam8tZmlyZXdhbGwxEDAOBgNVBAoMB1RQ LUxpbmsxEDAOBgNVBAsMB1RQLUxpbmswHhcNMjUwNDI4MDQxNTQ2WhcNMzUwNDI2 MDQxNTQ2WjBDMR0wGwYDVQQDDBRidWRpbWFuam9qby1maXJld2FsbDEQMA4GA1UE CgwHVFAtTGluazEQMA4GA1UECwwHVFAtTGluazCCAiIwDQYJKoZIhvcNAQEBBQAD ggIPADCCAgoCggIBAMkQecvSK44Xmx/KQCxYuqtZwWmdmZFQy8ZVTnz/HhQUdsyR n+dgPxLIIK3PXY8Iv747REIRNQMKLMmKzuC1DkGJ5/P+DdFdWT/ga3Z4B+F9ADY5 25Sqbk/MKF/CMh3kigTAu7GdgewHXXhQ6CfWI6RqQgiYNvuWTbCzzYVftZzVtqlI WCF7oqDJKThu3ks/+5EU/L0IIM99QWRJ8TJjAHW/xmKNM/mzCiw4FyG597sRoMQw mj850nXksultKCdRdsV33iX6EczDBqZJ9oXt9e7AxJfSFDRUrsSkQkZB0dRbqZWj CwZC18mQmuz73KFgTqP/v+JNVbSE2nAs6dXdtliXsDVMONFaGAnvX7Lhy3/NwlXp AYtenoiT5xPNpUBTHKaXWjoTHMcaO7lSnD+jGPaFVDUBYFdUWBwucZqe9ndZnvIs Rv6ugOnIZkItBatAe6I2irHWjGc61xgYfN2LccldZoAa8hgWTSFxkBTXhM1wycKc FaFTF9GquRJtwz1fmWIHqQ6KMis6Mcjn6qmFR3zMFOLCFS7J4xAw5Hc7J81iXSHZ pk89o0TFwyKeGGiKynNCh6JCKFdgGh341TumRdpvDKAOG3/+iutPRCJGnJtz8W7z X2Q6SXvKf8d/hOXrWt5Gj20hhQCNz3HG2VqaBW+IJch0ByqFJCoSkyOXGGzbAgMB AAGjfzB9MB0GA1UdDgQWBBRSLyLwhK3oV8a7Xh+9kkrHPZDo+jAfBgNVHSMEGDAW gBRSLyLwhK3oV8a7Xh+9kkrHPZDo+jAPBgNVHRMBAf8EBTADAQH/MCoGA1UdEQQj MCGCCWxvY2FsaG9zdIIUYnVkaW1hbmpvam8tZmlyZXdhbGwwDQYJKoZIhvcNAQEL BQADggIBAGBp34RrFxNlnNfHA5l1MAuFmUc5bBa6evBt9TwIAMMw/6sRy6y+HC/N snuCZQp+ubjUcbG8ebTDcynGsBMKTThn0LhQOCNxNEbOAClN7DKquYoWlWgCoy3K g139O9/w4JqVCMo0GeiNW6bFSaJvK3JKEZ2x49ZywI6PH4MSNC6S5l8mqieY6nDB gQXJ364TeE3L/XK3YyyKoQ8g8jDAr7TtZq9Wl2LDX77mPIfOqr9YaoeubBO9ERkm 9waQz32FTzNS1RJqktkxzQtx9t5h9xbstBrvF/RFtN7ip/F5doFeXrQg0+m3Fihm 6u+7BXNh/r9UbzbKVqpeiG9zd8zvCQ0hJG9R2QlrRGA94E2CIeEgwGjuFq/sNuMZ 1y6054ySR6VStstHRAIb1zrqIzMOFSXiwNuOKuXd1UJDtOfsrJh2nyUSUJEzhYdh zLWB9+BbtozNYhhFP3uXP6d3qItj9emideNCfGFXo2x7Z+/JPWpYnWVOVJ9kJokg 5urgqWDVnm+gsDE31aGcH9GtrS0lmnqHGOjncdusCFGiD/GJ9DuZgerNSJzRKsjh TmJXoOo2l+V7URrP2FzzgkYPSXpts2jD/eBJ0YuNIByrkscSChdNaEC4VVHVPUQA 0cvsnmGHLUpDtbYA+X79PI2qUnvpXQSC7SF5fW8JRvqIpJ4UErVu -----END CERTIFICATE----- ================================================ FILE: system/hosts/budimanjojo-firewall/_modules/services/powerdns/default.nix ================================================ { config, lib, pkgs, ... }: let directory = "/var/lib/pdns"; user = "pdns"; group = "pdns"; in { systemd.tmpfiles.rules = [ "d ${directory} 0750 ${user} ${group}" ]; sops.secrets."powerdns/env" = { sopsFile = ./secret.sops.yaml; owner = user; group = group; }; services.powerdns = { enable = true; extraConfig = '' local-address=192.168.10.1:8853 launch=gsqlite3 gsqlite3-database=${directory}/pdns.sqlite3 dnsupdate=yes allow-dnsupdate-from=192.168.10.1/32 default-soa-content=@ gateway.home.arpa. 0 7200 3600 120960 3600 ''; secretFile = config.sops.secrets."powerdns/env".path; }; systemd.services.pdns.serviceConfig = { # powerdns doesn't create the sqlite database for us # so we gotta either do it manually one-off or do the below to ensure it's created # if the file is missing before service start ExecStartPre = lib.mkBefore [ (pkgs.writeScript "pdns-sqlite-init.sh" '' #!${pkgs.bash}/bin/bash pdns_folder="${directory}" echo "INIT: checking if pdns sqlite exists" if [ ! -f "${directory}/pdns.sqlite3" ]; then echo "INIT: no sqlite db found, initializing from pdns pkgs schema..." ${pkgs.sqlite}/bin/sqlite3 "${directory}/pdns.sqlite3" < "${pkgs.pdns}/share/doc/pdns/schema.sqlite3.sql" ${pkgs.busybox}/bin/chown pdns:pdns ${directory}/pdns.sqlite3 fi # exit successfully exit 0 '') ]; ExecStartPost = ( pkgs.writeScript "pdns-ddns-setup.sh" '' #!${pkgs.bash}/bin/bash cmd=${pkgs.pdns}/bin/pdnsutil $cmd create-zone home.arpa. || true $cmd create-zone 168.192.in-addr.arpa. || true $cmd create-zone 10.in-addr.arpa. || true $cmd import-tsig-key kea hmac-sha512 $KEA_TSIG_KEY $cmd set-meta home.arpa. TSIG-ALLOW-DNSUPDATE kea $cmd set-meta 168.192.in-addr.arpa. TSIG-ALLOW-DNSUPDATE kea $cmd set-meta 10.in-addr.arpa. TSIG-ALLOW-DNSUPDATE kea '' ); }; } ================================================ FILE: system/hosts/budimanjojo-firewall/_modules/services/powerdns/secret.sops.yaml ================================================ powerdns: env: ENC[AES256_GCM,data:dKz3m8vUPAwwK8QeXIZN9RrcCx2iBEAcVwx0befTalZQyAeynTxRrW3+aDKJA25HoQLtL/I5JqUW7+9xOuMEYQHH9+6OpXNS/j0FRCCGe0NbEIRXRGhXIJX+gutHuPU+hg0W/jrTgfQ=,iv:AyK/lDVeBX+YKpo+TZbFR4WCPBFVcfLLiQlUByeFZDA=,tag:CEl8jCsHE0f3cipCbgFy8Q==,type:str] sops: kms: [] gcp_kms: [] azure_kv: [] hc_vault: [] age: - recipient: age1zeqkpfz7e3s207ynea0z0auc0mrct0pc7w4sh6j3d0c4qac3dahqj9ufdg enc: | -----BEGIN AGE ENCRYPTED FILE----- YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB3RlpmY1E1VkNCODFzTHMv V1p5Vy8rckUrMWc3bklNOUFGUG95Vk14Q0dvCjJhbmE4WGN3OVhsTmI4bklsMHI3 Q0EwNjUySFhxV1lPblJMdmF0dE5Lb0kKLS0tIHkrMGh2VHhiVGM0ZWZhd2pFSmQy VVU4RTJzN05LTmJPemFWTDR1a1BLSzAKaGbhfbvC/letEmNTLi26VDpf6dWpUqEq o0jkxjc7KFKXkua0o0NWQPcHldKvqlLWldnih7lbBpKQs8W13h+PTQ== -----END AGE ENCRYPTED FILE----- - recipient: age1tdwlq9y4jgejkhasqwynw5uaen9xwatcvr52l70trsdxkeyvlesqjnh7l8 enc: | -----BEGIN AGE ENCRYPTED FILE----- YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBqOThxZEd6S1E4MnlyUVZQ R1J2dE5kQldEVWtObWdlOVFGcjdtcnIyRlZrCjFtOGpqNnVOV01CUWtlVDBCT2F2 TzUzUlJxYnRPUlM5YTlaaS9HV3pYbkUKLS0tIEJZUWhHTEpTYkhOYlFFL05JYXl0 ZFNndW0ra3JHRDl4bHIxUWRPSmpxNGMKEGTRssBQmk+6xT3oxDCYn0tOS6Y08G15 YwlZM08Wvd2McKiy8XdMRAKlYDg3tKdJaI7BGZp4BuuPuf8jilhi8A== -----END AGE ENCRYPTED FILE----- - recipient: age16p3zls5n0jks6amszwcuaqgl5dyuyf8k8wgeyrw562s5s88xtq3qq046fh enc: | -----BEGIN AGE ENCRYPTED FILE----- YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB3cUlweVdaUTIvSEFEVUsx QmVUMnBIdXU1MUJ0UnBJNzhVYjV1L08welRBCmlVKzdKdSsyc3VPZDI0SE94bE1p N0lQZmp4aUtOQWZQS1lYNVhXTHJmNk0KLS0tIEwxdXRwdnN6TVdGMjVTTFVUeTlO U1psRm5ERmNVOWxCK1QvZXBuRHRrMFUKeMmw/c5/dQ/+BgZuR8BVDW4n4CtiPaqh xHoOPA8BNlTkBdkBB51cVnt+9O+tE7xQBmJXZZA1oeomiElu9zxGJw== -----END AGE ENCRYPTED FILE----- - recipient: age1v52mx8gs4ephprep0wcc4j3fvvprppvs9vktf2p24yv52sqsf33sd5crk9 enc: | -----BEGIN AGE ENCRYPTED FILE----- YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBsTHVZZ3FDRlhNQlN0dHhK YkdZWkRRUExXRXg1UnBkR2JDeDNmVVg1b1M4CkxXR0xNUGsyYkZHbzBNa3BDWERq Tkt4YXJTVDB4RDRPSFdQeUVLbHAycU0KLS0tIEZxci92YmEvSXlFcW5id1NMVmtz ak5uZFA0bWFNQllvdHhxallnazZJYkkKY4XJx5t5JmVvvM+fx3ymBZG9Bot2oN1l VB6NkoNMQf6YTRR58F1bZD/LnCpPdcWORxtdN19oH+0bZV9QEi1Q6A== -----END AGE ENCRYPTED FILE----- - recipient: age1k8ufac2s0gs6nh0xsfavafz062vd36petmyv6nwmg00z4a7s4gnsjtd837 enc: | -----BEGIN AGE ENCRYPTED FILE----- YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBLei9TeVZCb1JFbHVlUzd2 bGw2aGEydC9PNi9xMThNRUFOcnQ0M2NLZ25VCndUcjNiSTNkNWx4aURWaXFJbC9T SFdySUJGK1VYUGhoemRxWnJOSzNLa0EKLS0tIGNadGNSa2YwdHQwVXM0bGRBbFp5 ZW9XZzNDR3B0SVJ2OWxBL2s2M3Y4NkUKLunf5l2I0IIcke2fOTvHgPh2Kb2LoyiL V1VoRkA9g+ao2NWNVSfbDT0LixpSHKH36v0RJh0xoTYjP/+AV70iRg== -----END AGE ENCRYPTED FILE----- - recipient: age1dcsm5awz8ekzchk7gsvndkc4kq65n2wzgavxtqe53vxdsfk9k9pqh3whru enc: | -----BEGIN AGE ENCRYPTED FILE----- YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBlOUVTT0JQdDBkYzRVMXVH MlZ0NEtuNGpIbXg0bmZvMlpFcFFDTG9QbHpjCjBvSTN2WXlSUXZRWkRoUkJKY3FJ dTdMS2hOTDRmTUl5emxkZXRzdDZibjAKLS0tIFNaVjhUUWM4b1o3K0VHdmRVWEQx NSt1Nng0bTRMdFluY0NCaHNNdDdXVFUKJ/C8M59h8SISE2kIUnipqnuZUmfE06mp 43rDnKh36H8H0mzJl8HGz3G5lWMLJji6ebu/OXogbSLrt6DxVc+MZg== -----END AGE ENCRYPTED FILE----- lastmodified: "2024-12-10T03:27:04Z" mac: ENC[AES256_GCM,data:Eh4UmkwzMcZjAllAHOE0HRTrUagbMq6vZ/vaS6BZizItRxeWO4DmkjrCPA3ffYThk89w4OW/CUSYATJC7tCHfM7+oSZCYbPJBs0adAidbH92twv1MoQUMmcptPRN0vXK+cEmejS6RtD+0xVisdxFiG+3wQeEylApkNkbPU9zvyc=,iv:SyVoD3m109joWWfNuSiZPTgPkdkcARHw014egfqO/Ag=,tag:m9GqmOmXGjRqsHsEdVV9Eg==,type:str] pgp: [] unencrypted_suffix: _unencrypted version: 3.8.1 ================================================ FILE: system/hosts/budimanjojo-firewall/_modules/services/rsyslogd/default.nix ================================================ { config, ... }: { services = { journald.forwardToSyslog = true; rsyslogd = { enable = true; defaultConfig = '' # for some reason rsyslog shows the hostname as "localhost" which is not helpful global(localHostname="${config.networking.hostName}") # TOOD: the options are needed because of how `vector` is setup in my kubernetes cluster # I will remove them once I updated the vector part module(load="imklog" ParseKernelTimestamp="on" KeepKernelTimestamp="on") kern.warning @@(o)192.168.15.5:6000;RSYSLOG_SyslogProtocol23Format ''; }; }; } ================================================ FILE: system/hosts/budimanjojo-firewall/_modules/services/tdarr/default.nix ================================================ { config, inputs, ... }: let app = "tdarr"; serverDir = "/var/lib/tdarr-server"; configsDir = "/var/lib/tdarr-config"; logsDir = "/var/lib/tdarr-logs"; cacheDir = "/var/lib/tdarr-cache"; in { # we mount media directory from the NFS server boot.supportedFilesystems = [ "nfs" ]; services.rpcbind.enable = true; systemd.mounts = [ { type = "nfs"; what = "192.168.200.30:/Kubernetes-Volumes/default-shared-media"; where = "/mnt/media-nfs"; options = "nfsvers=4.2,nolock,hard,rw,noatime,nconnect=8"; } ]; systemd.automounts = [ { where = "/mnt/media-nfs"; requiredBy = [ "podman-tdarr.service" ]; } ]; # create directories needed for the volumes systemd.tmpfiles.rules = [ "d ${serverDir} 0755 1000 1000" "d ${configsDir} 0755 1000 1000" "d ${logsDir} 0755 1000 1000" "d ${cacheDir} 0755 1000 1000" ]; # the actual Tdarr service in container virtualisation.oci-containers.containers.tdarr = { image = "ghcr.io/haveagitgat/tdarr:2.70.01"; extraOptions = [ "--ip=10.5.0.20" # we use our local dns server to connect to Radarr/Sonarr inside my internal k8s gateway "--dns=192.168.10.1" # tdarr can take out the system by eating all RAM sometimes "--memory=5g" ]; volumes = [ "${serverDir}:/app/server:rw" "${configsDir}:/app/configs:rw" "${logsDir}:/app/logs:rw" "${cacheDir}:/temp:rw" # my own tdarr-plugins repo fork that contains my personal flow # it is fetched into a nix-store so I can mount it "${inputs.tdarr-plugins}:/tdarr-plugins:ro" "/mnt/media-nfs:/media:rw" ]; environment = { serverIP = "0.0.0.0"; serverPort = "8266"; webUIPort = "8265"; internalNode = "true"; inContainer = "true"; maxLogSizeMB = "10"; pluginsDir = "/tdarr-plugins"; TZ = "Asia/Jakarta"; PUID = "1000"; PGID = "1000"; }; devices = [ "/dev/dri:/dev/dri" ]; }; # restic backup services.restic.backups = config.lib.mySystem.mkRestic { inherit app; paths = [ "${serverDir}" "${configsDir}" ]; }; } ================================================ FILE: system/hosts/budimanjojo-firewall/_modules/wireguard.nix ================================================ { config, ... }: { sops.secrets."wireguard/privatekey" = { sopsFile = ./secret.sops.yaml; owner = "systemd-network"; restartUnits = [ "systemd-networkd.service" ]; }; systemd.network = { netdevs."50-wg0" = { netdevConfig = { Name = "wg0"; Description = "WireGuard"; Kind = "wireguard"; MTUBytes = "1420"; }; wireguardConfig = { PrivateKeyFile = "${config.sops.secrets."wireguard/privatekey".path}"; RouteTable = "main"; }; wireguardPeers = [ { # budimanjojo-oracle PublicKey = "e71Old3Ax2DEw8QB9yvhyOIIuNJHtp8nBYKJJaDVPkw="; AllowedIPs = [ "10.10.10.0/24" ]; Endpoint = "140.245.111.170:51821"; PersistentKeepalive = 15; } ]; }; networks."50-wg0" = { matchConfig.Name = "wg0"; address = [ "10.10.10.11/32" ]; }; }; } ================================================ FILE: system/hosts/budimanjojo-firewall/default.nix ================================================ { ... }: { imports = [ ./disk-config.nix ./hardware-configuration.nix ./_modules # host specific modules ../../profiles/server.nix ]; config = { myHardware.cpu = "intel"; mySystem = { programs.nh = { enable = true; flake = "/home/budiman/Github/nix-config"; }; services = { btrfs-autoscrub = { enable = true; fileSystems = [ "/" ]; }; restic-backup = { enable = true; location = "/mnt/backup-nfs"; }; }; }; boot.supportedFilesystems = [ "nfs" ]; services.rpcbind.enable = true; systemd.mounts = [ { type = "nfs"; what = "192.168.200.30:/Backups/restic-budimanjojo-firewall"; where = "/mnt/backups-nfs"; options = "nfsvers=4.2,nolock,hard,rw,noatime,nconnect=8"; } ]; systemd.automounts = [ { wantedBy = [ "multi-user.target" ]; automountConfig.TimeoutIdleSec = "600"; where = "/mnt/backups-nfs"; } ]; services.openssh.listenAddresses = [ { addr = "192.168.200.1"; } { addr = "192.168.50.1"; } ]; # sshd failed to start on boot when interface is not ready yet systemd.services.sshd = { after = [ "network-online.target" ]; wants = [ "network-online.target" ]; }; }; } ================================================ FILE: system/hosts/budimanjojo-firewall/disk-config.nix ================================================ { ... }: { config = { # filesystems managed by Disko are defined here disko.devices.disk.main = { type = "disk"; device = "/dev/disk/by-id/ata-BR_128GB_202209231491"; content = { type = "gpt"; partitions = { ESP = { priority = 1; name = "ESP"; start = "1M"; end = "512M"; type = "EF00"; content = { type = "filesystem"; format = "vfat"; mountpoint = "/boot"; }; }; root = { size = "100%"; content = { type = "btrfs"; extraArgs = [ "-f" ]; # override existing partiion subvolumes = { "/rootfs" = { mountpoint = "/"; }; "/home" = { mountpoint = "/home"; }; "/nix" = { mountpoint = "/nix"; mountOptions = [ "compress=zstd" "noatime" ]; }; }; }; }; }; }; }; # filesystems not managed by Disko are defined here fileSystems = { "/home" = { # This is needed for `sops-nix` to work properly on reboot # see: https://github.com/Mic92/sops-nix/issues/149 neededForBoot = true; }; }; }; } ================================================ FILE: system/hosts/budimanjojo-firewall/hardware-configuration.nix ================================================ { modulesPath, ... }: { imports = [ (modulesPath + "/installer/scan/not-detected.nix") ]; boot = { initrd = { availableKernelModules = [ "xhci_pci" "ahci" "nvme" "usbhid" "usb_storage" "sd_mod" ]; }; }; } ================================================ FILE: system/hosts/budimanjojo-main/default.nix ================================================ { ... }: let pubkeys = import ../../pubkeys; in { imports = [ ./hardware-configuration.nix ../../profiles/workstation-hyprland.nix ../../profiles/gaming.nix ../../profiles/work.nix ]; config = { myHardware = { cpu = "amd"; gpuDriver = "nvidia"; monitors = [ { name = "HDMI-A-1"; xname = "HDMI-0"; width = 1920; height = 1080; wallpaper = ./wallpapers/tokyonight01-left.png; workspaces = [ 1 3 5 7 9 ]; } { name = "DP-1"; width = 1920; height = 1080; primary = true; x = 1920; wallpaper = ./wallpapers/tokyonight01-right.png; workspaces = [ 2 4 6 8 10 ]; } ]; }; mySystem = { displaymanager.sddm.wallpaper = ./wallpapers/tokyonight01-left.png; programs.nh = { enable = true; flake = "/home/budiman/Github/nix-config"; }; services = { btrfs-autoscrub = { enable = true; fileSystems = [ "/" "/home" ]; }; openssh = { enable = true; authorizedKeys = [ pubkeys.work-pc pubkeys.op9-termux ]; }; }; }; }; } ================================================ FILE: system/hosts/budimanjojo-main/hardware-configuration.nix ================================================ { modulesPath, lib, ... }: { imports = [ (modulesPath + "/installer/scan/not-detected.nix") ]; config = { boot = { initrd.availableKernelModules = [ "nvme" "xhci_pci" "ahci" "usb_storage" "usbhid" "sd_mod" ]; }; fileSystems = { "/" = { device = "/dev/disk/by-uuid/d1d4bccb-7efb-4af3-b0c8-6b3cf162604f"; fsType = "btrfs"; options = [ "subvol=root" ]; }; "/home" = { device = "/dev/disk/by-uuid/e5dd078f-db8a-4652-8931-6c2e8c8c2ed6"; fsType = "btrfs"; options = [ "subvol=home" ]; neededForBoot = true; }; "/nix" = { device = "/dev/disk/by-uuid/d1d4bccb-7efb-4af3-b0c8-6b3cf162604f"; fsType = "btrfs"; options = [ "subvol=nix" ]; }; "/boot" = { device = "/dev/disk/by-uuid/294B-A128"; fsType = "vfat"; }; }; swapDevices = [ { device = "/var/lib/swapfile"; size = 16 * 1024; } ]; networking.useDHCP = lib.mkDefault true; hardware.bluetooth = { enable = true; powerOnBoot = true; }; }; } ================================================ FILE: system/hosts/budimanjojo-nas/_modules/default.nix ================================================ { ... }: { imports = [ ./network.nix ./nfs.nix ./incus.nix ]; } ================================================ FILE: system/hosts/budimanjojo-nas/_modules/incus.nix ================================================ { config, pkgs, ... }: let runner = "${config.virtualisation.incus.package}/bin/incus"; oidcSetup = pkgs.writeShellScript "oidcSetup" '' DOMAIN=$(${pkgs.coreutils}/bin/cat ${config.sops.secrets.secret-domain-0.path}) ${runner} config set oidc.client.id incus ${runner} config set oidc.audience https://incus."$DOMAIN" ${runner} config set oidc.issuer https://auth."$DOMAIN" ''; in { sops.secrets.secret-domain-0 = { sopsFile = ./secret.sops.yaml; }; virtualisation.incus = { enable = true; ui.enable = true; preseed = { config = { "core.https_address" = "[::]:8443"; }; networks = [ { name = "incusbr0"; type = "bridge"; config = { "ipv4.address" = "10.0.100.1/24"; "ipv4.nat" = true; "ipv6.address" = "auto"; }; } ]; storage_pools = [ { config.source = "/vm-data"; driver = "btrfs"; name = "default"; } ]; profiles = [ { name = "default"; devices = { eth0 = { name = "eth0"; type = "nic"; network = "incusbr0"; }; root = { path = "/"; pool = "default"; size = "20GB"; type = "disk"; }; }; } { name = "external"; devices = { eth1 = { name = "eth1"; type = "nic"; nictype = "bridged"; parent = "br0"; }; root = { path = "/"; pool = "default"; size = "20GB"; type = "disk"; }; }; } ]; }; }; users.users.${config.mySystem.adminUser}.extraGroups = [ "incus-admin" ]; systemd.services.incus-preseed.postStart = "${oidcSetup}"; networking = { nftables.enable = true; firewall = { allowedTCPPorts = [ 8443 53 67 ]; allowedUDPPorts = [ 53 67 ]; }; }; } ================================================ FILE: system/hosts/budimanjojo-nas/_modules/network.nix ================================================ { ... }: { networking.useDHCP = false; # creating a `br0` network bridge that is used for Incus to get IP address from router # so our other machines can connect to containers or VM in `external` profile systemd.network = { enable = true; netdevs = { "20-br0" = { netdevConfig = { Name = "br0"; Description = "virtual network interface with fixed MAC address"; Kind = "bridge"; MACAddress = "d0:50:99:25:88:91"; }; }; }; networks = { "20-br0" = { matchConfig.Name = "br0"; networkConfig = { Description = "configure br0 to get IP from DHCP server"; DHCP = true; }; linkConfig.RequiredForOnline = "carrier"; }; "20-br0-en" = { matchConfig.Name = "en*"; networkConfig = { Description = "add en* interfaces to become member of br0 bridge"; Bridge = "br0"; }; linkConfig.RequiredForOnline = "enslaved"; }; }; }; } ================================================ FILE: system/hosts/budimanjojo-nas/_modules/nfs.nix ================================================ { ... }: { fileSystems = { "/export/Kubernetes-Volumes" = { device = "/nas-data/Kubernetes-Volumes"; options = [ "bind" "nfsvers=4.2" "nofail" ]; }; "/export/Media" = { device = "/nas-data/Media"; options = [ "bind" "nfsvers=4.2" "nofail" ]; }; "/export/Backups" = { device = "/nas-data/Backups"; options = [ "bind" "nfsvers=4.2" "nofail" ]; }; }; services.nfs.server = { enable = true; exports = '' /export/Backups *(rw,no_subtree_check,insecure,no_root_squash,crossmnt,async) /export/Media *(fsid=aa341bb8-719d-4700-96f2-40f33abfefad,rw,no_subtree_check,insecure,no_root_squash,crossmnt,async) /export/Kubernetes-Volumes *(fsid=0241f5d9-633f-457a-9f48-dbbbf9d5adcb,rw,no_subtree_check,insecure,no_root_squash,crossmnt,async) /export *(ro,fsid=0,root_squash,no_subtree_check,hide) ''; }; networking.firewall.allowedTCPPorts = [ 2049 ]; } ================================================ FILE: system/hosts/budimanjojo-nas/_modules/secret.sops.yaml ================================================ secret-domain-0: ENC[AES256_GCM,data:r4F6tC+u2XkxlFsldxXg/3A=,iv:lV0NHP3/zgQKqtGItFfi7FRbjFdvGb+Mh9jpu42vKSM=,tag:nJCnP6BnLwgpHlzkQF28hQ==,type:str] sops: age: - recipient: age1zeqkpfz7e3s207ynea0z0auc0mrct0pc7w4sh6j3d0c4qac3dahqj9ufdg enc: | -----BEGIN AGE ENCRYPTED FILE----- YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBJNGxUNjc1L1JNUFd1TGZw clFSOUpSbXNIa3plNHdHaWVaaklLVEtNQ2dnCloremxUV1crVFVTNEZleVJTNnZo TFdYWlY2OVdYMGdLNnRWdEFHczd5SjQKLS0tICs4QnloUTRXNVNRYnZsbUxlK3dW a1RBVGxUV3czUWhPeUczeWh0MitpSTgKtOukeVZHlJfqVNgQEmtzvAidfvaxQ0Vu eAZBxVlGlqRGUmSzhHukdBUY8RTjfsbP0NXaL/FzGfWp8EjMfu9xUA== -----END AGE ENCRYPTED FILE----- - recipient: age1tdwlq9y4jgejkhasqwynw5uaen9xwatcvr52l70trsdxkeyvlesqjnh7l8 enc: | -----BEGIN AGE ENCRYPTED FILE----- YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBpMWI3bUtDQkwrdXVmYUxU SmU1WHdlK1FLNDdBcklVOTZ6aVpnbnEyZURFCnVVY2dqbzlua2d0dkU2NFY2M2pY UW94bVNOWi9vWFprSmxRQkxTRldpcXMKLS0tIHFOMjhKc214NlMvMGxZSW9ZSHUz TnppY1ZWTFdhNlpXNTVHeDlGZkwxRWsKRZdQHvJV+ZxWJblV67sE5hQ2mNbX5Nix QUBNhgsKmHgU9yQxOekjOyV+8PEU9ziHtNOP87TrclF3IBawUIYYYQ== -----END AGE ENCRYPTED FILE----- - recipient: age16p3zls5n0jks6amszwcuaqgl5dyuyf8k8wgeyrw562s5s88xtq3qq046fh enc: | -----BEGIN AGE ENCRYPTED FILE----- YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBrZTcxWnRYNThKZkZQTGo4 aDhNVlV6VTN1MHpTSkdwRVk1SVJSb1RKTDBvClhuNUxiTTZwQXhRTWdPU1VsY3hu YzFhaHNVK3JxYVpLdVpOalVwQmxhTjgKLS0tIHU0NHY0ZnVkbXB5UnBXSGNFcVYz Ti9RanBDc2pmSGlYbTQzQUc5Q0pwdzAKfq7K2LYsr260aJcTwHSSEyuAi+aj5jaK L3jfnKuQ7P0sq0sMu8il9ThOoLaNWgD35n+Vf+5ene7WL55hJEjd1A== -----END AGE ENCRYPTED FILE----- - recipient: age1v52mx8gs4ephprep0wcc4j3fvvprppvs9vktf2p24yv52sqsf33sd5crk9 enc: | -----BEGIN AGE ENCRYPTED FILE----- YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB4WlZyV1Axdi96eCtpOUkw cjcySStsazV4MFdEcWtZWXpQS25HNjdTWHdNCitYR1J2ajdKb0hEbjZFQ05jaG9D cWw1VzFVQ29HRnFVdUR0QitBVW90QkEKLS0tIC9MUlRvSW1uajVLQ1lWNStDblg1 MVpHalZ5TTRQamFGUFlvR3pPWGRlRFUKTkmSQOiOkK4BVHKVumkVglNdpIG6vkc9 9kJwLfTbD+XTYsJTtzwKRbCSDEDaTNyIyWBrJBcDxiwEDK7dKsptTg== -----END AGE ENCRYPTED FILE----- - recipient: age1k8ufac2s0gs6nh0xsfavafz062vd36petmyv6nwmg00z4a7s4gnsjtd837 enc: | -----BEGIN AGE ENCRYPTED FILE----- YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBxQVZ4RC9MZkcyRURsUllS TWRhS3JxTkpJN01YRUg3azQvTFRPMXFOUWxvCkp6eVQ3dnVvVzFUZEhwQkNscnpX OGQ1VFZWMFYvN0xYVmhGY1B2RzlsQXMKLS0tIGdvd3N5VlRTdDBpM0hBS2hENUEw T3JVZGc2MkwyWTcrOUgyWE14WmpqMEEKXB4VPhWSe/z3yUH6X7cBYUQBXUjjLBxP o7M8uCCpKSM4VyEzeHiga4cskWI/PjLgRjoo569z6lw5rXhCXv2vtg== -----END AGE ENCRYPTED FILE----- - recipient: age1dcsm5awz8ekzchk7gsvndkc4kq65n2wzgavxtqe53vxdsfk9k9pqh3whru enc: | -----BEGIN AGE ENCRYPTED FILE----- YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA1LzU0RFBIU2ozQlhWTjVw MFlqd2dYWnNEeWRZNlJMZnBOVUo2OXZiQmswCkFpRTkwVGtnRkJaSllkNkdWK21j VDBpMkU5Vm5yVkhBQUhpUnRlN0N1bEkKLS0tIDlyT3JXaG9lc0hlS2FYS3gyRUZz d2RscGFWZ1h3cWJSQjJxTFJvSkNlekUKQ7kY3N0uiPiLbrSKMbzxvP0h6ptMrfJl TOhgqrijxet/giycycam5OilBbtEkUGxFJ95w/BlG2+QRgnQPl1g6g== -----END AGE ENCRYPTED FILE----- lastmodified: "2025-09-25T07:14:25Z" mac: ENC[AES256_GCM,data:6GfeWvYdem9o72hH+2TXnjo8gLMGLgupX3/fDk88oK+8VhZlyn/NA0WGMw1zzic7TdLojbHBFr1jV2qhCqUlexIimR2+hyMuxwe8xLKk+R/n6NNQIOeND72gbymezUrDhMZLtDPEZXAdMd95bUZjYcrU1zcXAg9YntllNp+JTdY=,iv:kEPQ4VMElpx0tJ1oAmErCFAX8t0HYXsHrvRY8fJPVCM=,tag:qvDmpY5hGuEQjy6Y3vSPiQ==,type:str] unencrypted_suffix: _unencrypted version: 3.10.2 ================================================ FILE: system/hosts/budimanjojo-nas/default.nix ================================================ { ... }: { imports = [ ./disk-config.nix ./hardware-configuration.nix ./_modules # host specific modules ../../profiles/server.nix ]; config = { myHardware.cpu = "intel"; mySystem = { programs.nh = { enable = true; flake = "/home/budiman/Github/nix-config"; }; services.btrfs-autoscrub = { enable = true; fileSystems = [ "/" "/nas-data" ]; }; }; }; } ================================================ FILE: system/hosts/budimanjojo-nas/disk-config.nix ================================================ { ... }: { config = { # filesystems managed by Disko are defined here disko.devices.disk = { main = { type = "disk"; device = "/dev/disk/by-id/ata-KINGSTON_SA400S37120G_50026T0910A0397"; content = { type = "gpt"; partitions = { ESP = { priority = 1; name = "ESP"; start = "1M"; end = "512M"; type = "EF00"; content = { type = "filesystem"; format = "vfat"; mountpoint = "/boot"; }; }; root = { size = "100%"; content = { type = "btrfs"; extraArgs = [ "-f" ]; # override existing partiion subvolumes = { "/rootfs" = { mountpoint = "/"; }; "/home" = { mountpoint = "/home"; }; "/nix" = { mountpoint = "/nix"; mountOptions = [ "compress=zstd" "noatime" ]; }; "/swap" = { mountpoint = "/swap"; swap.swapfile.size = "16G"; }; }; }; }; }; }; }; # full btrfs drive for incus vm-data = { type = "disk"; device = "/dev/disk/by-id/ata-Kingmax_SSD_240GB_1393070A113C00097082"; content = { type = "btrfs"; extraArgs = [ "-f" ]; # override existing partition mountpoint = "/vm-data"; }; }; }; # NAS data not managed by Disko fileSystems = { "/nas-data" = { device = "/dev/disk/by-label/BTRFS-RAID10"; fsType = "btrfs"; }; "/home" = { # This is needed for `sops-nix` to work properly on reboot # see: https://github.com/Mic92/sops-nix/issues/149 neededForBoot = true; }; }; }; } ================================================ FILE: system/hosts/budimanjojo-nas/hardware-configuration.nix ================================================ { modulesPath, ... }: { imports = [ (modulesPath + "/installer/scan/not-detected.nix") ]; boot = { initrd = { availableKernelModules = [ "xhci_pci" "ehci_pci" "ahci" "mpt3sas" "usbhid" "usb_storage" "sd_mod" ]; kernelModules = [ "dm-snapshot" ]; }; }; } ================================================ FILE: system/hosts/budimanjojo-oracle/_modules/default.nix ================================================ { ... }: { imports = [ ./services/blocky ./services/qbittorrent ./firewall.nix ./network.nix ./wireguard.nix ]; } ================================================ FILE: system/hosts/budimanjojo-oracle/_modules/firewall.nix ================================================ { ... }: { networking = { firewall.enable = false; nat.enable = false; nftables = { enable = true; flushRuleset = true; tables = { main_filter = { family = "inet"; content = '' chain INPUT { type filter hook input priority filter; policy drop; ct state vmap { established : accept, related : accept, invalid : drop } iifname lo accept comment "allow loopback traffic" # FROM OUTSIDE iifname enp0s6 udp dport 51821 counter accept comment "allow access to WireGuard" iifname enp0s6 tcp dport 22 counter accept comment "allow access to SSH" iifname enp0s6 tcp dport 60413 counter accept comment "allow access to qBittorrent torrentingPort" # FROM WIREGUARD iifname wg0 tcp dport 22 counter accept comment "allow access to SSH" iifname wg0 ip saddr 10.10.10.0/24 counter accept comment "allow access from own CIDR" iifname wg0 tcp dport 8080 counter accept comment "allow access to qBittorrent webuiPort" } ''; }; }; }; }; } ================================================ FILE: system/hosts/budimanjojo-oracle/_modules/network.nix ================================================ { ... }: { networking = { nameservers = [ "1.1.1.1" "8.8.8.8" ]; useDHCP = false; }; systemd.network = { enable = true; networks = { "30-enp0s6" = { matchConfig.Name = "enp0s6"; networkConfig.DHCP = "yes"; linkConfig.RequiredForOnline = "routable"; }; }; }; } ================================================ FILE: system/hosts/budimanjojo-oracle/_modules/secret.sops.yaml ================================================ wireguard: privatekey: ENC[AES256_GCM,data:WgGBE9Gx7s66/ouwZnLFoUDDcmJO3Bvq6tkifCTrtTdYCAVKr63lkc/ijMg=,iv:SkrjD0XsxTxIKqpElLVZfiyZw0Ivi+IQAM586HJneLw=,tag:/8RWls0rxggMiLFvDsDVGQ==,type:str] sops: kms: [] gcp_kms: [] azure_kv: [] hc_vault: [] age: - recipient: age1zeqkpfz7e3s207ynea0z0auc0mrct0pc7w4sh6j3d0c4qac3dahqj9ufdg enc: | -----BEGIN AGE ENCRYPTED FILE----- YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBkc2tMaC9iSmpkaU94OVVR MFk1Mk1RQjBQZGhLL1hONGR5Q05hRGhZNVFnCnNiMURZWXZmL20wcktlbFV6ZXZB TFhnZk5US2RjL2wwOWVDMG9jeVk1OGcKLS0tIHJBUExRQmhBaVFoOXRkblh3Znc1 bUhqY21SclZHMGNXMmdSM3o0eTJIOEEKN/qOSLEmNOQK5uw1O1HVBtpyfvq9V7yG ltNe6dLSClbYU3xFXHpWSEHzzQ+r4rYQBcvM1r2Axd3lPSF1JtOwow== -----END AGE ENCRYPTED FILE----- - recipient: age1tdwlq9y4jgejkhasqwynw5uaen9xwatcvr52l70trsdxkeyvlesqjnh7l8 enc: | -----BEGIN AGE ENCRYPTED FILE----- YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB3QlJ5anpOTTJzMkY3NU1v NW5yN3VXZEtEc1NDVmluelZTOWJEMUxLeUEwCm9GMmJybGlzZ2R1S0tVbHpWaXNj eUpTOU5JdmpFN3JjRmhlRWRyenJhK3cKLS0tIDJIa25XSnhXRjBvTWFTekxvRldH MjJGemlPV3J5UXRCcWFKWUR0cjBPeHMKd++ZcoSWXDlJ32v04VGB1bXtMMhwsPR9 1eEU5IWuNSFlPnuoohpBGq0E8NQ5iv2FT2pB6J4ecT6OmLEshPYwzg== -----END AGE ENCRYPTED FILE----- - recipient: age16p3zls5n0jks6amszwcuaqgl5dyuyf8k8wgeyrw562s5s88xtq3qq046fh enc: | -----BEGIN AGE ENCRYPTED FILE----- YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBsbm14WEdpTVl6VVQxNHJQ UEhUYXI4L2dUOGRnT2Z1amdTd1grUHVOSVJNCmNRZnhBV005S2ZLL05FZnBHMEt6 dE9GbXR5QmtJL1hWY20yYm9oNkFWY2cKLS0tIDZlUkNCWDNFM2ZSKzQyTytWeW4r ZTFWUWlYdGF6eTAyM2VVRXVqOVVkVG8K/d/gbOq9g/YzVZQtaLObxf3KScgUgWc8 avSzLzrCeaZSsKnlp0+Ty2nPan6xLYKHZunEuDhEkJufhRaUpBRyAw== -----END AGE ENCRYPTED FILE----- - recipient: age1v52mx8gs4ephprep0wcc4j3fvvprppvs9vktf2p24yv52sqsf33sd5crk9 enc: | -----BEGIN AGE ENCRYPTED FILE----- YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBtNFFHVmlvbUo3VTEyYWNp RkVxRE5oSXJRMUh5MklVenhEaE5SZm8wVFQ4CjU3WHhjL3JwTTVqUFp3dFRPNlpF ZzIxdEVZTXpQbktJSTVadDRPMXZqSmcKLS0tIEliQ0NQRVI2bXNFNHJjUlVpVXBI NG1UckhSSkxuanl5eEswZ0lDVWZ5Y1EKHGpTpMoRap1//A/bLP30HRBg6ThFOCbc wEcH38Vw3R8fjgLlPZp8rGDoiPIOJH6uDevQkkXeU8MetWNC8M97nA== -----END AGE ENCRYPTED FILE----- - recipient: age1k8ufac2s0gs6nh0xsfavafz062vd36petmyv6nwmg00z4a7s4gnsjtd837 enc: | -----BEGIN AGE ENCRYPTED FILE----- YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBvL3QyZ3h1VHVwQ0djUkl1 UjFsL2Rybk5PaXkyOElEbVRzcnFBd1QxeXlZClM1MjZ6RUgrSUZLZ2lOejdLQWJJ Smx6KzBkcG1ITzREblkxNUJRSmlBMGsKLS0tIFp2cnV6QmdmYTFKbnBJS2pFcE1y SzAwWVM1OElXU2RwWmp2UXRpUkhKSm8KnLOp1YUeLaxXt4lTDs2nCk815KDetxdq mhca1uhcXnrOpFOyKSLj5sAx/1v0JZv8Uz22j5kHa8IPo7+KJ0Yr4Q== -----END AGE ENCRYPTED FILE----- - recipient: age1dcsm5awz8ekzchk7gsvndkc4kq65n2wzgavxtqe53vxdsfk9k9pqh3whru enc: | -----BEGIN AGE ENCRYPTED FILE----- YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB3VkEraEdtZUxwUjdVRmQv MUN4SGo4RzhkOFY1OHRMQndNRW9FY21hREVJCnU3eVVTRjFlVUlHZjl4RERZS01l YzdmL2hmaDM0TzcvMEhRMmgxeE5kYjAKLS0tIFNQd1kySlE0aDZNQ2xJU2RuWGRI MWVrbTlOUDZrVDJUc0lOczRaM3FyN0EKOvVa7ubBozsa2TCMhdQLrRSe4iZmm6rd 6qYBWtlfiTaqHKgVi6MVrob9vPACrkegQgHIH/OAcIDubfY8kLGDZg== -----END AGE ENCRYPTED FILE----- lastmodified: "2025-02-18T04:57:27Z" mac: ENC[AES256_GCM,data:pWaGivxmqz289Q3afJKD7eiCmIhkZFyeVPBrS/noKA+azD6xHkLZG1thoQw7V5e1q1OqeMJzWMChXGTB4VC9kwKGC+/CwsJ+Xw/+ZaUj8ruUugEHKeAx41AkXxg+KOwejgHI5DCUksrHGUwjCEQKb5jWF2nLpld2QenaKyO90iw=,iv:jwweOgqiycYcoQLvl4IOyijEGNf/RarvYMTmG/PhYk4=,tag:aeERpYuTJT2TMpCZmAsjLA==,type:str] pgp: [] unencrypted_suffix: _unencrypted version: 3.9.4 ================================================ FILE: system/hosts/budimanjojo-oracle/_modules/services/blocky/config.yaml ================================================ --- upstreams: groups: default: - 1.1.1.1 - 8.8.8.8 # we let the adguardhome running in my home network to handle my own domains conditional: mapping: ${SECRET_DOMAIN_0}: 192.168.10.1 ${SECRET_DOMAIN_1}: 192.168.10.1 blocking: denylists: ads: - https://raw.githubusercontent.com/jerryn70/GoodbyeAds/master/Hosts/GoodbyeAds.txt - https://raw.githubusercontent.com/ABPindo/indonesianadblockrules/master/subscriptions/abpindo.txt allowlists: ads: - | s2.youtube.com graph.facebook.com i.instagram.com fonts.gstatic.com click.redditmail.com aypbpr.tokopedia.com plenty.vidio.com i.sgsnssdk.com clientGroupsBlock: default: - ads caching: cacheTimeNegative: -1 ================================================ FILE: system/hosts/budimanjojo-oracle/_modules/services/blocky/default.nix ================================================ { pkgs, config, ... }: let blockyUser = "blocky"; configFile = ./config.yaml; in { # this is enabled by default when `systemd.network.enable` is `true` # it listens on port 53 for stub DNS by default so we should just disable it services.resolved.enable = false; sops.secrets."blocky/env" = { sopsFile = ./secret.sops.yaml; owner = blockyUser; restartUnits = [ "blocky.service" ]; }; # add user, needed to access the secret users = { users.${blockyUser} = { isSystemUser = true; group = blockyUser; }; groups.${blockyUser} = { }; }; systemd.services.blocky = { description = "A DNS proxy and ad-blocker for the local network"; wantedBy = [ "multi-user.target" ]; # we do `envsubst` to substitute string like ${VAR} using the environment secrets preStart = '' ${pkgs.envsubst}/bin/envsubst -no-unset -i "${configFile}" -o "$RUNTIME_DIRECTORY/config.yaml" ''; serviceConfig = { EnvironmentFile = "${config.sops.secrets."blocky/env".path}"; User = blockyUser; RuntimeDirectory = "blocky"; ExecStart = "${pkgs.blocky}/bin/blocky --config /run/blocky/config.yaml"; Restart = "on-failure"; AmbientCapabilities = [ "CAP_NET_BIND_SERVICE" ]; CapabilityBoundingSet = [ "CAP_NET_BIND_SERVICE" ]; }; }; } ================================================ FILE: system/hosts/budimanjojo-oracle/_modules/services/blocky/secret.sops.yaml ================================================ blocky: env: ENC[AES256_GCM,data:wp3pLGX07GJnzWlhahn4ZfQsF7TVK7fmMJqdiPekygDHdHMxXT0NXKGUY8zSVRJcwSpE3TrmzHldTPuXSU/PHT6g,iv:WRVJACMx+57APiCRyBzt4CCnxXeSeIIpl5UrXT9r3dY=,tag:YIAAOcZi/45HU3m8+DtzxA==,type:str] sops: kms: [] gcp_kms: [] azure_kv: [] hc_vault: [] age: - recipient: age1zeqkpfz7e3s207ynea0z0auc0mrct0pc7w4sh6j3d0c4qac3dahqj9ufdg enc: | -----BEGIN AGE ENCRYPTED FILE----- YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBqRC93QTI3eWhwQ2ZsdUky UHFiY21yb3RWMFBCNUdkbFEwVTUyQ3BpUm13CkhIZkd4cHozdTR5YVJBdWZDcHg0 QW5OTkxqd2c2SEIyUkd2V2JpN2JVZDQKLS0tIHNRMFZpbDJaSDlsbWU3Z0dUcXBh ek5jTDRUaXUrU1lvNEdQdUdmQmFzRzQKjASIGxTeZr67bE5EvhwUG0qVTlyGW/Jd p3eXV+XEp5CgBnKgWZ4s8IBrNr0gASZj6d0b4Cj0MUNhgks55K668Q== -----END AGE ENCRYPTED FILE----- - recipient: age1tdwlq9y4jgejkhasqwynw5uaen9xwatcvr52l70trsdxkeyvlesqjnh7l8 enc: | -----BEGIN AGE ENCRYPTED FILE----- YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBvK2FnQVFYTnVzRjltazRC SG9qRTlyNlQvRTI2YklOZ0tPbDN2dFBHTW1NClZVQlVPK2lmUWdLZDZCOURYbTNj T3Vlbm9TazVoVkZKNnhISEVNQ01Ba00KLS0tIEVKYnlES0FRNzU3R1JkbjNoQ0dl YnJGbk5Zc2NaSlBacnVUTlJmdklVT1UKFCZtfeIxHFZqfFw8CbcW4G6AECC0W1LD 2fixufcXwoIOqPXhxszYB0BSlI3odtLbXb4LDwSpzdJuYSq77f79ig== -----END AGE ENCRYPTED FILE----- - recipient: age16p3zls5n0jks6amszwcuaqgl5dyuyf8k8wgeyrw562s5s88xtq3qq046fh enc: | -----BEGIN AGE ENCRYPTED FILE----- YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBIUTN0NzFaQ1VHRUZWa0hQ KzJHQWtWcHdJdjBObmZYbzJxYXpIcXdiUmhJCmVFT2UzbXhkU1BsUmZkY3cxak92 L0hCSDE5SWc5U2FGM3lJV3hBNGttRFEKLS0tIGMzQmdaSUdIRERNOGtCcmdXZldL WVpMU2xwL1I4K2J5djloNUMrbTNmTEUK5g9TLwVGqcchUHYcjf/lSS0xEruq44aq HKJ3EPYCCLexOFm2FzeZ4IC8eqkgKINaPZe2k6ZVcFqSckKAhuQ5jg== -----END AGE ENCRYPTED FILE----- - recipient: age1v52mx8gs4ephprep0wcc4j3fvvprppvs9vktf2p24yv52sqsf33sd5crk9 enc: | -----BEGIN AGE ENCRYPTED FILE----- YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBIYUhsZHRtYWF5ME0yWisw Y3NvRHlNTlFPTzlPR0l0SnlhT2wxVkZLeUZRCmJ6L1lVQXVyQ3FvQ3VyYk1ZSzVz Yk13S1gxcVg2TmxDYU10WUI3cDhPVkEKLS0tIEkveVVJTVdqcnI0MmRQTWhYYnFU WGc3ckVBbDhWeFpiSXBYT3V6NXN3QzgK5EDt6GoAsKfCIKd4ZxFFTGNYBR+xHXDG k2qgsKs8W8z+AI1qNou216fU1z/4tsK+kix1YQglYf3k164npCvVfA== -----END AGE ENCRYPTED FILE----- - recipient: age1k8ufac2s0gs6nh0xsfavafz062vd36petmyv6nwmg00z4a7s4gnsjtd837 enc: | -----BEGIN AGE ENCRYPTED FILE----- YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBsRUlwS3NTK1R3SkZOd0hC UVFwNkZWYW5IKzZ1L3dpRTFRQng4VFBiR0FFCnRETFNVTnRKZTQ2d2ZXT2RBUTVX YTNwMXkrWXh4YWsvTEZPNS9JeDlXRVUKLS0tIGRlZ1V4WU5kQ25WVzNvMjF1VEMw Q1VBMVl2UVcra29ieXUwV3E3RkwwN2MKFsytvsuLSFub/5xWjVto3GN+XaUNHvGg dVwyqo0mIwsC6Kvay7UaPQm/y8n2NmQRWpdDOLOe86s7IDzvXAvpHg== -----END AGE ENCRYPTED FILE----- - recipient: age1dcsm5awz8ekzchk7gsvndkc4kq65n2wzgavxtqe53vxdsfk9k9pqh3whru enc: | -----BEGIN AGE ENCRYPTED FILE----- YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB6OFk5c3ZSMjc1SzVYcStv RDVlRGl0NDY1U0lza0ZNRGxpMmF5WitSNUFnCit0RFZ3Um9lZWJGS1dOOVhhZUZz bjVUL3RuZjZKSUVOczFGblgva0RFencKLS0tIDFEbWxtWjJ3WFRkdklreC9YRlVH YUt0WVVEc2cvSFpQMWtMY1BVdkE3VXMKszlTBVsJlkmelUZLx6llgCNzcdncIqCK G+4heKmxGtYkigj+CRuW5Pdpl2Nu0dEsyuUpEFzwESo64elFfHQ69Q== -----END AGE ENCRYPTED FILE----- lastmodified: "2025-02-23T06:36:37Z" mac: ENC[AES256_GCM,data:ROQmlC2yiL1sADLXB/ejOMcEm08zD6TqUpry9twClwWvUKOljJWGoRB+oba6pGw4HKNdd/v1wCD/qwPM5Hr2jWSt/vEMC/iZg9XSQtjmLbuX4PvCv+td33z8HByY/MGm4U3Okp/7sulqCxm/Z6GWDagQkC6SEMEZO9SxOPI+j3E=,iv:UKx1fu5tbuSGA+6pecbPUMeJpHLP30zDugqv92V3Exw=,tag:23AYOjpw1slPxKd0xZrTag==,type:str] pgp: [] unencrypted_suffix: _unencrypted version: 3.9.4 ================================================ FILE: system/hosts/budimanjojo-oracle/_modules/services/qbittorrent/default.nix ================================================ { pkgs, ... }: let downloadsDir = "/var/qBittorrent-downloads"; qbitUser = "qbittorrent"; in { systemd.tmpfiles.settings.qbittorrent = { "${downloadsDir}".d = { mode = "755"; user = qbitUser; group = qbitUser; }; }; services.qbittorrent = { enable = true; package = pkgs.unstable.qbittorrent-nox; user = qbitUser; group = qbitUser; webuiPort = 8080; torrentingPort = 60413; serverConfig = { Preferences = { WebUI = { AuthSubnetWhitelistEnabled = true; AuthSubnetWhitelist = "10.10.10.12/32, 10.10.10.13/32, 192.168.50.49/32"; ClickjackingProtection = false; HostHeaderValidation = false; SecureCookie = false; CSRFProtection = false; }; Connection.UPnP = false; }; BitTorrent = { Session = { BTProtocol = "TCP"; Preallocation = true; DefaultSavePath = downloadsDir; DisableAutoTMMByDefault = false; DisableAutoTMMTriggers = { CategorySavePathChanged = false; DefaultSavePathChanged = false; }; MaxConnections = -1; MaxConnectionsPerTorrent = -1; MaxUploads = -1; MaxUploadsPerTorrent = -1; QueueingSystemEnabled = false; }; }; }; }; } ================================================ FILE: system/hosts/budimanjojo-oracle/_modules/wireguard.nix ================================================ { config, ... }: let homeNetworks = [ "192.168.10.0/24" # LAN0 network "192.168.50.0/24" # HOME network "192.168.69.0/24" # IOT network "192.168.200.0/24" # SERVER network "192.168.250.0/24" # GUEST network "10.5.0.0/24" # CONTAINER network "192.168.15.0/24" # k8s LB network ]; in { sops.secrets."wireguard/privatekey" = { sopsFile = ./secret.sops.yaml; owner = "systemd-network"; restartUnits = [ "systemd-networkd.service" ]; }; systemd.network = { netdevs."50-wg0" = { netdevConfig = { Name = "wg0"; Description = "WireGuard"; Kind = "wireguard"; MTUBytes = "1420"; }; wireguardConfig = { PrivateKeyFile = "${config.sops.secrets."wireguard/privatekey".path}"; ListenPort = 51821; RouteTable = "main"; }; wireguardPeers = [ { # budimanjojo-firewall PublicKey = "ZfOwDdOBpC2bpTp1pQl9Jlr0tBhm6njonXoJGU0xyBM="; AllowedIPs = [ "10.10.10.11/32" ] ++ homeNetworks; } { # pocof6-phone PublicKey = "WCESN/SAmvJekIxJSlmw+2FQg+uhvlseGhGvN/VZ0AU="; AllowedIPs = [ "10.10.10.12/32" ]; } { # work-pc PublicKey = "E+sLt3iBptnj6+7X/9S2ROglx8G3urufr18wCBt8bGQ="; AllowedIPs = [ "10.10.10.13/32" ]; } { # oneplus6t-phone PublicKey = "4g38+3wtUHHlrOCl/HsIM6i8p1QeoWgHtPh7hjcQx10="; AllowedIPs = [ "10.10.10.14/32" ]; } { # android-livingroom-tv PublicKey = "LYF3KLxBtfD1tIy2qS4Rl4COGlBLxFye7OaIUkZtfHM="; AllowedIPs = [ "10.10.10.15/32" ]; } { # qbit-gluetun PublicKey = "U1/0yWmjrRQapq+TWNv0mi1+gJkKkyBmN/ZtJWGbb0k="; AllowedIPs = [ "10.10.10.50/32" ]; } ]; }; networks = { "50-wg0" = { matchConfig.Name = "wg0"; address = [ "10.10.10.10/24" ]; networkConfig = { # IPMasquerade = "ipv4"; # we don't want to masquerade everything IPv4Forwarding = true; }; }; # we need to enable IP forwarding for outbound interface too "30-enp0s6".networkConfig.IPv4Forwarding = true; }; }; # this ensures the source address of peers are correctly forwarded to my # firewall server so I can set firewall rules for each peer while peers # still have access to the internet acting as this server networking.nftables = { enable = true; tables.wg_nat = { family = "ip"; content = '' set home_networks { type ipv4_addr flags interval elements = { ${builtins.concatStringsSep ", " homeNetworks} } } chain PREROUTING { type nat hook prerouting priority dstnat; policy accept; tcp dport 50413 counter dnat to 10.10.10.50 comment "port forward 50413 to qbit-gluetun" } chain POSTROUTING { type nat hook postrouting priority srcnat; policy accept; ip saddr 10.10.10.0/24 ip daddr != @home_networks masquerade } ''; }; }; } ================================================ FILE: system/hosts/budimanjojo-oracle/default.nix ================================================ { ... }: { imports = [ ./hardware-configuration.nix ./_modules # host specific modules ../../profiles/server.nix ]; config = { mySystem = { programs.nh = { enable = true; flake = "/home/budiman/Github/nix-config"; }; }; # sshd failed to start on boot when interface is not ready yet systemd.services.sshd = { after = [ "network-online.target" ]; wants = [ "network-online.target" ]; }; }; } ================================================ FILE: system/hosts/budimanjojo-oracle/hardware-configuration.nix ================================================ { modulesPath, lib, ... }: { imports = [ (modulesPath + "/profiles/qemu-guest.nix") ]; boot = { initrd = { availableKernelModules = [ "xhci_pci" "virtio_scsi" ]; }; }; fileSystems = { "/boot" = { device = "/dev/disk/by-uuid/C241-FDB6"; fsType = "vfat"; options = [ "fmask=0022" "dmask=0022" ]; }; "/" = { device = "/dev/disk/by-uuid/c85a6137-9a10-4e79-a5a5-86368db22114"; fsType = "ext4"; }; }; nixpkgs.hostPlatform = lib.mkDefault "aarch64-linux"; } ================================================ FILE: system/hosts/default.nix ================================================ { hostname, pkgs, ... }: { # host specific config imports = [ ./${hostname} ]; # global config applied to all hosts config = { environment.systemPackages = with pkgs; [ dig htop tree usbutils ]; }; } ================================================ FILE: system/hosts/nixos-livecd/default.nix ================================================ { modulesPath, pkgs, myPkgs, hostname, ... }: let pubkeys = import ../../pubkeys; in { imports = [ (modulesPath + "/installer/cd-dvd/installation-cd-minimal.nix") ]; config = { # faster build time isoImage.squashfsCompression = "gzip -Xcompression-level 1"; # enable SSH in the boot process systemd.services.sshd.wantedBy = pkgs.lib.mkForce [ "multi-user.target" ]; users.users.nixos.openssh.authorizedKeys.keys = [ pubkeys.main-pc ]; environment.systemPackages = [ myPkgs.neovim pkgs.gitMinimal ]; networking.hostName = hostname; time.timeZone = "Asia/Jakarta"; security = { sudo.wheelNeedsPassword = false; }; }; } ================================================ FILE: system/profiles/gaming.nix ================================================ { ... }: { config = { programs.gamemode.enable = true; }; } ================================================ FILE: system/profiles/server.nix ================================================ { ... }: let pubkeys = import ../pubkeys; in { config = { mySystem = { system.autoupgrade.enable = true; services.openssh = { enable = true; authorizedKeys = [ pubkeys.work-pc pubkeys.main-pc pubkeys.op9-termux ]; }; monitoring = { node-exporter.enable = true; smartctl-exporter.enable = true; }; }; }; } ================================================ FILE: system/profiles/work.nix ================================================ { ... }: { config = { mySystem.containers.beeaccounting.enable = true; }; } ================================================ FILE: system/profiles/workstation-common.nix ================================================ { ... }: { config = { mySystem = { programs = { adb.enable = true; hugo.enable = true; qmk.enable = true; }; monitoring.smartctl-exporter.enable = true; }; boot.plymouth.enable = true; }; } ================================================ FILE: system/profiles/workstation-hyprland.nix ================================================ { ... }: { imports = [ ./workstation-common.nix ]; config.mySystem.windowmanager.hyprland.enable = true; } ================================================ FILE: system/profiles/workstation-i3.nix ================================================ { ... }: { imports = [ ./workstation-common.nix ]; config.mySystem.windowmanager.i3.enable = true; } ================================================ FILE: system/profiles/workstation-sway.nix ================================================ { ... }: { imports = [ ./workstation-common.nix ]; config.mySystem.windowmanager.sway.enable = true; } ================================================ FILE: system/pubkeys/default.nix ================================================ # this contains all public keys of my machines { work-pc = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDIgzLDHHRtLD9DLfNTX8Te9zBcDmEF33UPJ5HngP6aJAhdQln7ebSOzQoehxN0th644G9csnujxjNAStIEUjzeXO1NQQfEYGqDVxDL0jE4hXr1WVg+6GxQnV1nWP5Sd2i24+ElygCdw3KuteeNlfGZ7BKs91zySb03DICXPNcgXj6ZR9INalabFhbjeVG5MRH38KRR9cxZKbgW+eQZZwVtDRPzL7rAfaeeJPg7ZQ3Iu0SC3q5SskGQD5XfqCwPDx9n0GWva36jwNneifv5WFDh0U+xKaoVT4HJTWyV/vf3+fTji1yEsGMBbPexuD5aHvmun9SdgIlGw65GJB7Ibz47Bq/jVfnTV6o5BVDhEfwayHZgahODl5Uyc3VqkKoJh9IWivoBr/bLHXepiJGUReEw61nBc3JL9QC4J5ngterLXP/iapl185+JSvUzDjbtFDHLiXCqa8X17Cm9LSIKik/W7gM2tU63QcQd/p8H55he/Kgm94vWJlq98rjLCtBYTNQDSNAU6AFQqk2c3x23L09wSRIQJ1aUEq6aPx3yfbiRHyTslTyP4tg0I1U1o+jjh9tZV/+JpRcwg9xi9YLoGMAv9aUVGHodjehZw1wHnuql3ALiy/Nnm2LANDh4vhJ2fKsrBqhk8dyDdqFHFsLyTSXUAE4NKGG1AUV3fgMjvRRZZQ=="; main-pc = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDJVJ+gHYFCoyd1WG8uJ2d0ErwmyASjhqv8ZXblNBHMjTSNXtqxTZ/MIIAPkxi3XSSFIcAGO7nr5myKyQVfiFlPAQMDR64iTfL6N/peMi25xE5+MhQKTIcJBSlRYx3RjJmx9JwmDuT8aXlPqL16QWk8WVe5XErNAXrsj+PrTISlS9O8wbqKExcC2jTzieg4L3II4cBv+M7QySt8ApzLJ5geYniO4mc5Jqor1hBZakQDw8vJdohOgnjVK+MIvVFm97iVmhNXUCgqFn3EGXVGLAB8J8h2aix8WvkP85WmvJ4nZ9k22QvijBr3WHQdwU/VoZ7xWpXVcGsXwnlD9tpGUotx"; op9-termux = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDwktTcrOt7pytCS3C8qWg+eRsOjVgEHLJ5iOufmsa6dlmvqYroalwCgvN0YcjoqPdg53S5F12C3voV3nvRhRPB0AEQQYlWEynSFdc/omCktA7NqHcj/Wb/r+52Bq5xVqjXJDLT3f4YCE3pectoaiKgKIS+OoNMOErFmnxO/uSetNrvJLIr6DrxLRW/lZ+xPDiN1eIHM1ETfk9CxIYjD3UztBPAa6qfahchqd64DS8UevLNlvc/SxPniqaZfaxlmk1qMUfvTSPJAcMBSVK2EasHd7tUZhbHvoC+F9723fwtxedTFofgw3iZHWjz8pL7ngFNA4NpyUdDSId3mydPYk0E/rY8WVbknvGlRBjI1OnXWHTOZty26vcoNYKfjP/+1Q3TINVIkvjhNCmS6UuUGfBtqB8uJkUHi4wVYkXLHsK5vUxM664bkeCezHbkR7o17Sa0YbINL510UOi+PLUPEHk98yKh+Xlw77kc0EEQdvmRDTVI+YBmsJ83brtouc2cXm0="; }