Repository: pinpox/nixos Branch: main Commit: 3537c4ac42ef Files: 402 Total size: 758.3 KB Directory structure: gitextract_pevrk36z/ ├── .envrc ├── .github/ │ ├── FUNDING.yml │ └── workflows/ │ ├── check-upstream-todos.yml │ └── manual.yml ├── .gitignore ├── .pre-commit-config.yaml ├── .woodpecker/ │ └── x86-64-linux.yaml ├── LICENSE ├── README.md ├── clan-service-modules/ │ ├── machine-type/ │ │ ├── default.nix │ │ ├── desktop.nix │ │ ├── mobile.nix │ │ ├── nextcloud-desktop.nix │ │ ├── scanners.nix │ │ └── server.nix │ ├── monitoring/ │ │ ├── alert-rules.nix │ │ ├── alertmanager-irc-relay.nix │ │ ├── blackbox.nix │ │ ├── default.nix │ │ ├── grafana.nix │ │ ├── loki.nix │ │ ├── node-exporter.nix │ │ └── prometheus.nix │ ├── navidrome.nix │ └── thelounge.nix ├── flake.nix ├── formatter.nix ├── home-manager/ │ ├── colorscheme.nix │ ├── modules/ │ │ ├── audio-recording/ │ │ │ └── default.nix │ │ ├── calendar/ │ │ │ └── default.nix │ │ ├── chromium/ │ │ │ └── default.nix │ │ ├── claude-code/ │ │ │ └── default.nix │ │ ├── credentials/ │ │ │ ├── age-recipients │ │ │ └── default.nix │ │ ├── easyeffects/ │ │ │ └── default.nix │ │ ├── email/ │ │ │ ├── aerc-style.nix │ │ │ └── default.nix │ │ ├── firefox/ │ │ │ ├── default.nix │ │ │ └── userchrome.css.mustache │ │ ├── fonts/ │ │ │ └── default.nix │ │ ├── foot/ │ │ │ └── default.nix │ │ ├── games/ │ │ │ └── default.nix │ │ ├── git/ │ │ │ └── default.nix │ │ ├── go/ │ │ │ └── default.nix │ │ ├── grobi/ │ │ │ └── default.nix │ │ ├── gtk/ │ │ │ ├── banana.nix │ │ │ └── default.nix │ │ ├── helix/ │ │ │ └── default.nix │ │ ├── k9s/ │ │ │ └── default.nix │ │ ├── kanshi/ │ │ │ └── default.nix │ │ ├── mako/ │ │ │ └── default.nix │ │ ├── mpv/ │ │ │ └── default.nix │ │ ├── neomutt/ │ │ │ └── default.nix │ │ ├── newsboat/ │ │ │ └── default.nix │ │ ├── obs-studio/ │ │ │ └── default.nix │ │ ├── pandoc/ │ │ │ └── default.nix │ │ ├── pi-mono/ │ │ │ └── default.nix │ │ ├── rio/ │ │ │ ├── config/ │ │ │ │ └── rio/ │ │ │ │ ├── config.toml │ │ │ │ └── themes/ │ │ │ │ ├── pinpox-dark.toml │ │ │ │ ├── pinpox-light.toml │ │ │ │ ├── wildcharm-dark.toml │ │ │ │ └── wildcharm-light.toml │ │ │ └── default.nix │ │ ├── river/ │ │ │ ├── default.nix │ │ │ ├── json.lua │ │ │ ├── layout.lua │ │ │ └── river-config │ │ ├── shell/ │ │ │ ├── default.nix │ │ │ ├── fish.nix │ │ │ ├── starship.nix │ │ │ ├── zellij-chooser │ │ │ ├── zsh.nix │ │ │ ├── zshrc │ │ │ ├── zshrc-coffee │ │ │ └── zshrc-extra │ │ ├── ssh/ │ │ │ ├── default.nix │ │ │ ├── ssh-key-cert.pub │ │ │ └── ssh-key.pub │ │ ├── sway/ │ │ │ └── default.nix │ │ ├── swaylock/ │ │ │ ├── default.nix │ │ │ └── style.css │ │ ├── taskwarrior/ │ │ │ └── default.nix │ │ ├── theme-switcher/ │ │ │ ├── default.nix │ │ │ └── toggle-theme.sh │ │ ├── tmux/ │ │ │ ├── default.nix │ │ │ └── tmux.conf │ │ ├── waybar/ │ │ │ ├── default.nix │ │ │ └── style.css │ │ ├── xdg/ │ │ │ └── default.nix │ │ ├── zed/ │ │ │ └── default.nix │ │ ├── zellij/ │ │ │ └── default.nix │ │ └── zk/ │ │ ├── config.toml │ │ ├── default.md │ │ ├── default.nix │ │ └── journal.md │ └── profiles/ │ ├── common.nix │ ├── desktop/ │ │ └── default.nix │ ├── mobile/ │ │ └── default.nix │ └── server/ │ └── default.nix ├── images/ │ ├── configuration.nix │ └── raspi.nix ├── inventory.json ├── inventory.nix ├── machines/ │ ├── birne/ │ │ ├── configuration.nix │ │ └── hardware-configuration.nix │ ├── clementine/ │ │ ├── configuration.nix │ │ ├── disko.nix │ │ └── facter.json │ ├── fichte/ │ │ ├── configuration.nix │ │ ├── disko-config-btrfs.nix │ │ └── facter.json │ ├── kartoffel/ │ │ ├── configuration.nix │ │ ├── hardware-configuration.nix │ │ └── retiolum.nix │ ├── kfbox/ │ │ ├── configuration.nix │ │ ├── hardware-configuration.nix │ │ └── retiolum.nix │ ├── kiwi/ │ │ ├── configuration.nix │ │ ├── disko-config-btrfs.nix │ │ ├── framework.nix │ │ ├── ollama-local.nix │ │ └── opencrow-geninf.nix │ ├── limette/ │ │ ├── configuration.nix │ │ ├── disko-config-btrfs.nix │ │ └── disko-config-zfs.nix │ ├── porree/ │ │ ├── blog.nix │ │ ├── caddy.nix │ │ ├── configuration.nix │ │ ├── hardware-configuration.nix │ │ ├── nostr/ │ │ │ └── nostr.json │ │ └── retiolum.nix │ ├── tanne/ │ │ ├── configuration.nix │ │ └── disko-config-btrfs.nix │ ├── traube/ │ │ ├── configuration.nix │ │ ├── disko.nix │ │ └── facter.json │ └── uconsole/ │ ├── configuration.nix │ └── disko-config.nix ├── modules/ │ ├── bluetooth/ │ │ └── default.nix │ ├── caddy-security/ │ │ └── default.nix │ ├── calibre-web/ │ │ └── default.nix │ ├── ci/ │ │ └── default.nix │ ├── clan-common/ │ │ └── default.nix │ ├── environment/ │ │ └── default.nix │ ├── fonts/ │ │ └── default.nix │ ├── forgejo/ │ │ └── default.nix │ ├── gitea/ │ │ └── default.nix │ ├── hedgedoc/ │ │ └── default.nix │ ├── hello/ │ │ ├── default.nix │ │ └── test.nix │ ├── home-assistant/ │ │ └── default.nix │ ├── http2irc/ │ │ └── default.nix │ ├── immich/ │ │ └── default.nix │ ├── jitsi-matrix-presence/ │ │ └── default.nix │ ├── kf-homepage/ │ │ ├── default.nix │ │ └── page/ │ │ └── index.html │ ├── locale/ │ │ └── default.nix │ ├── lvm-grub/ │ │ └── default.nix │ ├── miniflux/ │ │ └── default.nix │ ├── minio/ │ │ ├── default.nix │ │ └── policies/ │ │ ├── nextcloud-external.json │ │ └── restic.json │ ├── networking/ │ │ └── default.nix │ ├── nextcloud/ │ │ └── default.nix │ ├── nix-common/ │ │ └── default.nix │ ├── ntfy-sh/ │ │ └── default.nix │ ├── opencloud/ │ │ └── default.nix │ ├── opencrow/ │ │ └── default.nix │ ├── openssh/ │ │ ├── ca.pub │ │ └── default.nix │ ├── owncast/ │ │ └── default.nix │ ├── paperless/ │ │ ├── default.nix │ │ └── django-apps/ │ │ └── paperless_perms/ │ │ ├── __init__.py │ │ └── apps.py │ ├── radio/ │ │ └── default.nix │ ├── restic/ │ │ └── default.nix │ ├── screego/ │ │ └── default.nix │ ├── sound/ │ │ └── default.nix │ ├── storagebox/ │ │ └── default.nix │ ├── twitch-first/ │ │ └── default.nix │ ├── unbound-desktop/ │ │ └── default.nix │ ├── unifi-controller/ │ │ └── default.nix │ ├── vaultwarden/ │ │ └── default.nix │ ├── vikunja/ │ │ └── default.nix │ ├── virtualisation/ │ │ └── default.nix │ ├── wastebin/ │ │ └── default.nix │ ├── wayland/ │ │ └── default.nix │ ├── web-vm/ │ │ └── default.nix │ ├── yubikey/ │ │ └── default.nix │ └── zsh/ │ └── default.nix ├── overlays/ │ ├── default.nix │ └── nextcloud.patch ├── packages/ │ ├── fritzbox_exporter/ │ │ └── default.nix │ ├── groups-relay/ │ │ └── default.nix │ ├── hello-custom/ │ │ └── default.nix │ ├── machine-report/ │ │ ├── default.nix │ │ └── machine_report.sh │ ├── manual/ │ │ ├── default.nix │ │ └── template.html │ ├── mqtt2prometheus/ │ │ └── default.nix │ ├── noctalia-askpass/ │ │ └── default.nix │ ├── raspi-image │ ├── river-luatile/ │ │ └── default.nix │ ├── smartmon-script/ │ │ ├── default.nix │ │ └── smartmon.py │ ├── woodpecker-pipeline/ │ │ └── default.nix │ ├── zsh-abbrev-alias/ │ │ └── default.nix │ ├── zsh-async/ │ │ └── default.nix │ └── zsh-colored-man-pages/ │ └── default.nix ├── templates/ │ └── default/ │ └── flake.nix ├── users/ │ ├── pinpox-wraps/ │ │ ├── chromium/ │ │ │ └── default.nix │ │ └── ffmpeg/ │ │ └── default.nix │ ├── pinpox.nix │ └── root.nix ├── utils/ │ └── default.nix └── vars/ ├── per-machine/ │ ├── birne/ │ │ ├── data-mesher-node-identity/ │ │ │ ├── identity.pub/ │ │ │ │ └── value │ │ │ └── peer.id/ │ │ │ └── value │ │ ├── dm-pull-deploy-status-key/ │ │ │ └── signing.pub/ │ │ │ └── value │ │ ├── minio/ │ │ │ └── .validation-hash │ │ ├── restic-server/ │ │ │ └── .validation-hash │ │ ├── state-version/ │ │ │ └── version/ │ │ │ └── value │ │ ├── wireguard/ │ │ │ └── publickey/ │ │ │ └── value │ │ ├── wireguard-wg-clan/ │ │ │ ├── ipv4/ │ │ │ │ └── value │ │ │ └── publickey/ │ │ │ └── value │ │ ├── wireguard-wg-clan-ip/ │ │ │ └── ipv4/ │ │ │ └── value │ │ └── yggdrasil/ │ │ ├── address/ │ │ │ └── value │ │ └── publicKey/ │ │ └── value │ ├── clementine/ │ │ ├── data-mesher-node-identity/ │ │ │ ├── identity.pub/ │ │ │ │ └── value │ │ │ └── peer.id/ │ │ │ └── value │ │ ├── dm-pull-deploy-status-key/ │ │ │ └── signing.pub/ │ │ │ └── value │ │ ├── punchcard/ │ │ │ └── .validation-hash │ │ ├── punchcard2/ │ │ │ └── .validation-hash │ │ ├── state-version/ │ │ │ └── version/ │ │ │ └── value │ │ ├── trippy-track/ │ │ │ └── .validation-hash │ │ ├── twitch-first/ │ │ │ └── .validation-hash │ │ ├── wireguard-wg-clan/ │ │ │ └── publickey/ │ │ │ └── value │ │ ├── wireguard-wg-clan-ip/ │ │ │ └── ipv4/ │ │ │ └── value │ │ ├── wireguard-wg-tunnel/ │ │ │ └── publickey/ │ │ │ └── value │ │ ├── wireguard-wg-tunnel-ip/ │ │ │ └── ipv4/ │ │ │ └── value │ │ ├── yggdrasil/ │ │ │ ├── address/ │ │ │ │ └── value │ │ │ └── publicKey/ │ │ │ └── value │ │ └── zerotier/ │ │ ├── zerotier-ip/ │ │ │ └── value │ │ └── zerotier-network-id/ │ │ └── value │ ├── fichte/ │ │ ├── data-mesher-node-identity/ │ │ │ ├── identity.pub/ │ │ │ │ └── value │ │ │ └── peer.id/ │ │ │ └── value │ │ ├── dm-pull-deploy-status-key/ │ │ │ └── signing.pub/ │ │ │ └── value │ │ ├── state-version/ │ │ │ └── version/ │ │ │ └── value │ │ └── yggdrasil/ │ │ ├── address/ │ │ │ └── value │ │ └── publicKey/ │ │ └── value │ ├── kartoffel/ │ │ ├── data-mesher-node-identity/ │ │ │ ├── identity.pub/ │ │ │ │ └── value │ │ │ └── peer.id/ │ │ │ └── value │ │ ├── dm-pull-deploy-status-key/ │ │ │ └── signing.pub/ │ │ │ └── value │ │ ├── state-version/ │ │ │ └── version/ │ │ │ └── value │ │ ├── wireguard/ │ │ │ └── publickey/ │ │ │ └── value │ │ ├── wireguard-wg-clan/ │ │ │ └── publickey/ │ │ │ └── value │ │ ├── wireguard-wg-clan-ip/ │ │ │ └── ipv4/ │ │ │ └── value │ │ └── yggdrasil/ │ │ ├── address/ │ │ │ └── value │ │ └── publicKey/ │ │ └── value │ ├── kfbox/ │ │ ├── caddy/ │ │ │ └── .validation-hash │ │ ├── cert-irc/ │ │ │ ├── .validation-hash │ │ │ ├── irc.crt/ │ │ │ │ └── value │ │ │ └── irc.fullchain.crt/ │ │ │ └── value │ │ ├── cert-music/ │ │ │ ├── .validation-hash │ │ │ ├── music.crt/ │ │ │ │ └── value │ │ │ └── music.fullchain.crt/ │ │ │ └── value │ │ ├── data-mesher-host-key/ │ │ │ └── public_key/ │ │ │ └── value │ │ ├── data-mesher-node-identity/ │ │ │ ├── identity.pub/ │ │ │ │ └── value │ │ │ └── peer.id/ │ │ │ └── value │ │ ├── dex/ │ │ │ └── .validation-hash │ │ ├── dm-pull-deploy-status-key/ │ │ │ └── signing.pub/ │ │ │ └── value │ │ ├── go-karma-bot/ │ │ │ └── .validation-hash │ │ ├── hedgedoc/ │ │ │ └── .validation-hash │ │ ├── jitsi-presence/ │ │ │ └── .validation-hash │ │ ├── restic-exporter/ │ │ │ └── .validation-hash │ │ ├── state-version/ │ │ │ └── version/ │ │ │ └── value │ │ ├── vikunja/ │ │ │ └── .validation-hash │ │ ├── wireguard/ │ │ │ └── publickey/ │ │ │ └── value │ │ ├── wireguard-wg-clan/ │ │ │ ├── ipv4/ │ │ │ │ └── value │ │ │ └── publickey/ │ │ │ └── value │ │ ├── wireguard-wg-clan-ip/ │ │ │ └── ipv4/ │ │ │ └── value │ │ ├── wireguard-wg-star/ │ │ │ ├── ipv6/ │ │ │ │ └── value │ │ │ └── publickey/ │ │ │ └── value │ │ └── yggdrasil/ │ │ ├── address/ │ │ │ └── value │ │ └── publicKey/ │ │ └── value │ ├── kiwi/ │ │ ├── cert-claw/ │ │ │ ├── .validation-hash │ │ │ ├── claw.crt/ │ │ │ │ └── value │ │ │ └── claw.fullchain.crt/ │ │ │ └── value │ │ ├── data-mesher-host-key/ │ │ │ └── public_key/ │ │ │ └── value │ │ ├── data-mesher-node-identity/ │ │ │ ├── identity.pub/ │ │ │ │ └── value │ │ │ └── peer.id/ │ │ │ └── value │ │ ├── dm-pull-deploy-status-key/ │ │ │ └── signing.pub/ │ │ │ └── value │ │ ├── openclaw/ │ │ │ └── .validation-hash │ │ ├── opencrow/ │ │ │ └── .validation-hash │ │ ├── opencrow-email/ │ │ │ └── .validation-hash │ │ ├── opencrow-eversports/ │ │ │ └── .validation-hash │ │ ├── opencrow-geninf/ │ │ │ └── pubkey/ │ │ │ └── value │ │ ├── opencrow-geninf-user/ │ │ │ └── pubkey/ │ │ │ └── value │ │ ├── opencrow-nextcloud/ │ │ │ └── .validation-hash │ │ ├── opencrow-nextcloud-work/ │ │ │ └── .validation-hash │ │ ├── opencrow-nostr-bot/ │ │ │ └── nostr-public-key/ │ │ │ └── value │ │ ├── opencrow-nostr-user/ │ │ │ └── nostr-public-key/ │ │ │ └── value │ │ ├── state-version/ │ │ │ └── version/ │ │ │ └── value │ │ ├── tor_tor/ │ │ │ └── hostname/ │ │ │ └── value │ │ ├── wireguard-wg-clan/ │ │ │ ├── ipv4/ │ │ │ │ └── value │ │ │ └── publickey/ │ │ │ └── value │ │ ├── wireguard-wg-clan-ip/ │ │ │ └── ipv4/ │ │ │ └── value │ │ ├── wireguard-wg-star/ │ │ │ ├── ipv6/ │ │ │ │ └── value │ │ │ └── publickey/ │ │ │ └── value │ │ ├── wireguard-wg-tunnel/ │ │ │ └── publickey/ │ │ │ └── value │ │ ├── wireguard-wg-tunnel-ip/ │ │ │ └── ipv4/ │ │ │ └── value │ │ ├── yggdrasil/ │ │ │ ├── address/ │ │ │ │ └── value │ │ │ └── publicKey/ │ │ │ └── value │ │ └── zerotier/ │ │ └── zerotier-ip/ │ │ └── value │ ├── limette/ │ │ ├── data-mesher-node-identity/ │ │ │ ├── identity.pub/ │ │ │ │ └── value │ │ │ └── peer.id/ │ │ │ └── value │ │ ├── dm-pull-deploy-status-key/ │ │ │ └── signing.pub/ │ │ │ └── value │ │ ├── state-version/ │ │ │ └── version/ │ │ │ └── value │ │ ├── wireguard/ │ │ │ └── publickey/ │ │ │ └── value │ │ ├── wireguard-wg-clan/ │ │ │ └── publickey/ │ │ │ └── value │ │ ├── wireguard-wg-clan-ip/ │ │ │ └── ipv4/ │ │ │ └── value │ │ └── yggdrasil/ │ │ ├── address/ │ │ │ └── value │ │ └── publicKey/ │ │ └── value │ ├── porree/ │ │ ├── alertmanager-ntfy/ │ │ │ └── .validation-hash │ │ ├── caddy/ │ │ │ └── .validation-hash │ │ ├── caddy-basicauth/ │ │ │ └── .validation-hash │ │ ├── cert-prometheus/ │ │ │ ├── .validation-hash │ │ │ ├── prometheus.crt/ │ │ │ │ └── value │ │ │ └── prometheus.fullchain.crt/ │ │ │ └── value │ │ ├── cert-status/ │ │ │ ├── .validation-hash │ │ │ ├── status.crt/ │ │ │ │ └── value │ │ │ └── status.fullchain.crt/ │ │ │ └── value │ │ ├── data-mesher-node-identity/ │ │ │ ├── identity.pub/ │ │ │ │ └── value │ │ │ └── peer.id/ │ │ │ └── value │ │ ├── dm-pull-deploy-status-key/ │ │ │ └── signing.pub/ │ │ │ └── value │ │ ├── matrix-hook/ │ │ │ └── .validation-hash │ │ ├── opencrow/ │ │ │ └── .validation-hash │ │ ├── opencrow-email/ │ │ │ └── .validation-hash │ │ ├── opencrow-eversports/ │ │ │ └── .validation-hash │ │ ├── opencrow-local/ │ │ │ └── .validation-hash │ │ ├── opencrow-nextcloud/ │ │ │ └── .validation-hash │ │ ├── opencrow-nextcloud-work/ │ │ │ └── .validation-hash │ │ ├── restic-exporter/ │ │ │ └── .validation-hash │ │ ├── state-version/ │ │ │ └── version/ │ │ │ └── value │ │ ├── tor_tor/ │ │ │ └── hostname/ │ │ │ └── value │ │ ├── vaultwarden/ │ │ │ └── .validation-hash │ │ ├── wireguard/ │ │ │ └── publickey/ │ │ │ └── value │ │ ├── wireguard-wg-clan/ │ │ │ └── publickey/ │ │ │ └── value │ │ ├── wireguard-wg-clan-ip/ │ │ │ └── ipv4/ │ │ │ └── value │ │ ├── wireguard-wg-star/ │ │ │ ├── ipv6/ │ │ │ │ └── value │ │ │ └── publickey/ │ │ │ └── value │ │ └── yggdrasil/ │ │ ├── address/ │ │ │ └── value │ │ └── publicKey/ │ │ └── value │ ├── tanne/ │ │ ├── data-mesher-node-identity/ │ │ │ ├── identity.pub/ │ │ │ │ └── value │ │ │ └── peer.id/ │ │ │ └── value │ │ ├── dm-pull-deploy-status-key/ │ │ │ └── signing.pub/ │ │ │ └── value │ │ ├── state-version/ │ │ │ └── version/ │ │ │ └── value │ │ └── yggdrasil/ │ │ ├── address/ │ │ │ └── value │ │ └── publicKey/ │ │ └── value │ ├── traube/ │ │ ├── data-mesher-node-identity/ │ │ │ ├── identity.pub/ │ │ │ │ └── value │ │ │ └── peer.id/ │ │ │ └── value │ │ ├── dm-pull-deploy-status-key/ │ │ │ └── signing.pub/ │ │ │ └── value │ │ ├── opencrow/ │ │ │ └── .validation-hash │ │ ├── opencrow-traube/ │ │ │ └── .validation-hash │ │ ├── state-version/ │ │ │ └── version/ │ │ │ └── value │ │ └── yggdrasil/ │ │ ├── address/ │ │ │ └── value │ │ └── publicKey/ │ │ └── value │ ├── tunnelmonster/ │ │ ├── abiotic-docker/ │ │ │ └── .validation-hash │ │ ├── porkbun-dns/ │ │ │ └── .validation-hash │ │ └── state-version/ │ │ └── version/ │ │ └── value │ └── uconsole/ │ ├── data-mesher-node-identity/ │ │ ├── identity.pub/ │ │ │ └── value │ │ └── peer.id/ │ │ └── value │ ├── dm-pull-deploy-status-key/ │ │ └── signing.pub/ │ │ └── value │ ├── state-version/ │ │ └── version/ │ │ └── value │ ├── wireguard-wg-clan/ │ │ └── publickey/ │ │ └── value │ ├── wireguard-wg-clan-ip/ │ │ └── ipv4/ │ │ └── value │ └── yggdrasil/ │ ├── address/ │ │ └── value │ └── publicKey/ │ └── value └── shared/ ├── bulletin-signing-key/ │ └── signing.pub/ │ └── value ├── data-mesher-ca/ │ └── ca.pub/ │ └── value ├── data-mesher-network/ │ └── network.pub/ │ └── value ├── data-mesher-network-key/ │ └── public_key/ │ └── value ├── data-mesher-signing-key/ │ └── signing.pub/ │ └── value ├── dm-dns/ │ ├── .validation-hash │ └── zone.conf/ │ └── value ├── dm-dns-signing-key/ │ └── signing.pub/ │ └── value ├── dm-pull-deploy-signing-key/ │ └── signing.pub/ │ └── value ├── dm-wallpaper-signing-key/ │ └── signing.pub/ │ └── value ├── dm-wg-star-wg-star-signing-key/ │ └── signing.pub/ │ └── value ├── dns-mesher/ │ ├── .validation-hash │ ├── entries/ │ │ └── value │ └── zone.conf/ │ └── value ├── pki-root-ca/ │ └── ca.crt/ │ └── value ├── restic-cert/ │ ├── .validation-hash │ └── restic-cert/ │ └── value ├── restic-credentials/ │ └── .validation-hash ├── restic-credentials-backblaze/ │ └── .validation-hash ├── restic-kfbox/ │ └── .validation-hash ├── sftp-credentials/ │ └── ssh-public-key/ │ └── value ├── step-ca/ │ └── ca.crt/ │ └── value ├── storagebox-ssh/ │ └── ssh-public-key/ │ └── value └── zerotier-controller/ ├── zerotier-ip/ │ └── value └── zerotier-network-id/ └── value ================================================ FILE CONTENTS ================================================ ================================================ FILE: .envrc ================================================ use flake ================================================ FILE: .github/FUNDING.yml ================================================ github: [pinpox] buy_me_a_coffee: pinpox ================================================ FILE: .github/workflows/check-upstream-todos.yml ================================================ name: Check Upstream TODOs on: push: branches: [main] pull_request: branches: [main] schedule: - cron: "0 0 * * 0" # Weekly on Sunday jobs: check-todos: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Check for resolved upstream TODOs run: | #!/bin/bash set -e # Find all TODO comments with GitHub PR/issue links echo "Searching for TODO comments with GitHub links..." # Search for TODO (with or without colon) followed by GitHub URLs TODO_PATTERN="TODO:?\s*https://github\.com/[^/]+/[^/]+/(pull|issues)/[0-9]+" # Find all matches and extract URLs TODOS=$(grep -r -n -E "$TODO_PATTERN" . --exclude-dir=.git --exclude-dir=.github || true) if [ -z "$TODOS" ]; then echo "No TODO comments with GitHub links found." exit 0 fi echo "Found TODO comments:" echo "$TODOS" echo "" # Extract unique URLs from the matches URLS=$(echo "$TODOS" | grep -oE "https://github\.com/[^/]+/[^/]+/(pull|issues)/[0-9]+" | sort -u) FAILED=false FAILED_LOCATIONS="" for URL in $URLS; do echo "Checking: $URL" # Find all locations where this URL appears LOCATIONS=$(echo "$TODOS" | grep -F "$URL" || true) # Extract owner, repo, type (pull or issues), and number from URL if [[ $URL =~ https://github\.com/([^/]+)/([^/]+)/(pull|issues)/([0-9]+) ]]; then OWNER="${BASH_REMATCH[1]}" REPO="${BASH_REMATCH[2]}" TYPE="${BASH_REMATCH[3]}" NUMBER="${BASH_REMATCH[4]}" # Use GitHub API to check status if [ "$TYPE" == "pull" ]; then echo " Checking PR #$NUMBER in $OWNER/$REPO..." API_URL="https://api.github.com/repos/$OWNER/$REPO/pulls/$NUMBER" STATE=$(curl -s -H "Accept: application/vnd.github.v3+json" "$API_URL" | jq -r '.state // "unknown"') MERGED=$(curl -s -H "Accept: application/vnd.github.v3+json" "$API_URL" | jq -r '.merged // false') if [ "$STATE" == "closed" ] && [ "$MERGED" == "true" ]; then echo " ❌ FAIL: PR #$NUMBER is MERGED! Remove TODO and workaround." echo " Locations:" while IFS= read -r line; do echo " $line" done <<< "$LOCATIONS" FAILED=true FAILED_LOCATIONS="$FAILED_LOCATIONS$LOCATIONS"$'\n' elif [ "$STATE" == "closed" ] && [ "$MERGED" == "false" ]; then echo " ⚠️ PR #$NUMBER is closed but not merged." elif [ "$STATE" == "open" ]; then echo " ✅ PR #$NUMBER is still open." else echo " ⚠️ Could not determine PR status (state: $STATE, merged: $MERGED)" fi else echo " Checking Issue #$NUMBER in $OWNER/$REPO..." API_URL="https://api.github.com/repos/$OWNER/$REPO/issues/$NUMBER" STATE=$(curl -s -H "Accept: application/vnd.github.v3+json" "$API_URL" | jq -r '.state // "unknown"') if [ "$STATE" == "closed" ]; then echo " ❌ FAIL: Issue #$NUMBER is CLOSED! Remove TODO and workaround." echo " Locations:" while IFS= read -r line; do echo " $line" done <<< "$LOCATIONS" FAILED=true FAILED_LOCATIONS="$FAILED_LOCATIONS$LOCATIONS"$'\n' elif [ "$STATE" == "open" ]; then echo " ✅ Issue #$NUMBER is still open." else echo " ⚠️ Could not determine issue status (state: $STATE)" fi fi else echo " ⚠️ Could not parse URL: $URL" fi echo "" done if [ "$FAILED" == "true" ]; then echo "❌ One or more upstream issues/PRs have been resolved!" echo "Please review and remove the corresponding TODO comments and workarounds." echo "" echo "Summary of resolved TODOs:" echo "$FAILED_LOCATIONS" exit 1 else echo "✅ All upstream issues/PRs are still unresolved." fi env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} ================================================ FILE: .github/workflows/manual.yml ================================================ name: Build and Deploy Manual on: push: branches: - main jobs: tests: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v3 - name: Install Nix uses: cachix/install-nix-action@v20 - name: Build manual run: | nix build '.#manual' --show-trace -vv -L mkdir docs curl https://nixos.org/favicon.ico -o docs/favicon.ico cp result/index.html docs/index.html - name: Deploy 🚀 uses: JamesIves/github-pages-deploy-action@4.1.0 with: branch: gh-pages # The branch the action should deploy to. folder: docs # The folder the action should deploy. ================================================ FILE: .gitignore ================================================ result ### Vim # Swap [._]*.s[a-v][a-z] !*.svg # comment out if you don't need vector files [._]*.sw[a-p] [._]s[a-rt-v][a-z] [._]ss[a-gi-z] [._]sw[a-p] # Session Session.vim Sessionx.vim # Temporary .netrwhist *~ # Auto-generated tag files tags # Persistent undo [._]*.un~ tags.lock tags.temp .direnv .env ================================================ FILE: .pre-commit-config.yaml ================================================ repos: - repo: local hooks: - id: treefmt name: treefmt entry: nix fmt language: system pass_filenames: false # - id: nix lint # name: Nix lint # entry: nix run 'nixpkgs#nix-linter' # files: \.nix$ # language: system ================================================ FILE: .woodpecker/x86-64-linux.yaml ================================================ { "labels": { "backend": "local", "platform": "linux/amd64" }, "steps": [ { "commands": [ "nix flake show" ], "image": "bash", "name": "Nix flake show" }, { "commands": [ "attic login lounge-rocks https://cache.lounge.rocks $ATTIC_KEY --set-default" ], "environment": { "ATTIC_KEY": { "from_secret": "attic_key" } }, "image": "bash", "name": "Setup Attic" }, { "commands": [ "nix-fast-build --no-nom --skip-cached --attic-cache lounge-rocks:nix-cache --flake \".#ciBuilds.x86_64-linux\"" ], "image": "bash", "name": "Build all machines and push to cache" }, { "commands": [ "nix build --print-out-paths '.#ciBuilds.x86_64-linux.birne' -o 'result-birne'" ], "failure": "ignore", "image": "bash", "name": "Build birne" }, { "commands": [ "nix path-info --closure-size -h $(readlink -f 'result-birne')" ], "failure": "ignore", "image": "bash", "name": "Show birne info" }, { "commands": [ "nix build --print-out-paths '.#ciBuilds.x86_64-linux.clementine' -o 'result-clementine'" ], "failure": "ignore", "image": "bash", "name": "Build clementine" }, { "commands": [ "nix path-info --closure-size -h $(readlink -f 'result-clementine')" ], "failure": "ignore", "image": "bash", "name": "Show clementine info" }, { "commands": [ "nix build --print-out-paths '.#ciBuilds.x86_64-linux.fichte' -o 'result-fichte'" ], "failure": "ignore", "image": "bash", "name": "Build fichte" }, { "commands": [ "nix path-info --closure-size -h $(readlink -f 'result-fichte')" ], "failure": "ignore", "image": "bash", "name": "Show fichte info" }, { "commands": [ "nix build --print-out-paths '.#ciBuilds.x86_64-linux.kartoffel' -o 'result-kartoffel'" ], "failure": "ignore", "image": "bash", "name": "Build kartoffel" }, { "commands": [ "nix path-info --closure-size -h $(readlink -f 'result-kartoffel')" ], "failure": "ignore", "image": "bash", "name": "Show kartoffel info" }, { "commands": [ "nix build --print-out-paths '.#ciBuilds.x86_64-linux.kfbox' -o 'result-kfbox'" ], "failure": "ignore", "image": "bash", "name": "Build kfbox" }, { "commands": [ "nix path-info --closure-size -h $(readlink -f 'result-kfbox')" ], "failure": "ignore", "image": "bash", "name": "Show kfbox info" }, { "commands": [ "nix build --print-out-paths '.#ciBuilds.x86_64-linux.kiwi' -o 'result-kiwi'" ], "failure": "ignore", "image": "bash", "name": "Build kiwi" }, { "commands": [ "nix path-info --closure-size -h $(readlink -f 'result-kiwi')" ], "failure": "ignore", "image": "bash", "name": "Show kiwi info" }, { "commands": [ "nix build --print-out-paths '.#ciBuilds.x86_64-linux.limette' -o 'result-limette'" ], "failure": "ignore", "image": "bash", "name": "Build limette" }, { "commands": [ "nix path-info --closure-size -h $(readlink -f 'result-limette')" ], "failure": "ignore", "image": "bash", "name": "Show limette info" }, { "commands": [ "nix build --print-out-paths '.#ciBuilds.x86_64-linux.porree' -o 'result-porree'" ], "failure": "ignore", "image": "bash", "name": "Build porree" }, { "commands": [ "nix path-info --closure-size -h $(readlink -f 'result-porree')" ], "failure": "ignore", "image": "bash", "name": "Show porree info" }, { "commands": [ "nix build --print-out-paths '.#ciBuilds.x86_64-linux.tanne' -o 'result-tanne'" ], "failure": "ignore", "image": "bash", "name": "Build tanne" }, { "commands": [ "nix path-info --closure-size -h $(readlink -f 'result-tanne')" ], "failure": "ignore", "image": "bash", "name": "Show tanne info" } ], "when": [ { "event": "manual" }, { "branch": "main", "event": "push" } ] } ================================================ FILE: LICENSE ================================================ GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . ================================================ FILE: README.md ================================================ ![nixos](https://socialify.git.ci/pinpox/nixos/image?description=1&font=Source%20Code%20Pro&forks=1&issues=1&logo=https%3A%2F%2Fpablo.tools%2Fnixoscolorful.svg&owner=1&pattern=Circuit%20Board&pulls=1&stargazers=1&theme=Light) **Configuration checks:** [![Build Status](https://build.lounge.rocks/api/badges/9/status.svg)](https://build.lounge.rocks/repos/9) All Module options are documented at: https://pinpox.github.io/nixos/ This repository includes all configurations for my NixOS machines. Feel free to use parts of it as you please, but keep it mind it is intended mostly for personal use. I've written posts about certain aspects of this setup on my [personal blog](https://pablo.tools/posts). # Overview The structure of this repository is meant to allow easy manual deployment while being [clan](https://clan.lol) compatible. Individual hosts are defined in `/machines/` and import re-usable parts of the configuration as needed. Deployment and management is done with [clan](https://clan.lol), and secrets are stored in [passage](https://github.com/FiloSottile/passage), which uses age for encryption. The current hosts are: | Configuration | Type | Location | Description | | --------------------------------- | ------- | --------- | ------------------------------------ | | [kartoffel](./machines/kartoffel) | Desktop | local | Main desktop workstation | | [kiwi](./machines/kiwi) | Laptop | local | Framework 13 (AMD AI 300 series) | | [limette](./machines/limette) | Laptop | local | Notebook | | [fichte](./machines/fichte) | Laptop | local | ThinkPad T490 | | [tanne](./machines/tanne) | Laptop | local | ThinkPad T480s | | [uconsole](./machines/uconsole) | Handheld| local | Clockwork uConsole (RPi CM4) | | [birne](./machines/birne) | Server | local | Local NAS | | [porree](./machines/porree) | Server | netcup.de | Server for pablo.tools | | [kfbox](./machines/kfbox) | Server | netcup.de | Server for 0cx.de | | [clementine](./machines/clementine) | Server | netcup.de | Server for megaclan3000.de | | [traube](./machines/traube) | Server | local | aarch64 server | # Deployment Deployment is done via [clan CLI](https://clan.lol) provided via the flake's default nix shell. I use [direnv](https://direnv.net/) to automatically start it when entering the repository's directory. Run `direnv allow` on the first time, after that, deployment can be done via: ```sh clan machines update ``` ## Repository Organization The configuration is organized as follows: - `machines/`: Host-specific configurations - `modules`: System-level NixOS modules - `home-manager/modules`: User-level home-manager modules for specific applications - `home-manager/profiles`: Profiles that combine multiple home-manager modules - `home-manager/packages`: Custom packages for applications not present in nixpkgs - `clan-service-modules`: [Clan](https://clan.lol) services # Contributing? While contributions don't make much sense for a personal configuration repository, I'm always happy to get hints, tips and constructive criticism. If you find something that could be done in a better way, please let me know! Buy Me A Coffee ================================================ FILE: clan-service-modules/machine-type/default.nix ================================================ { _class = "clan.service"; manifest.name = "machine-type"; manifest.readme = "Machine classification/profiles"; roles.server.perInstance.nixosModule = ./server.nix; roles.server.description = "Server machine settings, no GUI"; roles.desktop.perInstance.nixosModule = ./desktop.nix; roles.desktop.description = "Desktop machine settings, including wayland and sway"; roles.mobile.perInstance.nixosModule = ./mobile.nix; roles.mobile.description = "Mobile/ARM device settings, lightweight desktop"; # Common configuration for all macine types perMachine.nixosModule = { lib, ... }: { security.acme.acceptTerms = true; security.acme.defaults.email = lib.mkDefault "letsencrypt@pablo.tools"; clan.core.settings.state-version.enable = true; hardware.enableRedistributableFirmware = true; hardware.enableAllHardware = true; }; } ================================================ FILE: clan-service-modules/machine-type/desktop.nix ================================================ { config, pkgs, lib, nur, flake-self, wl-harmonograph, promterm, home-manager, ... }: { imports = [ ./nextcloud-desktop.nix home-manager.nixosModules.home-manager ]; services.fwupd.enable = true; services.acpid.enable = true; services.upower.enable = true; # To build raspi images boot.binfmt.emulatedSystems = [ "aarch64-linux" ]; # Enable networkmanager networking.networkmanager.enable = true; # Often hangs systemd.services = { NetworkManager-wait-online.enable = lib.mkForce false; systemd-networkd-wait-online.enable = lib.mkForce false; }; hardware.keyboard.qmk.enable = true; services.udev.packages = [ # pkgs.via pkgs.qmk-udev-rules # For QMK/Via pkgs.libsigrok # For pulseview ]; # DON'T set useGlobalPackages! It's not necessary in newer # home-manager versions and does not work with configs using # nixpkgs.config` home-manager.useUserPackages = true; # Backup files before overwriting them with home-manager home-manager.backupFileExtension = "hm-backup"; # Pass all flake inputs to home-manager modules aswell so we can use them # there. # home-manager.extraSpecialArgs = flake-self.inputs; home-manager.extraSpecialArgs = { inherit wl-harmonograph flake-self nur promterm ; # Pass system configuration (top-level "config") to home-manager modules, # so we can access it's values for conditional statements. Writing is NOT possible! system-config = config; }; nixpkgs.overlays = [ nur.overlays.default flake-self.overlays.default ]; # TODO parametrize the username home-manager.users.pinpox = flake-self.homeConfigurations.desktop; # Hardware accelleration hardware.graphics = { enable = true; enable32Bit = true; }; pinpox = { defaults = { bluetooth.enable = true; environment.enable = true; storagebox = { enable = true; mountOnAccess = true; }; fonts.enable = true; locale.enable = true; locale.automatic-timezone = true; networking.enable = true; nix.enable = true; sound.enable = true; zsh.enable = true; yubikey.enable = true; lvm-grub.enable = true; # home-manager.enable = true; # home-manager.configuration = flake-self.homeConfigurations.desktop; }; virtualisation = { docker.enable =false; virt-manager.enable = true; virtualbox.enable = false; }; services = { unbound-desktop.enable = false; wayland.enable = true; openssh.enable = true; restic-client = { enable = true; backup-paths-onsite = [ "/home/pinpox/Notes" "/home/pinpox" "/home/pinpox/.mozilla/firefox/pinpox/places.sqlite" # "*/.local/share/password-store" # "*/.passage" # "*/.gnupg" # "*/.ssh" ]; backup-paths-offsite = [ "/home/pinpox/.mozilla/firefox/pinpox/places.sqlite" "/home/pinpox/Notes" "/home/pinpox" ]; }; }; }; # List packages installed in system profile. To search, run: # $ nix search wget environment.systemPackages = with pkgs; [ # irc-announce irc.hackint.org 6697 testbot992 '#lounge-rocks2' 1 "test2" # pkgs.nur.repos.mic92.irc-announce firefox openspec gcc comma acpi arandr binutils file git gnumake killall lm_sensors neovim nixpkgs-review nix-init nix-update nodejs ripgrep time usbutils wget ]; services.logind.settings.Login.RuntimeDirectorySize = "20G"; boot = { # Use GRUB2 as EFI boot loader. loader.grub.useOSProber = true; tmp.useTmpfs = false; }; programs.dconf.enable = true; # For user-space mounting things like smb:// and ssh:// in thunar etc. Dbus # is required. services.gvfs = { enable = true; # Default package does not support all protocols. Use the full-featured # gnome version package = lib.mkForce pkgs.gnome.gvfs; }; } ================================================ FILE: clan-service-modules/machine-type/mobile.nix ================================================ { config, pkgs, lib, nur, flake-self, promterm, home-manager, ... }: { imports = [ home-manager.nixosModules.home-manager ]; services.acpid.enable = true; services.upower.enable = true; # US QWERTY for TTY (mobile devices don't have colemak keyboards) console.keyMap = lib.mkForce "us"; # Enable networkmanager networking.networkmanager.enable = true; # Often hangs systemd.services = { NetworkManager-wait-online.enable = lib.mkForce false; systemd-networkd-wait-online.enable = lib.mkForce false; }; # DON'T set useGlobalPackages! It's not necessary in newer # home-manager versions and does not work with configs using # nixpkgs.config` home-manager.useUserPackages = true; # Backup files before overwriting them with home-manager home-manager.backupFileExtension = "hm-backup"; # Pass all flake inputs to home-manager modules aswell so we can use them # there. home-manager.extraSpecialArgs = { inherit flake-self nur promterm ; # Pass system configuration (top-level "config") to home-manager modules, # so we can access it's values for conditional statements. Writing is NOT possible! system-config = config; }; nixpkgs.overlays = [ nur.overlays.default flake-self.overlays.default ]; # TODO parametrize the username home-manager.users.pinpox = flake-self.homeConfigurations.mobile; # Hardware accelleration hardware.graphics = { enable = true; }; pinpox = { defaults = { bluetooth.enable = true; environment.enable = true; fonts.enable = false; # Disabled - noto-fonts-color-emoji can't cross-compile locale.enable = true; locale.automatic-timezone = true; networking.enable = true; nix.enable = true; sound.enable = false; zsh.enable = true; }; services = { wayland.enable = true; openssh.enable = true; }; }; # List packages installed in system profile environment.systemPackages = with pkgs; [ gcc comma acpi binutils file git gnumake killall neovim ripgrep time usbutils wget ]; programs.dconf.enable = true; # Disable speech-dispatcher to avoid pulling in mbrola-voices (675MB) services.speechd.enable = lib.mkForce false; # Override fontconfig defaults to avoid noto-fonts-color-emoji # which requires Python tools that can't cross-compile fonts.fontconfig.defaultFonts.emoji = lib.mkForce [ ]; # Disable default font packages (includes noto-fonts-color-emoji) fonts.enableDefaultPackages = false; # Disable modemmanager - cross-compilation is broken networking.modemmanager.enable = false; } ================================================ FILE: clan-service-modules/machine-type/nextcloud-desktop.nix ================================================ { # Nextcloud on the desktop services.davfs2 = { enable = true; # settings.globalSection.use_locks = false; # TODO: Note: Ordinary users can mount a davfs2 file system if they are a # member of the group dav_group as defined in the system wide # configuration. Make sure the option 'dav_group' is enabled in the system # wide configuration file. }; # fileSystems."/home/pinpox/Nextcloud" = { # device = "https://files.pablo.tools/remote.php/dav/files/pinpox"; # fsType = "davfs"; # options = [ # "user" # "rw" # "noauto" # I'll mount it manually # ]; # }; } ================================================ FILE: clan-service-modules/machine-type/scanners.nix ================================================ { hardware.sane.enable = true; users.users.pinpox.extraGroups = [ "scanner" "lp" ]; } ================================================ FILE: clan-service-modules/machine-type/server.nix ================================================ { restic-exporter, lib, pkgs, config, ... }: with lib; { imports = [ restic-exporter.nixosModules.default ]; # Limit log size for journal services.journald.extraConfig = "SystemMaxUse=1G"; environment.systemPackages = with pkgs; [ universal-ctags git gnumake go htop neovim nix-index nixfmt ripgrep wget ncdu duf tmux ]; pinpox.defaults = { environment.enable = true; locale.enable = true; nix.enable = true; zsh.enable = true; networking.enable = true; }; pinpox.services.openssh.enable = true; # Backups pinpox.services = { restic-client = { enable = true; backup-paths-onsite = [ config.services.postgresqlBackup.location "/home" "/root" ]; }; }; # Backup Postgres, if it is running services.postgresqlBackup = { enable = config.services.postgresql.enable; startAt = "*-*-* 01:15:00"; location = "/var/backup/postgresql"; backupAll = true; }; # Remove wayland dependencies on server machine type nixpkgs.overlays = [ (final: super: { passage = super.passage.override { wl-clipboard = null; xclip = null; }; neovim = super.neovim.override { waylandSupport = false; }; }) ]; } ================================================ FILE: clan-service-modules/monitoring/alert-rules.nix ================================================ { lib, meta }: let # docker's filesystems disappear quickly, leading to false positives deviceFilter = ''path!~"^(/var/lib/docker|/nix/store).*"''; in lib.mapAttrsToList (name: opts: { alert = name; expr = opts.condition; for = opts.time or "2m"; labels = { }; annotations.description = opts.description; }) ({ # prometheus_too_many_restarts = { # condition = ''changes(process_start_time_seconds{job=~"prometheus|alertmanager"}[15m]) > 2''; # description = "Prometheus has restarted more than twice in the last 15 minutes. It might be crashlooping."; # }; # alert_manager_config_not_synced = { # condition = ''count(count_values("config_hash", alertmanager_config_hash)) > 1''; # description = "Configurations of AlertManager cluster instances are out of sync."; # }; #alert_manager_e2e_dead_man_switch = { # condition = "vector(1)"; # description = "Prometheus DeadManSwitch is an always-firing alert. It's used as an end-to-end test of Prometheus through the Alertmanager."; #}; # prometheus_not_connected_to_alertmanager = { # condition = "prometheus_notifications_alertmanagers_discovered < 1"; # description = "Prometheus cannot connect the alertmanager\n VALUE = {{ $value }}\n LABELS = {{ $labels }}"; # }; # prometheus_rule_evaluation_failures = { # condition = "increase(prometheus_rule_evaluation_failures_total[3m]) > 0"; # description = "Prometheus encountered {{ $value }} rule evaluation failures, leading to potentially ignored alerts.\n VALUE = {{ $value }}\n LABELS = {{ $labels }}"; # }; # prometheus_template_expansion_failures = { # condition = "increase(prometheus_template_text_expansion_failures_total[3m]) > 0"; # time = "0m"; # description = "Prometheus encountered {{ $value }} template text expansion failures\n VALUE = {{ $value }}\n LABELS = {{ $labels }}"; # }; # promtail_file_lagging = { # condition = ''abs(promtail_file_bytes_total - promtail_read_bytes_total) > 1e6''; # time = "15m"; # description = ''{{ $labels.instance }} {{ $labels.job }} {{ $labels.path }} has been lagging by more than 1MB for more than 15m.''; # }; filesystem_full_80percent = { condition = ''100 - ((node_filesystem_avail_bytes{fstype!="rootfs",mountpoint="/"} * 100) / node_filesystem_size_bytes{fstype!="rootfs",mountpoint="/"}) > 80''; time = "10m"; description = "{{$labels.instance}} device {{$labels.device}} on {{$labels.mountpoint}} got less than 20% space left on its filesystem."; }; # filesystem_inodes_full = { # condition = ''disk_inodes_free / disk_inodes_total < 0.10''; # time = "10m"; # description = "{{$labels.instance}} device {{$labels.device}} on {{$labels.mountpoint}} got less than 10% inodes left on its filesystem."; # }; # daily_task_not_run = { # # give 6 hours grace period # condition = ''time() - task_last_run{state="ok",frequency="daily"} > (24 + 6) * 60 * 60''; # description = "{{$labels.instance}}: {{$labels.name}} was not run in the last 24h"; # }; # TODO for backups: # - Has not run # - No data for a wheek # - Too old # - to drastic file or size change # description = "status of ${name} is unknown: no data for a day"; # description = "{{$labels.instance}}: {{$labels.name}} was not run in the last week"; # description = "status of restic is unknown: no data for a week"; # homeassistant = { # condition = '' # homeassistant_entity_available{domain="persistent_notification", entity!="persistent_notification.http_login"} >= 0''; # description = # "homeassistant notification {{$labels.entity}} ({{$labels.friendly_name}}): {{$value}}"; # }; swap_using_20percent = { condition = "node_memory_SwapTotal_bytes - (node_memory_SwapCached_bytes + node_memory_SwapFree_bytes) > node_memory_SwapTotal_bytes * 0.2"; time = "30m"; description = "{{$labels.instance}} is using 20% of its swap space for at least 30 minutes."; }; systemd_service_failed = { condition = ''node_systemd_unit_state{state="failed"} == 1''; description = "{{$labels.instance}} failed to (re)start service {{$labels.name}}."; }; restic_backup_too_old = { condition = ''(time() - restic_snapshots_latest_time)/(60*60) > 24''; description = "{{$labels.instance}} not backed up for more than 24 hours. ({{$value}})"; }; host_down = { condition = ''up{job="node-stats", instance!~"limette.${meta.domain}:9100|kartoffel.${meta.domain}:9100"} == 0''; description = "{{$labels.instance}} is down!"; }; # service_not_running = { # condition = ''systemd_units_active_code{name=~"teamspeak3-server.service|tt-rss.service", sub!="running"}''; # description = "{{$labels.instance}} should have a running {{$labels.name}}."; # }; ram_using_90percent = { condition = "node_memory_Buffers_bytes + node_memory_MemFree_bytes + node_memory_Cached_bytes < node_memory_MemTotal_bytes * 0.1"; time = "1h"; description = "{{$labels.instance}} is using at least 90% of its RAM for at least 1 hour."; }; cpu_using_90percent = { condition = ''100 - (avg by (instance) (irate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) >= 90''; time = "10m"; description = "{{$labels.instance}} is running with cpu usage > 90% for at least 10 minutes: {{$value}}"; }; reboot = { condition = "node_boot_time_seconds < 300"; description = "{{$labels.instance}} just rebooted."; }; uptime = { condition = "(time() - node_boot_time_seconds ) / (60*60*24) > 30"; description = "Uptime monster: {{$labels.instance}} has been up for more than 30 days."; }; flake_nixpkgs_outdated = { condition = ''(time() - flake_input_last_modified{input="nixpkgs"}) / (60*60*24) > 30''; description = "Nixpkgs outdated: Nixpkgs on {{$labels.instance}} has not been updated in 30 days"; }; /* ping = { condition = "ping_result_code{type!='mobile'} != 0"; description = "{{$labels.url}}: ping from {{$labels.instance}} has failed!"; }; ping_high_latency = { condition = "ping_average_response_ms{type!='mobile'} > 5000"; description = "{{$labels.instance}}: ping probe from {{$labels.source}} is encountering high latency!"; }; */ http_status = { condition = ''probe_http_status_code{instance!~"https://megaclan3000.de"} != 200''; description = "http request failed from {{$labels.instance}}: {{$labels.result}}!"; }; /* http_match_failed = { condition = "http_response_response_string_match == 0"; description = "{{$labels.server}} : http body not as expected; status code: {{$labels.status_code}}!"; }; dns_query = { condition = "dns_query_result_code != 0"; description = "{{$labels.domain}} : could retrieve A record {{$labels.instance}} from server {{$labels.server}}: {{$labels.result}}!"; }; secure_dns_query = { condition = "secure_dns_state != 0"; description = "{{$labels.domain}} : could retrieve A record {{$labels.instance}} from server {{$labels.server}}: {{$labels.result}} for protocol {{$labels.protocol}}!"; }; connection_failed = { condition = "net_response_result_code != 0"; description = "{{$labels.server}}: connection to {{$labels.port}}({{$labels.protocol}}) failed from {{$labels.instance}}"; }; healthchecks = { condition = "hc_check_up == 0"; description = "{{$labels.instance}}: healtcheck {{$labels.job}} fails!"; }; */ cert_expiry = { condition = "(probe_ssl_earliest_cert_expiry - time())/(3600*24) < 30"; description = "{{$labels.instance}}: The TLS certificate will expire in less than 30 days: {{$value}}s"; }; # ignore devices that disabled S.M.A.R.T (example if attached via USB) # smart_errors = { # condition = ''smart_device_health_ok{enabled!="Disabled"} != 1''; # description = # "{{$labels.instance}}: S.M.A.R.T reports: {{$labels.device}} ({{$labels.model}}) has errors."; # }; oom_kills = { condition = "increase(node_vmstat_oom_kill[5m]) > 0"; description = "{{$labels.instance}}: OOM kill detected"; }; /* unusual_disk_read_latency = { condition = "rate(diskio_read_time[1m]) / rate(diskio_reads[1m]) > 0.1 and rate(diskio_reads[1m]) > 0"; description = '' {{$labels.instance}}: Disk latency is growing (read operations > 100ms) ''; }; unusual_disk_write_latency = { condition = "rate(diskio_write_time[1m]) / rate(diskio_write[1m]) > 0.1 and rate(diskio_write[1m]) > 0"; description = '' {{$labels.instance}}: Disk latency is growing (write operations > 100ms) ''; }; */ host_memory_under_memory_pressure = { condition = "rate(node_vmstat_pgmajfault[1m]) > 1000"; description = "{{$labels.instance}}: The node is under heavy memory pressure. High rate of major page faults: {{$value}}"; }; # ext4_errors = { # condition = "ext4_errors_value > 0"; # description = # "{{$labels.instance}}: ext4 has reported {{$value}} I/O errors: check /sys/fs/ext4/*/errors_count"; # }; # alerts_silences_changed = { # condition = ''abs(delta(alertmanager_silences{state="active"}[1h])) >= 1''; # description = # "alertmanager: number of active silences has changed: {{$value}}"; # }; }) ================================================ FILE: clan-service-modules/monitoring/alertmanager-irc-relay.nix ================================================ { pkgs, config, ... }: let am-irc-conf = { # listening host/port. http_host = "localhost"; http_port = 8667; # Connect to this IRC host/port. irc_host = "irc.hackint.org"; irc_port = 6697; # irc_host_password = "myserver_password"; irc_nickname = "alertus-maximus"; irc_nickname_password = "mynickserv_key"; irc_realname = "myrealname"; irc_channels = [ { name = "#lounge-rocks-log"; } ]; msg_once_per_alert_group = false; # Use PRIVMSG instead of NOTICE (default) to send messages. use_privmsg = true; # Define how IRC messages should be formatted. msg_template = "⚠ ⚠ ⚠ [{{.Labels.instance}}] - {{ .Labels.alertname }} is {{.Status}} ⚠ ⚠ ⚠ {{.Annotations.description}} (@pinpox act accordingly)"; # Note: When sending only one message per alert group the default # msg_template is set to # "Alert {{ .GroupLabels.alertname }} for {{ .GroupLabels.job }} is {{ .Status }}" # Set the internal buffer size for alerts received but not yet sent to IRC. alert_buffer_size = 2048; }; confPath = pkgs.writeText "config.yml" (builtins.toJSON am-irc-conf); in { # User and group users.groups."alertmanager-irc-relay" = { }; users.users."alertmanager-irc-relay" = { isSystemUser = true; group = "alertmanager-irc-relay"; }; # Service systemd.services.alertmanager-irc-relay = { wantedBy = [ "multi-user.target" ]; serviceConfig = { ExecStart = "${pkgs.alertmanager-irc-relay}/bin/alertmanager-irc-relay --config ${confPath}"; User = config.users.users.alertmanager-irc-relay.name; Group = config.users.users.alertmanager-irc-relay.name; }; }; } ================================================ FILE: clan-service-modules/monitoring/blackbox.nix ================================================ { pkgs, ... }: { services.prometheus.exporters.blackbox = { enable = true; # Default port is 9115 # Listen on 0.0.0.0, but we only open the firewall for wg-clan openFirewall = false; configFile = pkgs.writeTextFile { name = "blackbox-exporter-config"; text = '' modules: http_2xx: prober: http timeout: 5s http: valid_http_versions: ["HTTP/1.1", "HTTP/2.0"] valid_status_codes: [] # Defaults to 2xx method: GET no_follow_redirects: false fail_if_ssl: false fail_if_not_ssl: false tls_config: insecure_skip_verify: false preferred_ip_protocol: "ip4" # defaults to "ip6" ip_protocol_fallback: true # fallback to "ip6" ''; }; }; networking.firewall.interfaces.wg-clan.allowedTCPPorts = [ 9115 ]; } ================================================ FILE: clan-service-modules/monitoring/default.nix ================================================ { lib, ... }: { _class = "clan.service"; manifest.name = "monitoring"; manifest.description = "Prometheus/Loki/Grafana monitoring stack"; manifest.readme = '' Self-hosted monitoring with one role per component (prometheus, loki, grafana, blackbox, node-exporter, alertmanager-irc-relay) ''; manifest.categories = [ "Monitoring" ]; manifest.exports.out = [ "endpoints" "auth" ]; roles.node-exporter = { description = "Prometheus node-exporter for host metrics (assign to every host you want scraped)"; perInstance.nixosModule = ./node-exporter.nix; }; roles.loki = { description = "Loki log aggregation server"; perInstance.nixosModule = ./loki.nix; }; roles.grafana = { description = "Grafana dashboard server"; interface = { lib, meta, ... }: { options = { domain = lib.mkOption { type = lib.types.str; default = "status.${meta.domain}"; example = "dashboards.example.com"; description = "Domain for grafana"; }; oidc = { enable = lib.mkEnableOption "OIDC-only authentication (e.g. via Authelia). Disables the local login form."; issuer = lib.mkOption { type = lib.types.str; default = ""; example = "https://auth.example.com"; description = "Base URL of the OIDC provider (Authelia). Used to derive auth/token/userinfo endpoints."; }; clientId = lib.mkOption { type = lib.types.str; default = "grafana"; description = "OIDC client ID registered with the provider"; }; providerName = lib.mkOption { type = lib.types.str; default = "Authelia"; description = "Display name shown on the Grafana login button"; }; }; }; }; perInstance = { settings, roles, meta, mkExports, ... }: let # TODO The generator shoudl be independeat of authelia or kanidm. Use a dependant one for authelia to create the hash # TODO maybe we do not need a generato cofig at all, have a default? # Generator definition without runtimeInputs (pkgs isn't available # at inventory-eval time). Each nixosModule that declares this # generator adds runtimeInputs locally where pkgs IS available. oidcGenerator = lib.optionalAttrs settings.oidc.enable { share = true; files.client_secret = { owner = "grafana"; group = "authelia-main"; mode = "0440"; }; files.client_secret_hash.owner = "authelia-main"; script = '' mkdir -p $out openssl rand -hex 32 > $out/client_secret authelia crypto hash generate argon2 --password "$(cat $out/client_secret)" \ | sed 's/^Digest: //' > $out/client_secret_hash ''; }; in { exports = mkExports ( { endpoints.hosts = [ settings.domain ]; } // lib.optionalAttrs settings.oidc.enable { auth.client = { clientId = settings.oidc.clientId; clientName = "Grafana"; redirectUris = [ "https://${settings.domain}/login/generic_oauth" ]; scopes = [ "openid" "profile" "email" "groups" ]; public = false; }; auth.varsGenerator = oidcGenerator; } ); nixosModule = import ./grafana.nix { inherit settings roles meta oidcGenerator; }; }; }; roles.blackbox = { description = "Prometheus blackbox-exporter for HTTP/TLS probes"; perInstance.nixosModule = ./blackbox.nix; }; roles.alertmanager-irc-relay = { description = "Relay alertmanager notifications to an IRC channel"; perInstance.nixosModule = ./alertmanager-irc-relay.nix; }; roles.prometheus = { description = "Prometheus server (with bundled alertmanager). Discovers node-exporter and blackbox role members automatically."; interface = { lib, meta, ... }: { options = { domain = lib.mkOption { type = lib.types.str; default = "prometheus.${meta.domain}"; example = "metrics.example.com"; description = "Domain for the prometheus web UI (reverse proxied via caddy)"; }; blackboxTargets = lib.mkOption { type = lib.types.listOf lib.types.str; default = [ "https://pablo.tools" ]; example = [ "https://github.com" ]; description = "Targets to monitor with the blackbox-exporter"; }; jsonTargets = lib.mkOption { type = lib.types.listOf lib.types.str; default = [ ]; example = [ "http://birne.pin/restic-ahorn.json" ]; description = "Targets to probe with the json-exporter"; }; webExternalUrl = lib.mkOption { type = lib.types.str; default = "https://vpn.prometheus.pablo.tools"; description = "Prometheus web external URL"; }; alertmanagerWebExternalUrl = lib.mkOption { type = lib.types.str; default = "https://vpn.alerts.pablo.tools"; description = "Alertmanager web external URL"; }; }; }; perInstance = { settings, roles, meta, mkExports, ... }: let # oauth2-proxy OIDC generator definition (without runtimeInputs — # added by each nixosModule where pkgs is available). # Produces: client_secret (raw), client_secret_hash (argon2 for # authelia), envfile (oauth2-proxy env with client+cookie secrets). prometheusOidcGenerator = { share = true; files.client_secret = { owner = "oauth2_proxy"; group = "authelia-main"; mode = "0440"; }; files.client_secret_hash.owner = "authelia-main"; files.envfile = { owner = "oauth2_proxy"; mode = "0400"; }; script = '' mkdir -p $out CLIENT_SECRET=$(openssl rand -hex 32) COOKIE_SECRET=$(openssl rand -hex 16) printf '%s' "$CLIENT_SECRET" > $out/client_secret authelia crypto hash generate argon2 --password "$CLIENT_SECRET" \ | sed 's/^Digest: //' > $out/client_secret_hash printf 'OAUTH2_PROXY_CLIENT_SECRET=%s\nOAUTH2_PROXY_COOKIE_SECRET=%s\n' \ "$CLIENT_SECRET" "$COOKIE_SECRET" > $out/envfile ''; }; in { exports = mkExports ( { endpoints.hosts = [ settings.domain ]; } // { auth.client = { clientId = "prometheus"; clientName = "Prometheus"; redirectUris = [ "https://${settings.domain}/oauth2/callback" ]; scopes = [ "openid" "profile" "email" "groups" ]; public = false; }; auth.varsGenerator = prometheusOidcGenerator; } ); nixosModule = import ./prometheus.nix { inherit settings roles meta prometheusOidcGenerator; }; }; }; } ================================================ FILE: clan-service-modules/monitoring/grafana.nix ================================================ { settings, roles, meta, oidcGenerator }: { config, lib, pkgs, ... }: let prometheusHosts = builtins.attrNames (roles.prometheus.machines or { }); lokiHosts = builtins.attrNames (roles.loki.machines or { }); oidc = settings.oidc; in { # SMTP password file clan.core.vars.generators."grafana".prompts.smtp-password.persist = true; # OIDC client secret — declared here so the producing host (where grafana # runs) has the files. The same generator definition is also exported via # auth.varsGenerator so the authelia host declares it too (share=true). # runtimeInputs is added here (not in the export) because pkgs is only # available inside nixosModule, not at inventory-eval time. clan.core.vars.generators."authelia-oidc-${oidc.clientId}" = lib.mkIf oidc.enable ( oidcGenerator // { runtimeInputs = with pkgs; [ coreutils openssl authelia gnused ]; } ); # Grafana secret key for signing clan.core.vars.generators."grafana-secret-key" = { files."secret-key".owner = "grafana"; runtimeInputs = [ pkgs.openssl ]; script = '' openssl rand -hex 32 > "$out/secret-key" ''; }; # Backup Graphana dir, contains stateful config pinpox.services.restic-client.backup-paths-offsite = [ "/var/lib/grafana" ]; # Reverse proxy services.caddy = { enable = true; virtualHosts."${settings.domain}".extraConfig = "reverse_proxy 127.0.0.1:9005"; }; # Graphana fronend services.grafana = { enable = true; settings = { server = { domain = settings.domain; root_url = "https://${settings.domain}/"; # Default is 3000 http_port = 9005; http_addr = "127.0.0.1"; }; security.secret_key = "$__file{${config.clan.core.vars.generators."grafana-secret-key".files."secret-key".path}}"; # Mail notifications smtp = { enabled = true; host = "smtp.sendgrid.net:587"; user = "apikey"; passwordFile = "${config.clan.core.vars.generators."grafana".files."smtp-password".path}"; fromAddress = "status@pablo.tools"; }; } // lib.optionalAttrs oidc.enable { # SCIM is enabled by default in recent Grafana versions and intercepts # OIDC user provisioning, causing "Failed to create user: user not # found". Disable it so the standard generic_oauth flow can create users. feature_toggles.enableSCIM = false; # OIDC-only login: hide the username/password form, do not auto-create # local users via signup, redirect straight to the OIDC provider. auth = { disable_login_form = true; signout_redirect_url = "${oidc.issuer}/logout"; # Self-healing OIDC user linking: if user_auth has no row for the # OIDC subject (e.g. fresh DB restore, or Authelia rebuilt and the # sub UUID changed), Grafana falls back to matching by email and # auto-creates the user_auth link. Safe here because Authelia is a # fully trusted IdP we control. oauth_allow_insecure_email_lookup = true; }; users = { # Must be true so the OIDC provisioning path can create new users. # disable_login_form = true (above) already prevents the local signup # form from being rendered, so this only enables OIDC-initiated signup. allow_sign_up = true; auto_assign_org = true; auto_assign_org_role = "Admin"; }; "auth.generic_oauth" = { enabled = true; name = oidc.providerName; icon = "signin"; auto_login = true; client_id = oidc.clientId; client_secret = "$__file{${config.clan.core.vars.generators."authelia-oidc-${oidc.clientId}".files.client_secret.path}}"; scopes = "openid profile email groups"; empty_scopes = false; auth_url = "${oidc.issuer}/api/oidc/authorization"; token_url = "${oidc.issuer}/api/oidc/token"; api_url = "${oidc.issuer}/api/oidc/userinfo"; login_attribute_path = "preferred_username"; email_attribute_path = "email"; name_attribute_path = "name"; groups_attribute_path = "groups"; # Authelia already restricts who can reach the client via its # authorization policy, so anyone who successfully logs in is granted # Admin in Grafana. role_attribute_path = "'Admin'"; allow_sign_up = true; use_pkce = true; }; }; # TODO provision the dashboards as currently configured provision.datasources.settings = { datasources = lib.optional (prometheusHosts != [ ]) { name = "Prometheus"; url = "http://${builtins.head prometheusHosts}.${meta.domain}:9090"; type = "prometheus"; isDefault = true; } ++ lib.optional (lokiHosts != [ ]) { name = "loki"; url = "http://${builtins.head lokiHosts}.${meta.domain}:3100"; type = "loki"; }; }; }; } ================================================ FILE: clan-service-modules/monitoring/loki.nix ================================================ { ... }: let port-loki = 3100; in { pinpox.services.restic-client.backup-paths-exclude = [ "/var/lib/loki" ]; networking.firewall = { enable = true; interfaces.wg-clan.allowedTCPPorts = [ port-loki ]; }; services.loki = { enable = true; configuration = { auth_enabled = false; server.http_listen_port = port-loki; ingester = { lifecycler = { address = "0.0.0.0"; ring = { kvstore.store = "inmemory"; replication_factor = 1; }; final_sleep = "0s"; }; # Any chunk not receiving new logs in this time will be flushed chunk_idle_period = "1h"; # All chunks will be flushed when they hit this age, default is 1h max_chunk_age = "1h"; # Loki will attempt to build chunks up to 1.5MB, flushing first if # chunk_idle_period or max_chunk_age is reached first chunk_target_size = 1048576; # Must be greater than index read cache TTL if using an index cache (Default # index read cache TTL is 5m) chunk_retain_period = "30s"; }; schema_config.configs = [ { from = "2020-10-24"; store = "boltdb-shipper"; object_store = "filesystem"; schema = "v13"; index = { prefix = "index_"; period = "24h"; }; } ]; storage_config = { boltdb_shipper = { active_index_directory = "/var/lib/loki/boltdb-shipper-active"; cache_location = "/var/lib/loki/boltdb-shipper-cache"; # Can be increased for faster performance over longer query periods, # uses more disk space cache_ttl = "24h"; }; filesystem.directory = "/var/lib/loki/chunks"; }; limits_config = { reject_old_samples = true; reject_old_samples_max_age = "168h"; allow_structured_metadata = false; }; table_manager = { retention_deletes_enabled = false; retention_period = "0s"; }; compactor.working_directory = "/var/lib/loki/boltdb-shipper-compactor"; }; }; } ================================================ FILE: clan-service-modules/monitoring/node-exporter.nix ================================================ { ... }: { services.prometheus.exporters.node = { enable = true; # Default port is 9100 # Listen on 0.0.0.0, but we only open the firewall for wg-clan openFirewall = false; enabledCollectors = [ "cgroups" "systemd" ]; extraFlags = [ "--collector.textfile.directory=/etc/nix" ]; }; networking.firewall.interfaces.wg-clan.allowedTCPPorts = [ 9100 ]; } ================================================ FILE: clan-service-modules/monitoring/prometheus.nix ================================================ { settings, roles, meta, prometheusOidcGenerator }: { lib, pkgs, config, flake-self, pinpox-utils, ... }: let nodeExporterHosts = builtins.attrNames (roles."node-exporter".machines or { }); in { clan.core.vars.generators."restic-exporter" = pinpox-utils.mkEnvGenerator [ "RESTIC_PASSWORD" "AWS_ACCESS_KEY_ID" "AWS_SECRET_ACCESS_KEY" "RESTIC_REPOSITORY" ]; # OIDC client secret generator — declared here so the producing host has the # files. The same definition is exported via auth.varsGenerator so the # authelia host also declares it (share=true). runtimeInputs added here # because pkgs isn't available at inventory-eval time. clan.core.vars.generators."authelia-oidc-prometheus" = prometheusOidcGenerator // { runtimeInputs = with pkgs; [ coreutils openssl authelia gnused ]; }; # oauth2-proxy sits between Caddy and Prometheus and handles the OIDC # client flow against Authelia. Prometheus itself stays on 127.0.0.1:9090 # (loopback only); the only way to reach it is through oauth2-proxy on # 127.0.0.1:4180, which Caddy reverse-proxies as ${settings.domain}. services.oauth2-proxy = { enable = true; provider = "oidc"; clientID = "prometheus"; keyFile = config.clan.core.vars.generators."authelia-oidc-prometheus".files.envfile.path; oidcIssuerUrl = "https://auth.pablo.tools"; redirectURL = "https://${settings.domain}/oauth2/callback"; upstream = [ "http://127.0.0.1:9090" ]; httpAddress = "http://127.0.0.1:4180"; cookie.secure = true; cookie.refresh = "1h"; email.domains = [ "*" ]; setXauthrequest = true; reverseProxy = true; extraConfig = { skip-provider-button = true; # Authelia's prometheus client is registered with require_pkce = true, # so oauth2-proxy must send a PKCE code_challenge on the authorize # request and the matching verifier on the token exchange. code-challenge-method = "S256"; # Authelia's authorization_policies already restrict who can reach this # client; oauth2-proxy doesn't need to enforce its own allowlist. }; }; # Reverse proxy for the prometheus web UI. The pki clan service auto-issues # a TLS cert for ${settings.domain} and prepends a `tls` directive to this # vhost; dm-dns distributes a CNAME so any clan-internal host can resolve # it. Caddy hands traffic to oauth2-proxy, which performs the OIDC dance # against Authelia and (if authorized) proxies upstream to Prometheus. services.caddy = { enable = true; virtualHosts."${settings.domain}".extraConfig = "reverse_proxy 127.0.0.1:4180"; }; services.prometheus = { enable = true; # Bind to localhost only — defense in depth on top of the host firewall. # The only way in from outside the host is via Caddy → oauth2-proxy → # 127.0.0.1:9090. listenAddress = "127.0.0.1"; # Disable config checks. They will fail because they run sandboxed and # can't access external files, e.g. the secrets stored in /run/keys # https://github.com/NixOS/nixpkgs/blob/d89d7af1ba23bd8a5341d00bdd862e8e9a808f56/nixos/modules/services/monitoring/prometheus/default.nix#L1732-L1738 checkConfig = false; webExternalUrl = settings.webExternalUrl; extraFlags = [ "--log.level=debug" "--storage.tsdb.retention.size='6GB'" ]; ruleFiles = [ (pkgs.writeText "prometheus-rules.yml" ( builtins.toJSON { groups = [ { name = "alerting-rules"; rules = import ./alert-rules.nix { inherit lib meta; }; } ]; } )) ]; alertmanagers = [ { static_configs = [ { targets = [ "localhost:9093" ]; } ]; } ]; scrapeConfigs = [ { job_name = "backup-reports"; scrape_interval = "60m"; metrics_path = "/probe"; static_configs = [ { targets = settings.jsonTargets; } ]; relabel_configs = [ { source_labels = [ "__address__" ]; target_label = "__param_target"; } { source_labels = [ "__param_target" ]; target_label = "instance"; } { target_label = "__address__"; replacement = "127.0.0.1:7979"; # The blackbox exporter's real hostname:port. } ]; } # TODO: move restic-client to a clan service so this can be discovered # via roles..machines instead of filtering flake-self. { job_name = "restic-exporter"; scrape_interval = "1h"; metrics_path = "/probe"; static_configs = [ { targets = ( builtins.attrNames ( lib.filterAttrs ( n: v: v.config.pinpox.services.restic-client.enable ) flake-self.nixosConfigurations ) ); } ]; relabel_configs = [ { source_labels = [ "__address__" ]; target_label = "__param_target"; } { source_labels = [ "__param_target" ]; target_label = "instance"; } { target_label = "__address__"; replacement = "127.0.0.1:${builtins.toString config.services.restic-exporter.port}"; } ]; } { job_name = "blackbox"; scrape_interval = "2m"; metrics_path = "/probe"; params = { module = [ "http_2xx" ]; }; static_configs = [ { targets = settings.blackboxTargets; } ]; relabel_configs = [ { source_labels = [ "__address__" ]; target_label = "__param_target"; } { source_labels = [ "__param_target" ]; target_label = "instance"; } { target_label = "__address__"; replacement = "127.0.0.1:9115"; # The blackbox exporter's real hostname:port. } ]; } { job_name = "node-stats"; static_configs = [ { # Hosts assigned the "node-exporter" role of this monitoring instance targets = map (h: "${h}.${meta.domain}:9100") nodeExporterHosts; } ]; } ]; alertmanager = { enable = true; # port = 9093; # Default listenAddress = "127.0.0.1"; webExternalUrl = settings.alertmanagerWebExternalUrl; environmentFile = /var/src/secrets/alertmanager/envfile; configuration = { route = { receiver = "all"; group_by = [ "instance" ]; group_wait = "30s"; group_interval = "2m"; repeat_interval = "24h"; }; receivers = [ { name = "all"; webhook_configs = [ { url = "http://127.0.0.1:11000/alert"; } # matrix-hook { url = with config.services.alertmanager-ntfy; "http://${httpAddress}:${httpPort}"; } # alertmanger-ntfy ]; } ]; }; }; }; } ================================================ FILE: clan-service-modules/navidrome.nix ================================================ { ... }: { _class = "clan.service"; manifest.name = "navidrome"; manifest.description = "Navidrome music streaming server"; manifest.readme = "Self-hosted music streaming server with subsonic API"; manifest.categories = [ "Media" ]; manifest.exports.out = [ "endpoints" ]; roles.default = { description = "Sets up navidrome music server with caddy reverse proxy"; interface = { lib, meta, ... }: { options = { host = lib.mkOption { type = lib.types.str; default = "music.${meta.domain}"; description = "Host serving the navidrome instance"; example = "party.0cx.de"; }; }; }; perInstance = { settings, mkExports, ... }: { exports = mkExports { endpoints.hosts = [ settings.host ]; }; nixosModule = { config, ... }: { config = { # Reverse proxy services.caddy = { enable = true; virtualHosts."${settings.host}".extraConfig = "reverse_proxy 127.0.0.1:${toString config.services.navidrome.settings.Port}"; }; # Mount storagebox pinpox.defaults.storagebox = { enable = true; mountOnAccess = false; }; # Add navidrome user to storage-users group for access to storagebox users.users.navidrome.extraGroups = [ "storage-users" ]; # Set up navidrome services.navidrome = { enable = true; settings.Port = 4533; settings.Address = "127.0.0.1"; settings.MusicFolder = "${config.pinpox.defaults.storagebox.mountPoint}/music"; }; # Ensure storagebox is mounted before navidrome starts systemd.services.navidrome = { requires = [ "mnt-storagebox.mount" ]; after = [ "mnt-storagebox.mount" ]; }; }; }; }; }; } ================================================ FILE: clan-service-modules/thelounge.nix ================================================ { ... }: { _class = "clan.service"; manifest.name = "thelounge"; manifest.description = "The Lounge IRC client and bouncer"; manifest.readme = "Self-hosted IRC client and bouncer with web interface"; manifest.categories = [ "Communication" ]; manifest.exports.out = [ "endpoints" ]; roles.default = { description = "Sets up The Lounge IRC client with caddy reverse proxy"; interface = { lib, meta, ... }: { options = { host = lib.mkOption { type = lib.types.str; default = "irc.${meta.domain}"; description = "Host serving the The Lounge instance."; }; }; }; perInstance = { settings, mkExports, ... }: { exports = mkExports { endpoints.hosts = [ settings.host ]; }; nixosModule = { config, ... }: { config = { # Reverse proxy services.caddy = { enable = true; virtualHosts."${settings.host}".extraConfig = "reverse_proxy 127.0.0.1:${toString config.services.thelounge.port}"; }; # Set up The Lounge services.thelounge = { enable = true; port = 9090; public = false; extraConfig = { host = "127.0.0.1"; reverseProxy = true; storagePolicy = { enabled = true; maxAgeDays = 365; deletionPolicy = "everything"; }; theme = "morning"; }; }; # Backup paths pinpox.services.restic-client.backup-paths-offsite = [ "/var/lib/thelounge/certificates" "/var/lib/thelounge/config.js" # Don't backup logs for now - too big. # "/var/lib/thelounge/logs" # "/var/lib/thelounge/packages" "/var/lib/thelounge/sts-policies.json" "/var/lib/thelounge/users" "/var/lib/thelounge/vapid.json" ]; }; }; }; }; } ================================================ FILE: flake.nix ================================================ { description = "My machines"; inputs = { wrappers.url = "github:lassulus/wrappers"; wrappers.inputs.nixpkgs.follows = "nixpkgs"; nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable?shallow=1"; clan-core.url = "https://git.clan.lol/clan/clan-core/archive/main.tar.gz"; clan-core.inputs.nixpkgs.follows = "nixpkgs"; clan-core.inputs.disko.follows = "disko"; clan-core.inputs.data-mesher.follows = "data-mesher"; data-mesher.url = "git+https://git.clan.lol/clan/data-mesher"; data-mesher.inputs.nixpkgs.follows = "nixpkgs"; clan-community.url = "https://git.clan.lol/clan/clan-community/archive/main.tar.gz"; clan-community.inputs.clan-core.follows = "clan-core"; clan-community.inputs.nixpkgs.follows = "nixpkgs"; rio.url = "github:pinpox/rio"; rio.inputs.nixpkgs.follows = "nixpkgs"; rio.inputs.systems.follows = "clan-core/systems"; khard.url = "github:lucc/khard"; khard.inputs.nixpkgs.follows = "nixpkgs"; gif-searcher.url = "github:pinpox/gif-searcher"; gif-searcher.inputs.nixpkgs.follows = "nixpkgs"; nix-index-database.url = "github:nix-community/nix-index-database"; nix-index-database.inputs.nixpkgs.follows = "nixpkgs"; disko.url = "github:nix-community/disko/latest"; disko.inputs.nixpkgs.follows = "nixpkgs"; jitsi-matrix-presence.url = "github:pinpox/jitsi-matrix-presence"; jitsi-matrix-presence.inputs.nixpkgs.follows = "nixpkgs"; nixos-hardware.url = "github:NixOS/nixos-hardware/master"; aoe-taunt-discord-bot.url = "github:pinpox/aoe-taunt-discord-bot"; aoe-taunt-discord-bot.inputs.nixpkgs.follows = "nixpkgs"; pinpox-keys.url = "https://github.com/pinpox.keys"; pinpox-keys.flake = false; pinpox-neovim.url = "github:pinpox/pinpox-neovim"; pinpox-neovim.inputs.nixpkgs.follows = "nixpkgs"; radio.url = "github:pinpox/radio"; radio.inputs.nixpkgs.follows = "nixpkgs"; mc3000.url = "github:pinpox/mc3000"; mc3000.inputs.nixpkgs.follows = "nixpkgs"; naersk.url = "github:nix-community/naersk/master"; naersk.inputs.nixpkgs.follows = "nixpkgs"; promterm.url = "github:pinpox/promterm"; promterm.inputs = { nixpkgs.follows = "nixpkgs"; naersk.follows = "naersk"; }; go-karma-bot.url = "github:pinpox/go-karma-bot"; go-karma-bot.inputs.nixpkgs.follows = "nixpkgs"; retiolum.url = "git+https://git.thalheim.io/Mic92/retiolum"; retiolum.inputs.nixpkgs.follows = "nixpkgs"; flake-compat.url = "github:edolstra/flake-compat"; flake-compat.flake = false; home-manager.url = "github:nix-community/home-manager"; home-manager.inputs.nixpkgs.follows = "nixpkgs"; distro.url = "path:/home/pinpox/code/github.com/generational-infrastructure/distro"; distro.inputs.nixpkgs.follows = "nixpkgs"; distro.inputs.opencrow.follows = "opencrow"; distro.inputs.treefmt-nix.follows = "treefmt-nix"; nur.url = "github:pinpox/NUR"; nur.inputs.nixpkgs.follows = "nixpkgs"; nur.inputs.flake-parts.follows = "clan-core/flake-parts"; wl-harmonograph.url = "github:pinpox/wl-harmonograph"; wl-harmonograph.inputs.nixpkgs.follows = "nixpkgs"; restic-exporter.url = "github:pinpox/restic-exporter"; restic-exporter.inputs.nixpkgs.follows = "nixpkgs"; restic-exporter.inputs.flake-utils.follows = "age-plugin-picohsm/flake-utils"; alertmanager-ntfy = { url = "github:pinpox/alertmanager-ntfy"; inputs = { nixpkgs.follows = "nixpkgs"; flake-compat.follows = "flake-compat"; flake-utils.follows = "age-plugin-picohsm/flake-utils"; }; }; matrix-hook.url = "github:pinpox/matrix-hook"; matrix-hook.inputs = { nixpkgs.follows = "nixpkgs"; flake-compat.follows = "flake-compat"; flake-utils.follows = "age-plugin-picohsm/flake-utils"; }; # ZSH plugins zsh-abbrev-alias.url = "github:momo-lab/zsh-abbrev-alias"; zsh-abbrev-alias.flake = false; zsh-colored-man-pages.url = "github:ael-code/zsh-colored-man-pages"; zsh-colored-man-pages.flake = false; jj-zsh-prompt.url = "github:pinpox/jj-zsh-prompt"; jj-zsh-prompt.inputs.nixpkgs.follows = "nixpkgs"; zsh-async.url = "github:mafredri/zsh-async"; zsh-async.flake = false; nix-apple-fonts = { url = "github:pinpox/nix-apple-fonts"; inputs.flake-compat.follows = "flake-compat"; inputs.flake-utils.follows = "age-plugin-picohsm/flake-utils"; inputs.nixpkgs.follows = "nixpkgs"; }; treefmt-nix.follows = "clan-core/treefmt-nix"; age-plugin-picohsm.url = "github:pinpox/age-plugin-picohsm"; age-plugin-picohsm.inputs.nixpkgs.follows = "nixpkgs"; passage-secret-service.url = "github:pinpox/passage-secret-service"; passage-secret-service.inputs.nixpkgs.follows = "nixpkgs"; llm-agents.url = "github:numtide/llm-agents.nix"; llm-agents.inputs.nixpkgs.follows = "nixpkgs"; llm-agents.inputs.treefmt-nix.follows = "treefmt-nix"; opencrow.url = "github:pinpox/opencrow"; opencrow.inputs.nixpkgs.follows = "nixpkgs"; opencrow.inputs.treefmt-nix.follows = "treefmt-nix"; mics-skills.url = "github:Mic92/mics-skills"; mics-skills.inputs.nixpkgs.follows = "nixpkgs"; mics-skills.inputs.treefmt-nix.follows = "treefmt-nix"; twitch-first.url = "github:pinpox/twitch-first"; twitch-first.inputs.nixpkgs.follows = "nixpkgs"; trippy-track.url = "github:pinpox/trippy-track"; trippy-track.inputs.nixpkgs.follows = "nixpkgs"; }; outputs = { self, ... }@inputs: with inputs; let # System types to support. supportedSystems = [ "x86_64-linux" "x86_64-darwin" "aarch64-linux" "aarch64-darwin" ]; # Helper function to generate an attrset '{ x86_64-linux = f "x86_64-linux"; ... }'. forAllSystems = nixpkgs.lib.genAttrs supportedSystems; # Nixpkgs instantiated for supported system types. nixpkgsFor = forAllSystems ( system: import nixpkgs { inherit system; overlays = [ self.overlays.default ]; } ); # treefmt configuration treefmtEval = import ./formatter.nix { inherit treefmt-nix nixpkgsFor forAllSystems; }; clan = clan-core.lib.clan { # this needs to point at the repository root inherit self; # Vars backend configuration (moved from machine-level) vars.settings.secretStore = "password-store"; # Make inputs and the flake itself accessible as module parameters. # Technically, adding the inputs is redundant as they can be also # accessed with flake-self.inputs.X, but adding them individually # allows to only pass what is needed to each module. specialArgs = { flake-self = self; } // inputs; # Register custom clan service modules # desktop, localsend, wireguard-star moved to clan-community modules."@pinpox/navidrome" = ./clan-service-modules/navidrome.nix; modules."@pinpox/thelounge" = ./clan-service-modules/thelounge.nix; modules."@pinpox/machine-type" = ./clan-service-modules/machine-type; modules."@pinpox/monitoring" = ./clan-service-modules/monitoring; inventory = import ./inventory.nix { inherit self; }; }; in { devShells = forAllSystems ( system: with nixpkgsFor.${system}; { default = pkgs.mkShell { packages = [ clan-core.packages.${system}.clan-cli treefmtEval.${system}.config.build.wrapper ]; }; } ); # Custom packages added via the overlay are selectively exposed here, to # allow using them from other flakes that import this one. packages = forAllSystems ( system: with nixpkgsFor.${system}; { inherit hello-custom fritzbox_exporter mqtt2prometheus smartmon-script machine-report woodpecker-pipeline manual ; } ) // { # Flashable SD card image for uconsole (uses binfmt emulation) # Build with: nix build .#uconsole-image # Then flash with: dd if=result/main.raw of=/dev/sdX bs=4M status=progress # Build the image on the remote server # ssh root@build01.clan.lol \ # 'nix build github:pinpox/nixos#packages.aarch64-linux.uconsole-image -L' # Download image (compressed) # ssh root@build01.clan.lol \ # 'zstd -c \ # $(nix build github:pinpox/nixos#packages.aarch64-linux.uconsole-image --print-out-paths)/main.raw' > \ # uconsole.img.zst # Decompress # zstd -d uconsole.img.zst aarch64-linux = (forAllSystems (s: { })).aarch64-linux // { uconsole-image = self.nixosConfigurations.uconsole.config.system.build.diskoImages; }; }; # Expose overlay to flake outputs, to allow using it from other flakes. # Flake inputs are passed to the overlay so that the packages defined in # it can use the sources pinned in flake.lock overlays.default = final: prev: (import ./overlays inputs self pinpox-utils) final prev; # Use treefmt for 'nix fmt' formatter = forAllSystems (system: treefmtEval.${system}.config.build.wrapper); # Expose treefmt check for CI checks = forAllSystems (system: { formatting = treefmtEval.${system}.config.build.check self; }); # Machine toplevels for CI builds and cache pushing ciBuilds = forAllSystems ( system: nixpkgs.lib.mapAttrs' ( name: config: nixpkgs.lib.nameValuePair name config.config.system.build.toplevel ) (nixpkgs.lib.filterAttrs (_: config: config.pkgs.stdenv.hostPlatform.system == system) self.nixosConfigurations) ); # Each subdirectory in ./templates/ is a # template, which can be used for new proects with: # `nix flake init` templates = builtins.listToAttrs ( map (name: { inherit name; value = { path = ./templates + "/${name}"; description = (import (./templates + "/${name}/flake.nix")).description; }; }) (builtins.attrNames (builtins.readDir ./templates)) ); # Output all modules in ./modules/ to flake. Modules should be in # individual subdirectories and contain a default.nix file. # Each subdirectory in ./modules/ is a nixos module nixosModules = builtins.listToAttrs ( map (name: { inherit name; value = import (./modules + "/${name}"); }) (builtins.attrNames (builtins.readDir ./modules)) ); # Each subdirectory in ./machines/ is a host config. Clan # auto-imports all machines from ./machines inherit (clan.config) clanInternals; nixosConfigurations = clan.config.nixosConfigurations; clan = clan.config; # Each subdirectory in ./home-manager/profiles/ is a # home-manager profile homeConfigurations = builtins.listToAttrs ( map (name: { inherit name; value = { ... }: { imports = [ (./home-manager/profiles + "/${name}") ] ++ (builtins.attrValues self.homeManagerModules); }; }) ( builtins.attrNames ( nixpkgs.lib.filterAttrs (n: v: v == "directory") (builtins.readDir ./home-manager/profiles) ) ) ); # Each subdirectory in ./home-manager/modules/ is a # home-manager module homeManagerModules = builtins.listToAttrs ( map (name: { inherit name; value = import (./home-manager/modules + "/${name}"); }) (builtins.attrNames (builtins.readDir ./home-manager/modules)) ); }; } ================================================ FILE: formatter.nix ================================================ { treefmt-nix, nixpkgsFor, forAllSystems, }: forAllSystems ( system: treefmt-nix.lib.evalModule nixpkgsFor.${system} { projectRootFile = "flake.nix"; programs = { nixfmt.enable = true; flake-edit.enable = true; nixfmt.package = nixpkgsFor.${system}.nixfmt; prettier.enable = true; shellcheck.enable = true; shfmt.enable = true; }; settings.formatter = { prettier.includes = [ "*.md" "*.yaml" "*.yml" "*.json" "*.toml" ]; shellcheck.includes = [ "*.sh" ]; shfmt.includes = [ "*.sh" ]; }; } ) ================================================ FILE: home-manager/colorscheme.nix ================================================ { lib, ... }: with lib; let colornames = [ "Black" "BrightBlack" "White" "BrightWhite" "Yellow" "BrightYellow" "Green" "BrightGreen" "Cyan" "BrightCyan" "Blue" "BrightBlue" "Magenta" "BrightMagenta" "Red" "BrightRed" ]; in { options.pinpox.colors = builtins.listToAttrs ( map (c: { name = c; value = mkOption { type = types.str; }; }) colornames ); config.pinpox.colors = { Black = "000000"; # 000000 BrightBlack = "262626"; # 262626 White = "d0d0d0"; # d0d0d0 BrightWhite = "ffffff"; # ffffff Red = "d7005f"; # d7005f BrightRed = "ff5f87"; # ff5f87 Green = "00af5f"; # 00af5f BrightGreen = "00d75f"; # 00d75f Yellow = "d78700"; # d78700 BrightYellow = "ffaf00"; # ffaf00 Blue = "0087d7"; # 0087d7 BrightBlue = "00afff"; # 00afff Magenta = "d787d7"; # d787d7 BrightMagenta = "ff87ff"; # ff87ff Cyan = "00afaf"; # 00afaf BrightCyan = "00d7d7"; # 00d7d7 }; } ================================================ FILE: home-manager/modules/audio-recording/default.nix ================================================ { config, lib, pkgs, ... }: with lib; let cfg = config.pinpox.defaults.audio-recording; plugins = with pkgs; [ # Instruments x42-avldrums zynaddsubfx # Effects calf lsp-plugins zam-plugins talentedhack gxplugins-lv2 distrho ]; in { # These settings yield 10ms@1024 spls latency in reaper for me: # pw-metadata -n settings 0 clock.force-quantum 1024 # pw-metadata -n settings 0 clock.force-rate 96000 options.pinpox.defaults.audio-recording.enable = mkEnableOption "audio production setup (DAW and plugins)"; config = mkIf cfg.enable { home.packages = with pkgs; [ reaper alsa-scarlett-gui ] # Some plugins also have a binary, so we also add them to PATH ++ plugins; # Place vst, vst3, clap, lv2 and ladspa plugins in the according # directories where reaper will look for them home.file = let all-audio-plugins = pkgs.symlinkJoin { name = "all-audio-plugins"; paths = plugins; }; in { all-lv2 = { recursive = true; source = "${all-audio-plugins}/lib/lv2"; target = ".lv2"; }; all-clap = { recursive = true; source = "${all-audio-plugins}/lib/clap"; target = ".clap"; }; all-vst = { recursive = true; source = "${all-audio-plugins}/lib/vst"; target = ".vst"; }; all-vst3 = { recursive = true; source = "${all-audio-plugins}/lib/vst3"; target = ".vst3"; }; all-ladspa = { recursive = true; source = "${all-audio-plugins}/lib/ladspa"; target = ".ladspa"; }; }; }; } ================================================ FILE: home-manager/modules/calendar/default.nix ================================================ { config, lib, pkgs, flake-inputs, ... }: with lib; let cfg = config.pinpox.defaults.calendar; in { options.pinpox.defaults.calendar.enable = mkEnableOption "Calendar config"; config = mkIf cfg.enable { programs.vdirsyncer.enable = true; services.vdirsyncer.enable = true; programs.khal.enable = true; programs.khal.settings = { default.default_calendar = "Kalender"; # view = { # agenda_event_format = "{calendar-color}{cancelled}{start-end-time-style} {title}{repeat-symbol}{reset}"; # }; }; programs.khard.enable = true; programs.khard.package = flake-inputs.khard.packages."x86_64-linux".default; # khard.settings is broken, it does not support subsections, so we write # the file directly xdg.configFile."khard/khard.conf".source = pkgs.writeTextFile { name = "khard.conf"; text = '' [addressbooks] [[mailbox]] path = ~/.local/share/office/contacts/mailbox/* type = discover [[gmail]] path = ~/.local/share/office/contacts/gmail/* type = discover [[nextcloud]] path = ~/.local/share/office/contacts/nextcloud/* type = discover [general] debug = no default_action = list # These are either strings or comma seperated lists editor = nvim, -i, NONE merge_editor = nvim, -d ''; }; accounts.contact.accounts = { "nextcloud" = { vdirsyncer = { enable = true; collections = [ "from a" "from b" ]; metadata = [ "color" "displayname" ]; userNameCommand = [ "${pkgs.passage}/bin/passage" "vdirsyncer-nextcloud-user" ]; }; remote = { type = "carddav"; url = "https://files.pablo.tools"; passwordCommand = [ "${pkgs.passage}/bin/passage" "vdirsyncer-nextcloud" ]; }; local = { type = "filesystem"; path = "/home/pinpox/.local/share/office/contacts/nextcloud"; }; }; "mailbox" = { vdirsyncer = { enable = true; collections = [ "from a" "from b" ]; metadata = [ "color" "displayname" ]; userNameCommand = [ "${pkgs.passage}/bin/passage" "vdirsyncer-mailbox-user" ]; }; remote = { type = "carddav"; url = "https://dav.mailbox.org/carddav/"; passwordCommand = [ "${pkgs.passage}/bin/passage" "vdirsyncer-mailbox" ]; }; local = { type = "filesystem"; path = "/home/pinpox/.local/share/office/contacts/mailbox"; }; }; "gmail" = { vdirsyncer = { enable = true; collections = [ "from a" "from b" ]; metadata = [ "color" "displayname" ]; clientIdCommand = [ "${pkgs.passage}/bin/passage" "vdirsyncer-gmail-clientid" ]; clientSecretCommand = [ "${pkgs.passage}/bin/passage" "vdirsyncer-gmail-clientsecret" ]; tokenFile = "/home/pinpox/.local/share/vdirsyncer-contacts-gmail-token"; }; remote = { type = "google_contacts"; }; local = { type = "filesystem"; path = "/home/pinpox/.local/share/office/contacts/gmail"; }; }; }; accounts.calendar.accounts = { "nextcloud" = { khal = { enable = true; type = "discover"; priority = 1; }; vdirsyncer = { enable = true; collections = [ "from a" "from b" ]; metadata = [ "color" "displayname" ]; userNameCommand = [ "${pkgs.passage}/bin/passage" "vdirsyncer-nextcloud-user" ]; }; remote = { type = "caldav"; url = "https://files.pablo.tools/"; passwordCommand = [ "${pkgs.passage}/bin/passage" "vdirsyncer-nextcloud" ]; }; local = { type = "filesystem"; path = "/home/pinpox/.local/share/office/calendars/nextcloud"; }; }; "mailbox" = { khal = { enable = true; type = "discover"; priority = 1; }; vdirsyncer = { enable = true; collections = [ "from a" "from b" ]; metadata = [ "color" "displayname" ]; userNameCommand = [ "${pkgs.passage}/bin/passage" "vdirsyncer-mailbox-user" ]; }; remote = { type = "caldav"; url = "https://dav.mailbox.org/caldav/"; passwordCommand = [ "${pkgs.passage}/bin/passage" "vdirsyncer-mailbox" ]; }; local = { type = "filesystem"; path = "/home/pinpox/.local/share/office/calendars/mailbox"; }; }; "icloud" = { khal = { enable = true; type = "discover"; priority = 2; }; vdirsyncer = { enable = true; collections = [ "from a" "from b" ]; metadata = [ "color" "displayname" ]; userNameCommand = [ "${pkgs.passage}/bin/passage" "vdirsyncer-icloud-user" ]; }; remote = { type = "caldav"; url = "https://caldav.icloud.com/"; passwordCommand = [ "${pkgs.passage}/bin/passage" "vdirsyncer-icloud" ]; }; local = { type = "filesystem"; path = "/home/pinpox/.local/share/office/calendars/icloud"; }; }; "gmail" = { khal = { enable = true; type = "discover"; priority = 3; }; vdirsyncer = { enable = true; collections = [ "from a" "from b" ]; metadata = [ "color" "displayname" ]; clientIdCommand = [ "${pkgs.passage}/bin/passage" "vdirsyncer-gmail-clientid" ]; clientSecretCommand = [ "${pkgs.passage}/bin/passage" "vdirsyncer-gmail-clientsecret" ]; tokenFile = "/home/pinpox/.local/share/vdirsyncer-calendar-gmail-token"; }; remote = { type = "google_calendar"; }; local = { type = "filesystem"; path = "/home/pinpox/.local/share/office/calendars/gmail"; }; }; }; }; } ================================================ FILE: home-manager/modules/chromium/default.nix ================================================ { config, lib, ... }: with lib; let cfg = config.pinpox.programs.chromium; in { options.pinpox.programs.chromium.enable = mkEnableOption "chromium browser"; config = mkIf cfg.enable { programs.chromium = { enable = true; extensions = [ { id = "nngceckbapebfimnlniiiahkandclblb"; } # Bitwarden { id = "cjpalhdlnbpafiamejdnhcphjbkeiagm"; } # Ublock Origin { id = "gcbommkclmclpchllfjekcdonpmejbdp"; } # HTTPS everywhere { id = "mmpokgfcmbkfdeibafoafkiijdbfblfg"; } # Merge windows { id = "agldajbhchobfgjcmmigehfdcjbmipne"; } # Blank Dark New Tab ]; }; }; } ================================================ FILE: home-manager/modules/claude-code/default.nix ================================================ { pkgs, config, lib, flake-self, ... }: with lib; let cfg = config.pinpox.programs.claude-code; mics-skills = flake-self.inputs.mics-skills; mics-packages = mics-skills.packages.${pkgs.stdenv.hostPlatform.system}; firefoxPrefs = { "xpinstall.signatures.required" = false; "extensions.autoDisableScopes" = 0; "extensions.enabledScopes" = 15; "browser.shell.checkDefaultBrowser" = false; "datareporting.policy.dataSubmissionEnabled" = false; "toolkit.telemetry.reportingpolicy.firstRun" = false; "browser.uiCustomization.state" = builtins.toJSON { placements = { widget-overflow-fixed-list = [ ]; unified-extensions-area = [ ]; nav-bar = [ "back-button" "forward-button" "stop-reload-button" "urlbar-container" "downloads-button" "browser-cli-controller_thalheim_io-browser-action" "unified-extensions-button" ]; toolbar-menubar = [ "menubar-items" ]; TabsToolbar = [ "tabbrowser-tabs" "new-tab-button" "alltabs-button" ]; PersonalToolbar = [ "personal-bookmarks" ]; }; seen = [ "browser-cli-controller_thalheim_io-browser-action" ]; dirtyAreaCache = [ "nav-bar" ]; currentVersion = 20; newElementCount = 0; }; }; userJs = pkgs.writeText "browser-cli-user.js" (concatStringsSep "\n" ( mapAttrsToList (k: v: "user_pref(${builtins.toJSON k}, ${builtins.toJSON v});") firefoxPrefs )); browser-cli-firefox = pkgs.writeShellScriptBin "browser-cli-firefox" '' set -euo pipefail PROFILE="$(mktemp -d)" trap 'rm -rf "$PROFILE"' EXIT cp ${userJs} "$PROFILE/user.js" mkdir -p "$PROFILE/extensions" cp ${mics-packages.browser-cli-extension}/browser-cli-extension.xpi \ "$PROFILE/extensions/browser-cli-controller@thalheim.io.xpi" exec ${pkgs.firefox-devedition}/bin/firefox-devedition \ --no-remote -profile "$PROFILE" "$@" ''; in { imports = [ mics-skills.homeModules.default ]; options.pinpox.programs.claude-code = { enable = mkEnableOption "claude-code"; }; config = mkIf cfg.enable { home.packages = [ pkgs.claude-code browser-cli-firefox ]; programs.mics-skills = { enable = true; package = mics-packages; skills = [ "browser-cli" ]; }; # Declarative settings via settings.local.json (merged with mutable settings.json) home.file.".claude/settings.local.json".text = builtins.toJSON { permissions.allow = [ "Bash(browser-cli:*)" "Bash(browser-cli-firefox:*)" "Bash(browser-cli-server:*)" "Read(/tmp/**)" ]; }; # Supplement the browser-cli skill with browser launch instructions home.file.".claude/skills/browser-cli-setup/SKILL.md".text = '' --- name: browser-cli-setup description: How to start Firefox with the browser-cli extension pre-installed. Use this before using browser-cli commands. --- # Starting the browser Before using `browser-cli`, you must launch Firefox Developer Edition with the browser-cli extension already installed. Run: ```bash browser-cli-firefox & ``` This starts Firefox Developer Edition with a temporary profile that has: - The browser-cli WebExtension pre-installed - Extension signature checks disabled - Telemetry disabled After the browser is running, use `browser-cli` commands as documented in the browser-cli skill. ''; }; } ================================================ FILE: home-manager/modules/credentials/age-recipients ================================================ age15m0pgtr06pqaql2wx8e2xqupcctzkf0dk0cc06hv3cjcgpe5u3ns59jaun age1picohsm1qjpqjd9pnlh8zem6wwz62ml9z995fsdz9e23dumamjj0nl4cq0m5dx5v5mm5njelm3hmv4w3mfs5mzvks3xtu6k723jr0am49hrk9mduxvxpps ================================================ FILE: home-manager/modules/credentials/default.nix ================================================ { config, pkgs, lib, ... }: with lib; let cfg = config.pinpox.defaults.credentials; in { options.pinpox.defaults.credentials.enable = mkEnableOption "credentials defaults"; config = mkIf cfg.enable { # Set up passage store directory with remote configured (but not cloned) # On a fresh machine, just run: passage git pull home.activation.setupPassageStore = lib.hm.dag.entryAfter [ "writeBoundary" ] '' STORE_DIR="${config.home.homeDirectory}/.passage/store" if [ ! -d "$STORE_DIR/.git" ]; then run mkdir -p "$STORE_DIR" run ${pkgs.git}/bin/git -C "$STORE_DIR" init -b master run ${pkgs.git}/bin/git -C "$STORE_DIR" remote add origin "gitea@git.0cx.de:pinpox/passage.git" run ${pkgs.git}/bin/git -C "$STORE_DIR" config branch.master.remote origin run ${pkgs.git}/bin/git -C "$STORE_DIR" config branch.master.merge refs/heads/master fi ''; # List of public keys (recipients) to encrypt to. # Use `age-plugin-picohsm --list` to get the HSM key if needed. The second # key is an offline last-resort backup key home.sessionVariables.PASSAGE_RECIPIENTS_FILE = "${./age-recipients}"; programs.zsh.sessionVariables.PASSAGE_RECIPIENTS_FILE = "${./age-recipients}"; # The file ~/.config/age/identities still needs to be generated. # Run `age-plugin-picohsm -list` and put the age-key identity # (AGE-PLUGIN-PICOHSM-XXXXX) into the file. For sops, the recipients should # be added as a comment in the line before. Full format example: # # # recipient: age1picohsm1qjpqxxxxxxxxxxxxxxxxxxxxxxxxx # AGE-PLUGIN-PICOHSM-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX home.sessionVariables.PASSAGE_IDENTITIES_FILE = "$HOME/.config/age/identities"; programs.zsh.sessionVariables.PASSAGE_IDENTITIES_FILE = "$HOME/.config/age/identities"; # SOPS uses the same identity file as passage home.sessionVariables.SOPS_AGE_KEY_FILE = "$HOME/.config/age/identities"; programs.zsh.sessionVariables.SOPS_AGE_KEY_FILE = "$HOME/.config/age/identities"; # The nixos agent is better services.ssh-agent.enable = false; }; } ================================================ FILE: home-manager/modules/easyeffects/default.nix ================================================ { config, lib, pkgs, ... }: with lib; let cfg = config.pinpox.programs.easyeffects; in { options.pinpox.programs.easyeffects = { enable = mkEnableOption "EasyEffects audio effects"; }; config = mkIf cfg.enable { services.easyeffects = { enable = true; # preset = "my-preset"; # extraPresets = { # my-preset = { # input = { # blocklist = [ # # ]; # "plugins_order" = [ # "rnnoise#0" # ]; # "rnnoise#0" = { # bypass = false; # "enable-vad" = false; # "input-gain" = 0.0; # "model-path" = ""; # "output-gain" = 0.0; # release = 20.0; # "vad-thres" = 50.0; # wet = 0.0; # }; # }; # }; # }; }; # Add easyeffects to home packages home.packages = with pkgs; [ easyeffects ]; }; } ================================================ FILE: home-manager/modules/email/aerc-style.nix ================================================ { config, ... }: with config.pinpox.colors; { "*.default" = true; "*error.bold" = true; "statusline*.default" = true; "border.bg" = "#${Black}"; "border.fg" = "#${Blue}"; "completion_pill.reverse" = true; "default.bg" = "#${Black}"; "dirlist_default.selected.bg" = "#${BrightBlack}"; "dirlist_default.selected.fg" = "#${White}"; "dirlist_recent.selected.bg" = "#${White}"; "dirlist_recent.selected.fg" = "#${BrightBlack}"; "dirlist_unread.fg" = "#${BrightGreen}"; "dirlist_unread.selected.bg" = "#${BrightBlack}"; "dirlist_unread.selected.fg" = "#${BrightGreen}"; "error.fg" = "#${BrightRed}"; "header.bold" = true; "header.fg" = "#${BrightBlue}"; "msglist_default.selected.bg" = "#${White}"; "msglist_default.selected.fg" = "#${BrightBlack}"; "msglist_deleted.fg" = "#${BrightRed}"; "msglist_deleted.selected.reverse" = "toggle"; "msglist_marked.fg" = "#${BrightYellow}"; "msglist_marked.selected.bg" = "#${BrightBlack}"; "msglist_marked.selected.fg" = "#${BrightYellow}"; "msglist_read.selected.bg" = "#${BrightBlack}"; "msglist_read.selected.fg" = "#${White}"; "msglist_result.fg" = "#${BrightYellow}"; "msglist_result.selected.bg" = "#${BrightBlack}"; "msglist_unread.bold" = true; "msglist_unread.fg" = "#${BrightGreen}"; "msglist_unread.selected.bg" = "#${BrightBlack}"; "selector_chooser.bold" = true; "selector_focused.reverse" = true; "statusline_default.bg" = "#${Magenta}"; "statusline_default.fg" = "#${BrightWhite}"; "statusline_error.fg" = "#${BrightRed}"; "statusline_error.reverse" = true; "success.fg" = "#${BrightGreen}"; "tab.bg" = "#${Blue}"; "tab.fg" = "#${White}"; "tab.selected.bg" = "#${BrightWhite}"; "tab.selected.fg" = "#${Blue}"; "title.reverse" = true; "warning.fg" = "#${BrightYellow}"; } ================================================ FILE: home-manager/modules/email/default.nix ================================================ { config, pkgs, lib, ... }: with lib; let cfg = config.pinpox.defaults.email; in { options.pinpox.defaults.email.enable = mkEnableOption "email client config"; config = mkIf cfg.enable { accounts.email.maildirBasePath = "Mail"; programs.aerc = { extraConfig.general.unsafe-accounts-conf = true; enable = true; # stylesets.pinpox = (import ./aerc-style.nix { inherit config;}); extraConfig = { ui = { # styleset-name = "pinpox"; icon-attachment = "a "; icon-old = ""; icon-replied = "↩ "; }; compose.address-book-cmd = "carddav-query %s"; filters = { "text/plain" = "colorize"; "text/calendar" = "calendar"; "message/delivery-status" = "colorize"; "message/rfc822" = "colorize"; #text/html=pandoc -f html -t plain | colorize "text/html" = "! html"; #text/html=! w3m -T text/html -I UTF-8 #text/*=bat -fP --file-name="$AERC_FILENAME" #application/x-sh=bat -fP -l sh #image/*=catimg -w $(tput cols) - #subject,~Git(hub|lab)=lolcat -f #from,thatguywhodoesnothardwraphismessages=wrap -w 100 | colorize ".headers" = "colorize"; "image/*" = "${pkgs.libsixel}/bin/img2sixel - "; # image/*=catimg -w$(tput cols) - }; }; }; accounts.email.accounts = { pablo_tools = { folders = { # send = "SENT"; inbox = "INBOX"; }; aerc.enable = true; address = "mail@pablo.tools"; aliases = [ "git@pablo.tools" "github@pablo.tools" "pablo1@mailbox.org" ]; realName = "Pablo Ovelleiro Corral"; primary = true; maildir.path = "pablo_tools"; signature = { text = '' Pablo Ovelleiro Corral Web: https://pablo.tools Matrix: @pinpox:matrix.org Github: https://github.com/pinpox ''; showSignature = "append"; }; userName = "pablo1@mailbox.org"; passwordCommand = "passage show mailbox.org/himalaya"; imap = { host = "imap.mailbox.org"; tls.enable = true; }; smtp = { host = "smtp.mailbox.org"; port = 465; }; }; }; }; } ================================================ FILE: home-manager/modules/firefox/default.nix ================================================ { config, pkgs, lib, ... }: with lib; let cfg = config.pinpox.programs.firefox; in { options.pinpox.programs.firefox.enable = mkEnableOption "firefox browser"; config = mkIf cfg.enable { # Browserpass programs.browserpass = { enable = true; # browsers = [ "chromium" "firefox" ]; browsers = [ "firefox" ]; }; programs.firefox = { enable = true; package = pkgs.firefox; profiles = { pinpox = { # containers = { # # dangerous = { # # color = "red"; # # icon = "fruit"; # # id = 2; # # }; # work = { # color = "blue"; # icon = "cart"; # id = 1; # }; # }; search = { force = true; engines = { "Nix Options" = { urls = [ { template = "https://search.nixos.org/options"; params = [ { name = "channel"; value = "unstable"; } { name = "query"; value = "{searchTerms}"; } ]; } ]; icon = "${pkgs.nixos-icons}/share/icons/hicolor/scalable/apps/nix-snowflake.svg"; definedAliases = [ "@no" ]; }; "Nix Packages" = { urls = [ { template = "https://search.nixos.org/packages"; params = [ { name = "type"; value = "packages"; } { name = "query"; value = "{searchTerms}"; } ]; } ]; icon = "${pkgs.nixos-icons}/share/icons/hicolor/scalable/apps/nix-snowflake.svg"; definedAliases = [ "@np" ]; }; }; }; extensions.packages = with pkgs.nur.repos.rycee.firefox-addons; [ bitwarden darkreader web-search-navigator ublock-origin simple-tab-groups ]; isDefault = true; settings = { # 0 => blank page # 1 => your home page(s) {default} # 2 => the last page viewed in Firefox # 3 => previous session windows and tabs "browser.startup.page" = "3"; # Set the homepage "browser.startup.homepage" = "https://nixos.org"; # Export bookmarks to bookmarks.html when closing firefox "browser.bookmarks.autoExportHTML" = "true"; # Path where to export. Default is: # ~/.mozilla/firefox/pinpox/bookmarks.html # "browser.bookmarks.file" = # "browser.display.background_color" = "#${config.pinpox.colors.Black}"; # "browser.display.foreground_color" = "#${config.pinpox.colors.White}"; "browser.display.use_system_colors" = "true"; "browser.anchor_color" = "#${config.pinpox.colors.Yellow}"; "browser.display.use_document_colors" = "false"; # "browser.search.region" = "GB"; # "browser.search.isUS" = false; # "distribution.searchplugins.defaultLocale" = "en-GB"; # "general.useragent.locale" = "en-GB"; # "browser.bookmarks.showMobileBookmarks" = true; # TODO if possible, enable sync (log in) "toolkit.legacyUserProfileCustomizations.stylesheets" = true; "extensions.activeThemeID" = "default-theme@mozilla.org"; "devtools.theme" = "dark"; "dom.security.https_only_mode" = "true"; # HTTPS everywhere # Disable password managger "signon.rememberSignons" = "false"; "signon.autofillForms" = "false"; "signon.autofillForms.http" = "false"; }; # userChrome = builtins.readFile # (utils.renderMustache "userChrome.css" ./userchrome.css.mustache # { colors = config.pinpox.colors; font = fonts; }); # TODO userContent = '' @import url("userChrome.css"); /* Removes white loading page */ @-moz-document url(about:blank), url(about:newtab), url(about:home) { html:not(#ublock0-epicker), html:not(#ublock0-epicker) body, #newtab-customize-overlay { background: var(--mff-bg) !important; } } ''; }; }; }; }; } ================================================ FILE: home-manager/modules/firefox/userchrome.css.mustache ================================================ :root { /* Minimal Functional Fox variables*/ --mff-bg: #{{colors.Black}}; --mff-icon-color: #{{colors.Blue}}; --mff-nav-toolbar-padding: 8px; --mff-sidebar-bg: var(--mff-bg); --mff-sidebar-color: #{{colors.White}}; --mff-tab-border-radius: 0px; --mff-tab-color: #{{colors.White}}; --mff-tab-font-family: "{{font.normal}}"; --mff-tab-font-size: 11pt; --mff-tab-font-weight: 500; --mff-tab-height: 32px; --mff-tab-pinned-bg: #{{colors.Magenta}}; --mff-tab-selected-bg: #{{colors.BrightBlack}}; --mff-tab-selected-fg: #{{colors.Yellow}}; --mff-tab-soundplaying-bg: #{{colors.Magenta}}; --mff-urlbar-color: #{{colors.White}}; --mff-urlbar-focused-color: #{{colors.Blue}}; --mff-urlbar-font-family: "{{font.normal}}"; --mff-urlbar-font-size: 12pt; --mff-urlbar-font-weight: 500; --mff-urlbar-results-color: #{{colors.White}}; --mff-urlbar-results-font-family: "{{font.normal}}"; --mff-urlbar-results-font-size: 12pt; --mff-urlbar-results-font-weight: 500; --mff-urlbar-results-url-color: #{{colors.Blue}}; /* --mff-tab-selected-bg: linear-gradient(90deg, rgba(232,74,95,1) 0%, rgba(255,132,124,1) 50%, rgba(254,206,168,1) 100%); */ /* --mff-urlbar-font-weight: 500; */ /* Overriden Firefox variables*/ --autocomplete-popup-background: var(--mff-bg) !important; --default-arrowpanel-background: var(--mff-bg) !important; --default-arrowpanel-color: #{{colors.White}}!important; --lwt-toolbarbutton-icon-fill: var(--mff-icon-color) !important; --panel-disabled-color: #{{colors.White}}00; --toolbar-bgcolor: var(--mff-bg) !important; --urlbar-separator-color: transparent !important; } /* Tabs */ .tab-background[selected="true"] { background: var(--mff-tab-selected-bg) !important; } .tab-text[selected="true"] { color: #{{colors.Yellow}} !important; } .tab-background:not[visuallyselected] { background: var(--mff-tab-selected-bg) !important; colors: red; opacity: 0.5 !important; } /* This positions the tabs under the navaigator container */ #titlebar { -moz-box-ordinal-group: 3 !important; } .tabbrowser-tab::after, .tabbrowser-tab::before { border-left: none !important; } .tab-background { border: none !important; background: #{{colors.BrightBlack}} !important; } .tabbrowser-arrowscrollbox { margin-inline-start: 4px !important; margin-inline-end: 0px !important; } .tab-close-button { display: none !important; } .tab-text { font-family: var(--mff-tab-font-family); font-weight: var(--mff-tab-font-weight); font-size: var(--mff-tab-font-size) !important; color: var(--mff-tab-color); } /* Show the favicon for tabs that are pinned */ hbox.tab-content[pinned=true] .tab-icon-image { display: initial !important; } hbox.tab-content[pinned=true] .tab-text { display: none !important; } #tabbrowser-tabs { --tab-loading-fill: #033433 !important; } .tab-label-container:not([textoverflow]) { display: flex; overflow: hidden; justify-content: center; width: 50% !important; max-width: 50% !important; min-width: 50% !important; } .tab-line { display: none !important; } .tabbrowser-tab { border-radius: var(--mff-tab-border-radius) !important; border-width: 0; height: var(--mff-tab-height) !important; margin-bottom: 4px !important; margin-inline-end: 4px !important; margin-top: 4px !important; max-height: var(--mff-tab-height) !important; min-height: var(--mff-tab-height) !important; } .tabbrowser-tab[soundplaying="true"] { background-color: var(--mff-tab-soundplaying-bg) !important; } .tab-icon-sound { display: none !important; } /* Toolbar */ .urlbar-icon > image { fill: var(--mff-icon-color) !important; color: var(--mff-icon-color) !important; } .toolbarbutton-text { color: var(--mff-icon-color) !important; } .urlbar-icon { color: var(--mff-icon-color) !important; } .toolbarbutton-icon { /* filter: drop-shadow(0 0 0.75rem crimson); */ } #urlbar-results { font-family: var(--mff-urlbar-results-font-family); font-weight: var(--mff-urlbar-results-font-weight); font-size: var(--mff-urlbar-results-font-size) !important; color: var(--mff-urlbar-results-color) !important; } .urlbarView-row[type="bookmark"] > span{ color: green !important; } .urlbarView-row[type="switchtab"] > span{ color: orange !important; } .urlbarView-url, .search-panel-one-offs-container { color: var(--mff-urlbar-results-url-color) !important; font-family: var(--mff-urlbar-font-family); font-weight: var(--mff-urlbar-results-font-weight); font-size: var(--mff-urlbar-font-size) !important; } .urlbarView-favicon, .urlbarView-type-icon { display: none !important; } #urlbar-input { font-size: var(--mff-urlbar-font-size) !important; color: var(--mff-urlbar-color) !important; font-family: var(--mff-urlbar-font-family) !important; font-weight: var(--mff-urlbar-font-weight)!important; text-align: center !important; } #tracking-protection-icon-container, #identity-box { display: none; } #back-button > .toolbarbutton-icon{ --backbutton-background: transparent !important; border: none !important; } toolbar { background-image: none !important; } #urlbar-background { opacity: .98 !important; } #navigator-toolbox, toolbaritem { border: none !important; } #urlbar-background { background-color: var(--mff-bg) !important; border: none !important; } .toolbar-items { background-color: var(--mff-bg) !important; } #sidebar-search-container { background-color: var(--mff-sidebar-bg) !important; } box.panel-arrowbox { display: none; } box.panel-arrowcontent { border-radius: 8px !important; border: none !important; } tab.tabbrowser-tab { overflow: hidden; } tab.tabbrowser-tab:hover { box-shadow: 0 1px 4px rgba(0,0,0,.05); } image#star-button { display: none; } toolbar#nav-bar { padding: var(--mff-nav-toolbar-padding) !important; } toolbar#nav-bar { padding: 4px !important; } #urlbar { max-width: 70% !important; margin: 0 15% !important; /* position: unset!important; */; } #urlbar-input:focus { color: var(--mff-urlbar-focused-color) !important; } .megabar[breakout-extend="true"]:not([open="true"]) > #urlbar-background { box-shadow: none !important; background-color: transparent !important; } toolbarbutton { box-shadow: none !important; } /* Sidebar */ .close-icon, .urlbar-icon { fill: var(--mff-icon-color) !important; } .sidebar-placesTree { color: var(--mff-sidebar-color) !important; } #sidebar-box { --sidebar-background-color: var(--mff-sidebar-bg) !important; } splitter#sidebar-splitter { opacity: 0 !important; } splitter#sidebar-splitter { border: none !important; background-color: transparent !important; } image#sidebar-icon { display: none; } /* Arrow panel */ .panel-arrowcontent { padding: 0px !important; margin: 0px !important; } toolbarseparator { display: none; } ================================================ FILE: home-manager/modules/fonts/default.nix ================================================ { config, lib, ... }: with lib; let cfg = config.pinpox.defaults.fonts; in { options.pinpox = { defaults.fonts.enable = mkEnableOption "font defaults"; font = { normal = { family = mkOption { type = types.str; default = "Berkeley Mono"; }; style = mkOption { type = types.str; default = "Regular"; }; }; bold = { family = mkOption { type = types.str; default = "Berkeley Mono"; }; style = mkOption { type = types.str; default = "Bold"; }; }; italic = { family = mkOption { type = types.str; default = "Berkeley Mono"; }; style = mkOption { type = types.str; default = "Regular Italic"; }; }; size = 10; }; }; config = mkIf cfg.enable { fonts.fontconfig.enable = true; # home.packages = # [ flake-inputs.nix-apple-fonts.packages."x86_64-linux".sf-mono ]; }; } ================================================ FILE: home-manager/modules/foot/default.nix ================================================ { lib, pkgs, config, ... }: with lib; let cfg = config.pinpox.programs.foot; in { options.pinpox.programs.foot.enable = mkEnableOption "foot terminal emulator"; config = mkIf cfg.enable { home.packages = with pkgs; [ nerd-fonts.inconsolata foot ]; programs.foot = { enable = true; server.enable = true; settings = { main = { term = "xterm-256color"; font = "Berkeley Mono:size=13"; # dpi-aware = "yes"; # Defaults to auto }; scrollback.lines = 10000; cursor = { style = "beam"; blink = "yes"; # beam-thickness = }; colors = { alpha = "0.9"; # background = "${config.pinpox.colors.White}"; # foreground = "${config.pinpox.colors.Black}"; background = "${config.pinpox.colors.Black}"; foreground = "${config.pinpox.colors.White}"; ## Normal/regular colors (color palette 0-7) regular0 = "${config.pinpox.colors.Black}"; # black regular1 = "${config.pinpox.colors.Red}"; # red regular2 = "${config.pinpox.colors.Green}"; # green regular3 = "${config.pinpox.colors.Yellow}"; # yellow regular4 = "${config.pinpox.colors.Blue}"; # blue regular5 = "${config.pinpox.colors.Magenta}"; # magenta regular6 = "${config.pinpox.colors.Cyan}"; # cyan regular7 = "${config.pinpox.colors.White}"; # white ## Bright colors (color palette 8-15) bright0 = "${config.pinpox.colors.BrightBlack}"; # black bright1 = "${config.pinpox.colors.BrightRed}"; # red bright2 = "${config.pinpox.colors.BrightGreen}"; # green bright3 = "${config.pinpox.colors.BrightYellow}"; # yellow bright4 = "${config.pinpox.colors.BrightBlue}"; # blue bright5 = "${config.pinpox.colors.BrightMagenta}"; # magenta bright6 = "${config.pinpox.colors.BrightCyan}"; # cyan bright7 = "${config.pinpox.colors.BrightWhite}"; # white }; # mouse = { # hide-when-typing = "yes"; # }; }; }; }; } ================================================ FILE: home-manager/modules/games/default.nix ================================================ { config, lib, pkgs, ... }: with lib; let cfg = config.pinpox.programs.games; in { options.pinpox.programs.games.enable = mkEnableOption "games"; config = mkIf cfg.enable { home.packages = with pkgs; [ retroarch-free ]; }; } ================================================ FILE: home-manager/modules/git/default.nix ================================================ { pkgs, config, lib, ... }: with lib; let cfg = config.pinpox.defaults.git; prePushHook = pkgs.writeShellApplication { name = "pre-push"; runtimeInputs = [ pkgs.jq ]; text = '' [ -f flake.lock ] || exit 0 matches=$(jq -r ' .nodes | to_entries[] | select( .value.locked.type == "path" or ((.value.locked.url // "") | startswith("file://")) ) | "\(.key): \(.value.locked.path // .value.locked.url)" ' flake.lock) if [ -n "$matches" ]; then echo "warning: local-path inputs in flake.lock:" >&2 echo "$matches" >&2 if [ -e /dev/tty ]; then read -r -p "Push anyway? [y/N] " reply < /dev/tty [[ "$reply" =~ ^[Yy]$ ]] || { echo "aborted." >&2; exit 1; } else echo "error: no tty for confirmation — refusing push" >&2 exit 1 fi fi ''; }; in { options.pinpox.defaults.git.enable = mkEnableOption "git defaults"; config = mkIf cfg.enable { home.packages = with pkgs; [ tig jjui ]; programs = { lazygit = { enable = true; # https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md settings = { # reporting = "off"; # update.method = "never"; shortTimeFormat = "15h:30:13"; gui.showFileTree = true; os = { edit = "nvim {{filename}}"; editAtLine = "nvim +{{line}} {{filename}}"; editAtLineAndWait = "nvim --remote-wait +{{line}} {{filename}}"; editInTerminal = true; }; }; }; git = { enable = true; lfs.enable = true; ignores = [ "tags" "*.swp" "result" ".claude" ]; settings = { init.defaultBranch = "main"; core.hooksPath = "${prePushHook}/bin"; pull = { rebase = true; autostash = true; twohead = "ort"; }; push = { default = "simple"; autoSetupRemote = true; }; # rerere = { # autoUpdate = true # enabled = true # }; branch = { autoSetupRebase = "always"; autoSetupMerge = "always"; }; rebase = { stat = true; autoStash = true; autoSquash = true; updateRefs = true; }; help.autocorrect = 10; signing = { format = "ssh"; key = "~/.ssh/key.pub"; signByDefault = true; }; alias = { s = "status"; d = "diff"; a = "add"; c = "commit"; p = "push"; o = "checkout"; co = "checkout"; uncommit = "reset --soft HEAD^"; comma = "commit --amend"; reset-pr = "reset --hard FETCH_HEAD"; force-push = "push --force-with-lease"; }; user.email = "git@pablo.tools"; user.name = "pinpox"; }; }; }; # [kiwi] evaluation warning: pinpox profile: The option `programs.git.aliases' defined in `/nix/store/fsc89mqrbiij6pnl4vrf0l0plv0i8pp6-source/clan-service-modules/machine-type/desktop.nix' has been renamed to `programs.git.settings.alias'. # [kiwi] evaluation warning: pinpox profile: The option `programs.git.extraConfig' defined in `/nix/store/fsc89mqrbiij6pnl4vrf0l0plv0i8pp6-source/clan-service-modules/machine-type/desktop.nix' has been renamed to `programs.git.settings'. programs.jujutsu = { enable = true; settings = { merge-tools.meld.merge-args = [ "$left" "$base" "$right" "-o" "$output" "--auto-merge" ]; signing = { behavior = "own"; backend = "ssh"; key = "~/.ssh/key.pub"; allowed-signers = "~/.ssh/allowed_signers"; }; ui = { pager = "less -FRX"; default-command = "log"; merge-editor = [ "meld" "$left" "$base" "$right" "-o" "$output" ]; }; user = { email = "git@pablo.tools"; name = "pinpox"; }; }; }; }; } ================================================ FILE: home-manager/modules/go/default.nix ================================================ { config, lib, ... }: with lib; let cfg = config.pinpox.programs.go; in { options.pinpox.programs.go.enable = mkEnableOption "go compiler"; config = mkIf cfg.enable { programs = { go = { enable = true; env.GOPATH = "/home/pinpox/.go"; # packages = { # "golang.org/x/text" = # builtins.fetchGit "https://go.googlesource.com/text"; # "golang.org/x/time" = # builtins.fetchGit "https://go.googlesource.com/time"; # }; }; }; }; } ================================================ FILE: home-manager/modules/grobi/default.nix ================================================ { config, pkgs, lib, ... }: with lib; let cfg = config.pinpox.defaults.grobi; in { options.pinpox.defaults.grobi.enable = mkEnableOption "grobi defaults"; config = mkIf cfg.enable { services = { grobi = { # enable = true; enable = false; # executeAfter = [ " " ]; rules = [ { name = "kartoffel"; outputs_connected = [ "DVI-D-0" "DP-0" "DVI-D-1" ]; configure_row = [ "DVI-D-0" "DP-0" "DVI-D-1" ]; atomic = true; execute_after = [ '' ${pkgs.xorg.xrandr}/bin/xrandr \ --output DVI-D-0 --mode 1920x1200 --pos 3460x0 --rotate normal \ --output DP-0 --primary --mode 2560x1440 --pos 900x0 --rotate normal \ --output DVI-D-1 --mode 1440x900 --pos 0x0 --rotate right \ --output DP-1 --off \ --output HDMI-0 --off '' ]; } ]; }; }; }; } ================================================ FILE: home-manager/modules/gtk/banana.nix ================================================ { config, pkgs, ... }: { config = { home.pointerCursor = { name = "Banana"; size = 32; package = pkgs.banana-cursor; x11.enable = true; gtk.enable = true; }; wayland.windowManager.sway.config.seat."*".xcursor_theme = "${config.gtk.cursorTheme.name} ${toString config.gtk.cursorTheme.size}"; gtk.cursorTheme = { name = "Banana"; size = 32; package = pkgs.banana-cursor; }; }; } ================================================ FILE: home-manager/modules/gtk/default.nix ================================================ { config, pkgs, lib, ... }: with lib; let cfg = config.pinpox.defaults.gtk; # TODO use flake inputs materia-theme = pkgs.fetchFromGitHub { owner = "nana-4"; repo = "materia-theme"; rev = "76cac96ca7fe45dc9e5b9822b0fbb5f4cad47984"; sha256 = "sha256-0eCAfm/MWXv6BbCl2vbVbvgv8DiUH09TAUhoKq7Ow0k="; # Old version # "e329aaee160c82e85fe91a6467c666c7f9f2a7df"; # sha256 = "1qmq5ycfpzv0rcp5aav4amlglkqy02477i4bdi7lgpbn0agvms6c"; fetchSubmodules = true; }; materia_colors = pkgs.writeTextFile { name = "gtk-generated-colors"; text = '' BTN_BG=${config.pinpox.colors.BrightBlack} BTN_FG=${config.pinpox.colors.BrightWhite} FG=${config.pinpox.colors.White} BG=${config.pinpox.colors.Black} HDR_BTN_BG=${config.pinpox.colors.BrightBlack} HDR_BTN_FG=${config.pinpox.colors.White} ACCENT_BG=${config.pinpox.colors.Green} ACCENT_FG=${config.pinpox.colors.Black} HDR_FG=${config.pinpox.colors.White} HDR_BG=${config.pinpox.colors.BrightBlack} MATERIA_SURFACE=${config.pinpox.colors.BrightBlack} MATERIA_VIEW=${config.pinpox.colors.BrightBlack} MENU_BG=${config.pinpox.colors.BrightBlack} MENU_FG=${config.pinpox.colors.BrightWhite} SEL_BG=${config.pinpox.colors.Blue} SEL_FG=${config.pinpox.colors.Magenta} TXT_BG=${config.pinpox.colors.BrightBlack} TXT_FG=${config.pinpox.colors.BrightWhite} WM_BORDER_FOCUS=${config.pinpox.colors.White} WM_BORDER_UNFOCUS=${config.pinpox.colors.BrightBlack} UNITY_DEFAULT_LAUNCHER_STYLE=False NAME=generated MATERIA_COLOR_VARIANT=dark MATERIA_STYLE_COMPACT=True ''; }; in { options.pinpox.defaults.gtk.enable = mkEnableOption "gtk defaults"; imports = [ ./banana.nix ]; config = mkIf cfg.enable { nixpkgs.overlays = [ (self: super: { rendersvg = self.runCommand "rendersvg" { } '' mkdir -p $out/bin ln -s ${self.resvg}/bin/resvg $out/bin/rendersvg ''; generated-gtk-theme = self.stdenv.mkDerivation { name = "generated-gtk-theme"; src = materia-theme; buildInputs = with self; [ sassc bc which rendersvg meson ninja dart-sass gtk4.dev optipng ]; MATERIA_COLORS = materia_colors; phases = [ "unpackPhase" "installPhase" ]; installPhase = '' HOME=/build chmod 777 -R . patchShebangs . mkdir -p $out/share/themes mkdir bin sed -e 's/handle-horz-.*//' -e 's/handle-vert-.*//' -i ./src/gtk-2.0/assets.txt echo "Changing colours:" ./change_color.sh -o Generated "$MATERIA_COLORS" -i False -t "$out/share/themes" chmod 555 -R . ''; }; }) ]; # GTK settings gtk = { enable = true; font = { name = "Berkeley Mono"; # package = pkgs.iosevka; }; iconTheme = { name = "Papirus-Dark"; package = pkgs.papirus-icon-theme; }; theme = { name = "Generated"; package = pkgs.generated-gtk-theme; }; }; home.sessionVariables.GTK_THEME = "Generated"; # Set default dark theme via dconf (can be changed at runtime) dconf.settings = { "org/gnome/desktop/interface" = { color-scheme = "prefer-dark"; }; }; }; } ================================================ FILE: home-manager/modules/helix/default.nix ================================================ { config, lib, pkgs, ... }: with lib; let cfg = config.pinpox.programs.helix; in { options.pinpox.programs.helix = { enable = mkEnableOption "Helix editor configuration"; }; config = mkIf cfg.enable { programs.helix = { enable = true; # https://docs.helix-editor.com/languages.html languages = { language = [ { name = "nix"; auto-format = false; formatter.command = "${pkgs.nixpkgs-fmt}/bin/nixpkgs-fmt"; } ]; }; settings = { editor = { indent-guides.render = true; bufferline = "multiple"; cursorline = true; cursor-shape = { insert = "bar"; normal = "block"; select = "underline"; }; lsp.display-messages = true; }; theme = "catppuccin_mocha"; keys = { normal = { ";" = "command_mode"; "C-g" = [ ":new" ":insert-output ${pkgs.lazygit}/bin/lazygit" ":buffer-close!" ":redraw" ]; }; select = { ";" = "command_mode"; }; }; }; }; # Add helix to home packages home.packages = with pkgs; [ helix ]; }; } ================================================ FILE: home-manager/modules/k9s/default.nix ================================================ { config, lib, pkgs, ... }: with lib; let cfg = config.pinpox.programs.k9s; in { options.pinpox.programs.k9s.enable = mkEnableOption "k9s kubernetes CLI"; config = mkIf cfg.enable { home.packages = [ pkgs.k9s ]; xdg = { enable = true; configFile.k9s_theme = { target = "k9s/skin.yml"; text = builtins.toJSON { k9s = { body = { fgColor = "#cad3f5"; bgColor = "#24273a"; logoColor = "#cba6f7"; }; info = { fgColor = "#74c7ec"; sectionColor = "#8aadf4"; }; frame = { border = { fgColor = "#cba6f7"; focusColor = "#f5bde6"; }; menu = { fgColor = "#eed49f"; keyColor = "#8aadf4"; numKeyColor = "#f5bde6"; }; crumbs = { fgColor = "#cad3f5"; bgColor = "#8aadf4"; activeColor = "#74c7ec"; }; status = { newColor = "#a6da95"; modifyColor = "#68f288"; addColor = "#74c7ec"; errorColor = "#ed8796"; highlightcolor = "#8aadf4"; killColor = "#747c9e"; completedColor = "#5b6078"; }; title = { fgColor = "#8bd5ca"; bgColor = "#5b6078"; highlightColor = "#74c7ec"; counterColor = "#cba6f7"; filterColor = "#5b6078"; }; }; views = { table = { fgColor = "#cad3f5"; bgColor = "#24273a"; cursorColor = "#8bd5ca"; header = { fgColor = "#24273a"; bgColor = "#8aadf4"; }; }; yaml = { keyColor = "#8bd5ca"; colonColor = "#aee2da"; valueColor = "#a6da95"; }; logs = { fgColor = "#cad3f5"; bgColor = "#24273a"; }; }; }; }; }; }; }; } ================================================ FILE: home-manager/modules/kanshi/default.nix ================================================ { config, pkgs, lib, ... }: with lib; let cfg = config.pinpox.programs.kanshi; in { options.pinpox.programs.kanshi.enable = mkEnableOption "kanshi screen setup"; config = mkIf cfg.enable { home.packages = with pkgs; [ kanshi ]; # output eDP-1 mode 1920x1080 position 0,0 # output DP-1 mode 2560x1440 position 1080,0 # output DP-2 mode 2560x1440 position 3640,0 services.kanshi = { enable = true; settings = [ { profile.name = "laptop-only"; profile.outputs = [ { criteria = "eDP-1"; mode = "2880x1920@120Hz"; scale = 2.0; } ]; } { profile.name = "laptop-external-right"; profile.outputs = [ { criteria = "eDP-1"; mode = "2880x1920@120Hz"; position = "0,0"; scale = 2.0; status = "enable"; } { criteria = "Lenovo Group Limited Y27h-30 U3V040YW"; mode = "2560x1440"; position = "1440,0"; status = "enable"; } ]; } ]; }; }; } ================================================ FILE: home-manager/modules/mako/default.nix ================================================ { pkgs, config, lib, ... }: with lib; let cfg = config.pinpox.programs.mako; in { options.pinpox.programs.mako.enable = mkEnableOption "mako notifications"; config = mkIf cfg.enable { # Needed for firefox and thunderbird home.packages = [ pkgs.libnotify ]; services.mako = { enable = true; settings = { anchor = "top-right"; background-color = "#285577FF"; border-color = "#4C7899FF"; # progressColor = "over #5588AAFF"; text-color = "#FFFFFFFF"; border-radius = "5"; border-size = "5"; default-timeout = "10000"; # In milliseconds # extraConfig = ''''; font = "Berkeley Mono 12"; # %a Application name # %s Notification summary # %b Notification body # %g Number of notifications in the current group # %i Notification id # format = "%s\\n%b"; # groupBy = ""; height = "200"; width = "300"; # iconPath = ""; icons = "true"; margin = "10"; padding = "5"; }; }; }; } ================================================ FILE: home-manager/modules/mpv/default.nix ================================================ { lib, pkgs, config, ... }: with lib; let cfg = config.pinpox.programs.mpv; in { options.pinpox.programs.mpv.enable = mkEnableOption "mpv media player"; config = mkIf cfg.enable { programs.mpv = { enable = true; config = { # Screenshot settings screenshot-directory = "~/Pictures"; screenshot-format = "png"; screenshot-template = "photo-%F-%T"; # Low latency for camera preview profile = "low-latency"; untimed = "yes"; }; # Rotation for webcam (applied to v4l2 protocol) profiles = { "protocol.av" = { vf = "rotate=PI"; }; }; bindings = { # Screenshot bindings "s" = "screenshot"; "MBTN_LEFT" = "screenshot"; }; }; }; } ================================================ FILE: home-manager/modules/neomutt/default.nix ================================================ { config, lib, ... }: with lib; let cfg = config.pinpox.programs.neomutt; in { options.pinpox.programs.neomutt.enable = mkEnableOption "neomutt mail client"; config = mkIf cfg.enable { programs.neomutt = { enable = true; sidebar = { enable = true; }; extraConfig = '' set imap_user = "pablo1@mailbox.org" set imap_pass = "`pass mailbox.org/pablo1@mailbox.org`" ''; }; }; } ================================================ FILE: home-manager/modules/newsboat/default.nix ================================================ { config, lib, ... }: with lib; let cfg = config.pinpox.programs.newsboat; splitString = str: builtins.filter builtins.isString (builtins.split "\n" str); in { options.pinpox.programs.newsboat.enable = mkEnableOption "newsboat RSS reader"; config = mkIf cfg.enable { programs.newsboat = { enable = true; autoReload = true; urls = [ # https://hackaday.com/blog/feed/ { title = "nixOS mobile"; tags = [ "nixos" "nix" ]; url = "https://mobile.nixos.org/index.xml"; } { title = "r/NixOS"; tags = [ "nixos" "nix" "reddit" ]; url = "https://www.reddit.com/r/NixOS.rss"; } { title = "NixOS weekly"; tags = [ "nixos" "nix" ]; url = "https://weekly.nixos.org/feeds/all.rss.xml"; } ] ++ (map (x: { url = x; tags = [ "rss" ]; }) (splitString (builtins.readFile ./newsboat/rss.txt))) ++ (map (x: { url = x; tags = [ "podcast" ]; }) (splitString (builtins.readFile ./newsboat/podcast.txt))) ++ (map (x: { url = x; tags = [ "youtube" ]; }) (splitString (builtins.readFile ./newsboat/youtube.txt))); }; }; } ================================================ FILE: home-manager/modules/obs-studio/default.nix ================================================ { lib, pkgs, config, system-config, ... }: with lib; let cfg = config.pinpox.programs.obs-studio; droidcam-port = 5201; in { options.pinpox.programs.obs-studio.enable = mkEnableOption "obs-studio"; config = mkIf cfg.enable { # assertions = [ # { # assertion = (builtins.elem droidcam-port system-config.networking.firewall.allowedTCPPorts); # message = "Port ${toString droidcam-port}/tcp is not open in the firewall, but required by droidcam"; # } # ]; home.packages = [ pkgs.uxplay # AirPlay Unix mirroring server pkgs.slurp pkgs.xdg-desktop-portal pkgs.xdg-desktop-portal-gtk ]; programs.obs-studio = { enable = true; plugins = with pkgs.obs-studio-plugins; [ obs-pipewire-audio-capture # droidcam-obs wlrobs # obs-vintage-filter # obs-teleport obs-backgroundremoval input-overlay ]; }; }; } ================================================ FILE: home-manager/modules/pandoc/default.nix ================================================ { config, lib, ... }: with lib; let cfg = config.pinpox.programs.pandoc; in { options.pinpox.programs.pandoc.enable = mkEnableOption "pandoc config"; config = mkIf cfg.enable { programs.pandoc = { enable = true; citationStyles = [ ]; # templates = { # "default.latex" = path/to/your/template; # }; defaults = { metadata = { author = "Pablo Ovelleiro Corral"; }; # pdf-engine = "xelatex"; # citeproc = true; }; }; }; } ================================================ FILE: home-manager/modules/pi-mono/default.nix ================================================ { lib, pkgs, config, ... }: with lib; let cfg = config.pinpox.programs.pi; # Generate models.json from the declarative provider config modelsJson = pkgs.writeText "models.json" ( builtins.toJSON { providers = cfg.providers; } ); in { options.pinpox.programs.pi = { enable = mkEnableOption "pi coding agent"; package = mkOption { type = types.package; default = pkgs.pi; description = "The pi package to install."; }; extensions = mkOption { type = types.attrsOf types.path; default = { }; description = '' Extension name to source path mappings. Each entry is symlinked into `~/.pi/agent/extensions/`. Sources can be local paths, fetchGit results, or built packages. Pi auto-discovers extensions from `~/.pi/agent/extensions/` — both single `.ts` files and directories with an `index.ts` entry point. Note: Extensions that require `npm install` (i.e. have runtime dependencies in package.json) need to be built first or installed via `pi install` instead. ''; example = literalExpression '' { my-extension = ./extensions/my-extension.ts; } ''; }; providers = mkOption { type = types.attrsOf types.attrs; default = { }; description = '' Model provider configurations, keyed by provider name. All providers are merged and written to `~/.pi/agent/models.json` as a read-only symlink. Pi only reads this file, never writes to it. Can be set from multiple modules (e.g. machine-specific configs) and Nix will merge them. ''; example = literalExpression '' { ollama = { baseUrl = "http://100.96.100.103:11434/v1"; api = "openai-completions"; apiKey = "dummy"; compat = { supportsDeveloperRole = false; supportsReasoningEffort = false; }; models = [ { id = "llama3.3:latest"; contextWindow = 128000; maxTokens = 32000; } ]; }; } ''; }; }; config = mkIf cfg.enable { home.packages = [ cfg.package ]; home.file = mkMerge [ # Symlink extensions into the auto-discovery directory (mapAttrs' (name: src: { name = ".pi/agent/extensions/${name}"; value.source = src; }) cfg.extensions) # models.json as read-only symlink (only when providers are configured) (mkIf (cfg.providers != { }) { ".pi/agent/models.json".source = modelsJson; }) ]; }; } ================================================ FILE: home-manager/modules/rio/config/rio/config.toml ================================================ # Modifier to line-height. Default is `1.0` line-height = 1.06 # Environment variables env-vars = ["TERM=xterm-256color"] # Don't confirm before exiting Rio confirm-before-quit = false [adaptive-theme] light = "wildcharm-light" dark = "wildcharm-dark" # light = "pinpox-light" # dark = "pinpox-dark" # Hide the cursor while typing # hide-cursor-when-typing = false # Ignore theme selection foreground color # ignore-selection-foreground-color = false # Startup directory # # Directory the shell is started in. If this is unset the working # directory of the parent process will be used. # # This configuration only has effect if use-fork is disabled. # # Example: # working-dir = "/Users/raphael/Documents/" # Use fork # # Defaults for POSIX-based systems (Windows is not configurable): # MacOS: spawn processes # Linux/BSD: fork processes # # Example: # use-fork = false # Cursor # # shape - Default cursor shape is 'block' # Other available options are: 'underline', 'beam' or 'hidden' # # blinking - Whether the cursor blinks. The default is false # # blinking-interval - Cursor update on milliseconds interval # # [cursor] # shape = 'block' # blinking = false # blinking-interval = 800 # Editor # # Default editor on Linux and MacOS is "vi", # on Windows it is "notepad". # # Whenever the key binding `OpenConfigEditor` is triggered it will # use the value of the editor along with the rio configuration path. [editor] program = "nvim" # args = [] # Window configuration # # • width - define the initial window width. # Default: 600 # # • height - define the initial window height. # Default: 400 # # • mode - define how the window will be created # - "Windowed" (default) is based on width and height # - "Maximized" window is created with maximized # - "Fullscreen" window is created with fullscreen # # • opacity - Set window opacity # # • blur - Set blur on the window background. Changing this config requires restarting Rio to take effect. # # • decorations - Set window decorations, options: "Enabled", "Disabled", "Transparent", "Buttonless" # # • colorspace - Set the color space for the window # - "srgb" (default on non-macOS) # - "display-p3" (default on macOS) # - "rec2020" # # Example: [window] # width = 600 # height = 400 # mode = "windowed" opacity = 0.95 blur = true # decorations = "enabled" # colorspace = "display-p3" # Renderer # # • Performance: Set WGPU rendering performance # - High: Adapter that has the highest performance. This is often a discrete GPU. # - Low: Adapter that uses the least possible power. This is often an integrated GPU. # # • Backend: Set WGPU rendering backend # - Automatic: Leave Sugarloaf/WGPU to decide # - GL: Supported on Linux/Android, and Windows and macOS/iOS via ANGLE # - Vulkan: Supported on Windows, Linux/Android # - DX12: Supported on Windows 10 # - Metal: Supported on macOS/iOS # # • disable-unfocused-render: This property disable renderer processes while Rio is unfocused. # # • level: Configure renderer level # - Available options: 0 and 1. # Higher the level more rendering features and computations # will be done like enable font ligatures or emoji support. # For more information please check the docs. # # • filters: A list of paths to RetroArch slang shaders. Might not work with OpenGL. # # Example: # [renderer] # performance = "high" # backend = "automatic" # disable-unfocused-render = false # level = 1 # filters = [] # Keyboard # # use-kitty-keyboard-protocol - Enable Kitty Keyboard protocol # # disable-ctlseqs-alt - Disable ctlseqs with ALT keys # - For example: Terminal.app does not deal with ctlseqs with ALT keys # # ime-cursor-positioning - Enable IME cursor positioning # - When enabled, the IME input popup will appear at the cursor position # - Default is true # # Example: # [keyboard] # use-kitty-keyboard-protocol = false # disable-ctlseqs-alt = false # ime-cursor-positioning = true # Fonts # # Configure fonts used by the terminal # # Note: You can set different font families but Rio terminal # will always look for regular font bounds whene # # You can also set family on root to overwrite all fonts. # # You can also specify extra fonts to load # [fonts] # extras = [{ family = "Microsoft JhengHei" }] # # In case you want to specify any font feature: # [fonts] # features = ["ss02", "ss03", "ss05", "ss19"] # # Note: Font features do not have support to live reload on configuration, # so to reflect your changes, you will need to close and reopen Rio. # # You can also map the specified Unicode codepoints to a particular font. # symbol-map = [ # { start = "2297", end = "2299", font-family = "Cascadia Code NF" } # ] [fonts] # You can also disable font hinting. Font hinting is enabled by default. # hinting = false size = 16.6 family = "Berkeley Mono" [fonts.emoji] family = "Noto Emoji" [fonts.regular] style = "Normal" [fonts.bold] style = "Normal" weight = 800 [fonts.italic] style = "Italic" [fonts.bold-italic] style = "Italic" weight = 800 # [hints] # # Characters used for hint labels # alphabet = "jfkdls;ahgurieowpq" # # # URL hint example # [[hints.rules]] # regex = "(https://|http://)[^\u{0000}-\u{001F}\u{007F}-\u{009F}<>\"\\s{-}\\^⟨⟩`\\\\]+" # hyperlinks = true # post-processing = true # persist = false # # [hints.rules.action] # command = "xdg-open" # Linux/BSD # # command = "open" # macOS # # command = { program = "cmd", args = ["/c", "start", ""] } # Windows # # [hints.rules.binding] # key = "O" # mods = ["Control", "Shift"] # Scroll # # You can change how many lines are scrolled each time by setting this option. # # Scroll calculation for canonical mode will be based on `lines = (accumulated scroll * multiplier / divider)`, # If you want a quicker scroll, keep increasing the multiplier. # If you want to reduce scroll speed you will need to increase the divider. # You can use both properties also to find the best scroll for you. # # Multiplier default is 3.0. # Divider default is 1.0. # Example: # [scroll] # multiplier = 3.0 # divider = 1.0 # Navigation # # "mode" - Define navigation mode # • NativeTab (MacOS only) # • Bookmark # • BottomTab # • TopTab # • Plain # # "hide-if-single" - Hide navigation UI if is single. # "clickable" - Enable click on tabs to switch. # "use-current-path" - Use same path whenever a new tab is created (Note: requires `use-fork` to be set to false). # "color-automation" - Set a specific color for the tab whenever a specific program is running, or in a specific directory. # # Example: # [navigation] # mode = "bookmark" # clickable = false # hide-if-single = true # use-current-path = false # color-automation = [] # Shell # # You can set `shell.program` to the path of your favorite shell, e.g. `/bin/fish`. # Entries in `shell.args` are passed unmodified as arguments to the shell. # # Default: # - (macOS) user login shell # - (Linux/BSD) user login shell # - (Windows) powershell # # Example 1 using fish shell from bin path: # # [shell] # program = "/bin/fish" # args = ["--login"] # # Example 2 for Windows using powershell # # [shell] # program = "pwsh" # args = [] # # Example 3 for Windows using powershell with login # # [shell] # program = "pwsh" # args = ["-l"] # # Example 4 for MacOS with tmux installed by homebrew # # [shell] # program = "/opt/homebrew/bin/tmux" # args = ["new-session", "-c", "/var/www"] # Colors # # Colors definition will overwrite any property in theme # (considering if theme folder does exists and is being used) # # Example: # [colors] # background = '#0F0D0E' # foreground = '#F9F4DA' # cursor = '#F38BA3' # tabs = '#443d40' # tabs-active = '#F38BA3' # green = '#0BA95B' # red = '#ED203D' # blue = '#12B5E5' # yellow = '#FCBA28' # Bindings # # Create custom Key bindings for Rio terminal # More information in: https://raphamorim.io/rio/docs/key-bindings # # Example: # [bindings] # keys = [ # { key = "q", with = "super", action = "Quit" }, # # Bytes[27, 91, 53, 126] is equivalent to "\x1b[5~" # { key = "home", with = "super | shift", bytes = [27, 91, 53, 126] } # ] # Platform # # Rio now allows you to have different configurations per OS # You can write ovewrite properties like `Shell`, `Navigation` # and `Window`. # # Example: # [shell] # # default (in this case will be used only on MacOS) # program = "/bin/fish" # args = ["--login"] # # [platform] # # Microsoft Windows overwrite # windows.shell.program = "pwsh" # windows.shell.args = ["-l"] # # # Linux overwrite # linux.shell.program = "tmux" # linux.shell.args = ["new-session", "-c", "/var/www"] # Log level # # This property enables log level filter and file. The default level is "OFF" and the logs are not logged to a file as default. # # Example: # [developer] # log-level = "OFF" # enable-log-file = false ================================================ FILE: home-manager/modules/rio/config/rio/themes/pinpox-dark.toml ================================================ [colors] background = '#24273a' black = '#24273a' blue = '#8aadf4' cursor = '#e8f0ff' cyan = '#8bd5ca' foreground = '#e8f0ff' green = '#a6da95' magenta = '#cba6f7' red = '#ed8796' tabs = '#363a4f' tabs-active = '#494d64' white = '#e8f0ff' yellow = '#eed49f' dim-black = '#1e2030' dim-blue = '#7891d0' dim-cyan = '#73b9a8' dim-foreground = '#b0b7d0' dim-green = '#8cb77d' dim-magenta = '#ad92d5' dim-red = '#c9707d' dim-white = '#b0b7d0' dim-yellow = '#d1bc87' light-black = '#5b6078' light-blue = '#74c7ec' light-cyan = '#aee2da' light-foreground = '#747c9e' light-green = '#68f288' light-magenta = '#f5bde6' light-red = '#ff5370' light-white = '#747c9e' light-yellow = '#fab387' ================================================ FILE: home-manager/modules/rio/config/rio/themes/pinpox-light.toml ================================================ [colors] background = '#e8f0ff' black = '#e8f0ff' blue = '#1e66f5' cursor = '#24273a' cyan = '#179299' foreground = '#24273a' green = '#40a02b' magenta = '#8839ef' red = '#d20f39' tabs = '#e1e4e8' tabs-active = '#ccd0da' white = '#24273a' yellow = '#df8e1d' dim-black = '#dce0e8' dim-blue = '#1752c9' dim-cyan = '#127680' dim-foreground = '#1c1f2e' dim-green = '#357f23' dim-magenta = '#722fc3' dim-red = '#a90c2e' dim-white = '#1c1f2e' dim-yellow = '#b57418' light-black = '#5b6078' light-blue = '#267ff5' light-cyan = '#1da6ad' light-foreground = '#2c2f3d' light-green = '#4db332' light-magenta = '#9c46f5' light-red = '#e42144' light-white = '#363a4f' light-yellow = '#f29d23' ================================================ FILE: home-manager/modules/rio/config/rio/themes/wildcharm-dark.toml ================================================ [colors] background = '#000000' black = '#000000' blue = '#0087d7' cursor = '#ffffff' cyan = '#00afaf' foreground = '#d0d0d0' green = '#00af5f' magenta = '#d787d7' red = '#d7005f' tabs = '#1c1c1c' tabs-active = '#303030' white = '#d0d0d0' yellow = '#d78700' dim-black = '#262626' dim-blue = '#005faf' dim-cyan = '#008787' dim-foreground = '#8a8a8a' dim-green = '#008700' dim-magenta = '#875f87' dim-red = '#af005f' dim-white = '#8a8a8a' dim-yellow = '#af8700' light-black = '#767676' light-blue = '#00afff' light-cyan = '#00d7d7' light-foreground = '#ffffff' light-green = '#00d75f' light-magenta = '#ff87ff' light-red = '#ff5f87' light-white = '#ffffff' light-yellow = '#ffaf00' ================================================ FILE: home-manager/modules/rio/config/rio/themes/wildcharm-light.toml ================================================ [colors] background = '#ffffff' black = '#000000' blue = '#005faf' cursor = '#000000' cyan = '#008787' foreground = '#000000' green = '#008700' magenta = '#870087' red = '#af0000' tabs = '#e4e4e4' tabs-active = '#d0d0d0' white = '#ffffff' yellow = '#af5f00' dim-black = '#262626' dim-blue = '#00305f' dim-cyan = '#005f5f' dim-foreground = '#585858' dim-green = '#005f00' dim-magenta = '#5f005f' dim-red = '#870000' dim-white = '#d0d0d0' dim-yellow = '#875f00' light-black = '#585858' light-blue = '#0087d7' light-cyan = '#00afaf' light-foreground = '#262626' light-green = '#00af5f' light-magenta = '#af00af' light-red = '#d70000' light-white = '#ffffff' light-yellow = '#d78700' ================================================ FILE: home-manager/modules/rio/default.nix ================================================ { lib, pkgs, config, ... }: with lib; let cfg = config.pinpox.programs.rio; in { options.pinpox.programs.rio.enable = mkEnableOption "rio terminal emulator"; config = mkIf cfg.enable { home.packages = with pkgs; [ rio ]; xdg.enable = true; xdg.configFile.rio.source = ./config/rio; }; } ================================================ FILE: home-manager/modules/river/default.nix ================================================ { config, pkgs, lib, ... }: with lib; let cfg = config.pinpox.programs.river; start-river = pkgs.writeShellScriptBin "start-river" # sh '' export WLR_DRM_NO_MODIFIERS=1 dbus-launch --sh-syntax --exit-with-session ${pkgs.river-classic}/bin/river ''; screenshot-region = pkgs.writeShellScriptBin "screenshot-region" # sh '' ${pkgs.slurp}/bin/slurp | ${pkgs.grim}/bin/grim -g - - | ${pkgs.wl-clipboard}/bin/wl-copy -t image/png ''; screenshot-region-file = pkgs.writeShellScriptBin "screenshot-region-file" # sh '' ${pkgs.grim}/bin/grim -g "$(${pkgs.slurp}/bin/slurp)" $(date +'%s_grim.png') ''; in { options.pinpox.programs.river.enable = mkEnableOption "river window manager"; config = mkIf cfg.enable { # Sets --indicator for network-manager-applet, which makes it work in river xsession.preferStatusNotifierItems = true; # Install these packages for my user home.packages = with pkgs; [ river-classic river-luatile # way-displays waybar wl-clipboard wlr-randr start-river screenshot-region screenshot-region-satty screenshot-region-file ]; xdg = { enable = true; configFile = { # River configuration files river-config = { target = "river/init"; source = ./river-config; executable = true; }; river-config-extra = { target = "river/init_exta"; text = # sh '' riverctl map-switch normal lid close spawn ${pkgs.swaylock}/bin/swaylock # riverctl map normal Super F12 spawn '${pkgs.slurp}/bin/slurp | ${pkgs.grim}/bin/grim -g - - | ${pkgs.wl-clipboard}/bin/wl-copy -t image/png' # riverctl map normal Super F12 spawn ${screenshot-region} riverctl map normal Super p spawn "${pkgs.tofi}/bin/tofi-run" ${pkgs.mako}/bin/mako & waybar # ${pkgs.wlr-randr}/bin/wlr-randr --output eDP-1 --mode 1920x1080 --pos 0,0 \ # --output DP-1 --mode 2560x1440 --pos 4480,0 \ # --output DP-2 --mode 2560x1440@164.54 --pos 1920,0 # wlr-randr --output eDP-1 --on --mode 1920x1080 --pos 0,0 --output DP-1 --on --mode 2560x1440 --pos 4480,0 --output DP-2 --on --mode 2560x1440 --pos 1920,0 ''; executable = true; }; # river-luatile layouts luatile-layout = { target = "river-luatile/layout.lua"; source = ./layout.lua; }; luatile-json = { target = "river-luatile/json.lua"; source = ./json.lua; }; }; }; }; } ================================================ FILE: home-manager/modules/river/json.lua ================================================ -- -- json.lua -- -- Copyright (c) 2020 rxi -- -- Permission is hereby granted, free of charge, to any person obtaining a copy of -- this software and associated documentation files (the "Software"), to deal in -- the Software without restriction, including without limitation the rights to -- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -- of the Software, and to permit persons to whom the Software is furnished to do -- so, subject to the following conditions: -- -- The above copyright notice and this permission notice shall be included in all -- copies or substantial portions of the Software. -- -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -- SOFTWARE. -- local json = { _version = "0.1.2" } ------------------------------------------------------------------------------- -- Encode ------------------------------------------------------------------------------- local encode local escape_char_map = { [ "\\" ] = "\\", [ "\"" ] = "\"", [ "\b" ] = "b", [ "\f" ] = "f", [ "\n" ] = "n", [ "\r" ] = "r", [ "\t" ] = "t", } local escape_char_map_inv = { [ "/" ] = "/" } for k, v in pairs(escape_char_map) do escape_char_map_inv[v] = k end local function escape_char(c) return "\\" .. (escape_char_map[c] or string.format("u%04x", c:byte())) end local function encode_nil(val) return "null" end local function encode_table(val, stack) local res = {} stack = stack or {} -- Circular reference? if stack[val] then error("circular reference") end stack[val] = true if rawget(val, 1) ~= nil or next(val) == nil then -- Treat as array -- check keys are valid and it is not sparse local n = 0 for k in pairs(val) do if type(k) ~= "number" then error("invalid table: mixed or invalid key types") end n = n + 1 end if n ~= #val then error("invalid table: sparse array") end -- Encode for i, v in ipairs(val) do table.insert(res, encode(v, stack)) end stack[val] = nil return "[" .. table.concat(res, ",") .. "]" else -- Treat as an object for k, v in pairs(val) do if type(k) ~= "string" then error("invalid table: mixed or invalid key types") end table.insert(res, encode(k, stack) .. ":" .. encode(v, stack)) end stack[val] = nil return "{" .. table.concat(res, ",") .. "}" end end local function encode_string(val) return '"' .. val:gsub('[%z\1-\31\\"]', escape_char) .. '"' end local function encode_number(val) -- Check for NaN, -inf and inf if val ~= val or val <= -math.huge or val >= math.huge then error("unexpected number value '" .. tostring(val) .. "'") end return string.format("%.14g", val) end local type_func_map = { [ "nil" ] = encode_nil, [ "table" ] = encode_table, [ "string" ] = encode_string, [ "number" ] = encode_number, [ "boolean" ] = tostring, } encode = function(val, stack) local t = type(val) local f = type_func_map[t] if f then return f(val, stack) end error("unexpected type '" .. t .. "'") end function json.encode(val) return ( encode(val) ) end ------------------------------------------------------------------------------- -- Decode ------------------------------------------------------------------------------- local parse local function create_set(...) local res = {} for i = 1, select("#", ...) do res[ select(i, ...) ] = true end return res end local space_chars = create_set(" ", "\t", "\r", "\n") local delim_chars = create_set(" ", "\t", "\r", "\n", "]", "}", ",") local escape_chars = create_set("\\", "/", '"', "b", "f", "n", "r", "t", "u") local literals = create_set("true", "false", "null") local literal_map = { [ "true" ] = true, [ "false" ] = false, [ "null" ] = nil, } local function next_char(str, idx, set, negate) for i = idx, #str do if set[str:sub(i, i)] ~= negate then return i end end return #str + 1 end local function decode_error(str, idx, msg) local line_count = 1 local col_count = 1 for i = 1, idx - 1 do col_count = col_count + 1 if str:sub(i, i) == "\n" then line_count = line_count + 1 col_count = 1 end end error( string.format("%s at line %d col %d", msg, line_count, col_count) ) end local function codepoint_to_utf8(n) -- http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=iws-appendixa local f = math.floor if n <= 0x7f then return string.char(n) elseif n <= 0x7ff then return string.char(f(n / 64) + 192, n % 64 + 128) elseif n <= 0xffff then return string.char(f(n / 4096) + 224, f(n % 4096 / 64) + 128, n % 64 + 128) elseif n <= 0x10ffff then return string.char(f(n / 262144) + 240, f(n % 262144 / 4096) + 128, f(n % 4096 / 64) + 128, n % 64 + 128) end error( string.format("invalid unicode codepoint '%x'", n) ) end local function parse_unicode_escape(s) local n1 = tonumber( s:sub(1, 4), 16 ) local n2 = tonumber( s:sub(7, 10), 16 ) -- Surrogate pair? if n2 then return codepoint_to_utf8((n1 - 0xd800) * 0x400 + (n2 - 0xdc00) + 0x10000) else return codepoint_to_utf8(n1) end end local function parse_string(str, i) local res = "" local j = i + 1 local k = j while j <= #str do local x = str:byte(j) if x < 32 then decode_error(str, j, "control character in string") elseif x == 92 then -- `\`: Escape res = res .. str:sub(k, j - 1) j = j + 1 local c = str:sub(j, j) if c == "u" then local hex = str:match("^[dD][89aAbB]%x%x\\u%x%x%x%x", j + 1) or str:match("^%x%x%x%x", j + 1) or decode_error(str, j - 1, "invalid unicode escape in string") res = res .. parse_unicode_escape(hex) j = j + #hex else if not escape_chars[c] then decode_error(str, j - 1, "invalid escape char '" .. c .. "' in string") end res = res .. escape_char_map_inv[c] end k = j + 1 elseif x == 34 then -- `"`: End of string res = res .. str:sub(k, j - 1) return res, j + 1 end j = j + 1 end decode_error(str, i, "expected closing quote for string") end local function parse_number(str, i) local x = next_char(str, i, delim_chars) local s = str:sub(i, x - 1) local n = tonumber(s) if not n then decode_error(str, i, "invalid number '" .. s .. "'") end return n, x end local function parse_literal(str, i) local x = next_char(str, i, delim_chars) local word = str:sub(i, x - 1) if not literals[word] then decode_error(str, i, "invalid literal '" .. word .. "'") end return literal_map[word], x end local function parse_array(str, i) local res = {} local n = 1 i = i + 1 while 1 do local x i = next_char(str, i, space_chars, true) -- Empty / end of array? if str:sub(i, i) == "]" then i = i + 1 break end -- Read token x, i = parse(str, i) res[n] = x n = n + 1 -- Next token i = next_char(str, i, space_chars, true) local chr = str:sub(i, i) i = i + 1 if chr == "]" then break end if chr ~= "," then decode_error(str, i, "expected ']' or ','") end end return res, i end local function parse_object(str, i) local res = {} i = i + 1 while 1 do local key, val i = next_char(str, i, space_chars, true) -- Empty / end of object? if str:sub(i, i) == "}" then i = i + 1 break end -- Read key if str:sub(i, i) ~= '"' then decode_error(str, i, "expected string for key") end key, i = parse(str, i) -- Read ':' delimiter i = next_char(str, i, space_chars, true) if str:sub(i, i) ~= ":" then decode_error(str, i, "expected ':' after key") end i = next_char(str, i + 1, space_chars, true) -- Read value val, i = parse(str, i) -- Set res[key] = val -- Next token i = next_char(str, i, space_chars, true) local chr = str:sub(i, i) i = i + 1 if chr == "}" then break end if chr ~= "," then decode_error(str, i, "expected '}' or ','") end end return res, i end local char_func_map = { [ '"' ] = parse_string, [ "0" ] = parse_number, [ "1" ] = parse_number, [ "2" ] = parse_number, [ "3" ] = parse_number, [ "4" ] = parse_number, [ "5" ] = parse_number, [ "6" ] = parse_number, [ "7" ] = parse_number, [ "8" ] = parse_number, [ "9" ] = parse_number, [ "-" ] = parse_number, [ "t" ] = parse_literal, [ "f" ] = parse_literal, [ "n" ] = parse_literal, [ "[" ] = parse_array, [ "{" ] = parse_object, } parse = function(str, idx) local chr = str:sub(idx, idx) local f = char_func_map[chr] if f then return f(str, idx) end decode_error(str, idx, "unexpected character '" .. chr .. "'") end function json.decode(str) if type(str) ~= "string" then error("expected argument of type string, got " .. type(str)) end local res, idx = parse(str, next_char(str, 1, space_chars, true)) idx = next_char(str, idx, space_chars, true) if idx <= #str then decode_error(str, idx, "trailing garbage") end return res end return json ================================================ FILE: home-manager/modules/river/layout.lua ================================================ package.path = package.path .. ";/home/pinpox/.config/river-luatile/?.lua" json = require "json" print("river-luatile started!") -- You can define your global state here main_ratio = 0.65 gaps = 10 smart_gaps = false current_layout = "rivertile" output_layouts = { } -- The most important function - the actual layout generator -- -- The argument is a table with: -- * Focused tags -- * Window count -- * Output width -- * Output height -- -- The return value must be a table with exactly `count` entries. Each entry is a table with four -- numbers: -- * X coordinate -- * Y coordinate -- * Window width -- * Window height argumunts = {} function handle_metadata(args) return { name = output_layouts[args.output] } end -- We choose from one of the existing layouts defined in the table further down function handle_layout(args) -- print(args.output) arguments = args -- Default to rivertile as layout for all outputs if output_layouts[args.output] == nil then output_layouts[args.output] = "rivertile" end return layouts[output_layouts[args.output]](args) end -- This example is a simplified version of `rivertile` function handle_layout_rivertile(args) print("handle_layout() reached!") -- print(json.encode(args)) local retval = {} if args.count == 1 then if smart_gaps then table.insert(retval, { 0, 0, args.width, args.height }) else table.insert(retval, { gaps, gaps, args.width - gaps * 2, args.height - gaps * 2 }) end elseif args.count > 1 then local main_w = (args.width - gaps * 3) * main_ratio local side_w = (args.width - gaps * 3) - main_w local main_h = args.height - gaps * 2 local side_h = (args.height - gaps) / (args.count - 1) - gaps table.insert(retval, { gaps, gaps, main_w, main_h, }) for i = 0, (args.count - 2) do table.insert(retval, { main_w + gaps * 2, gaps + i * (side_h + gaps), side_w, side_h, }) end end -- print(json.encode(retval)) return retval end -- Monocle layout: Show just one big window, but not fullscreen function handle_layout_monocle(args) local retval = {} offset = 20 gap = 5 for i = 0, (args.count -1) do table.insert(retval, { gap + i*offset, gap + i*offset, (args.width - gap *2) - (args.count -1) * offset, (args.height - gap *2) - (args.count -1) * offset, }) end return retval end -- IMPORTANT: User commands send via `riverctl send-layout-cmd` are treated as lua code. -- Active tags are stored in `CMD_TAGS` global variable. -- Here is an example of a function that can be mapped to some key -- Run with `riverctl send-layout-cmd luatile "toggle_gaps()"` local gaps_alt = 0 function toggle_gaps() print("toggle_gaps() reached!") local tmp = gaps gaps = gaps_alt gaps_alt = tmp end -- Change output to a specific layout function layout_switch(layout_name) if layouts[layout_name] ~= nil then current_layout = layout_name end end -- Cycle layout of an output function layout_cycle() print("CYCLING LAYOUTS") current_layout = output_layouts[CMD_OUTPUT] -- TODO this could be nicer with a cycle if current_layout == "rivertile" then current_layout = "monocle" elseif current_layout == "monocle" then current_layout = "rivertile" end output_layouts[CMD_OUTPUT] = current_layout end -- Add all layouts here that should be supported layouts = { rivertile = handle_layout_rivertile, monocle = handle_layout_monocle, -- grid = handle_layout_grid, } ================================================ FILE: home-manager/modules/river/river-config ================================================ #!/usr/bin/env bash # This is the example configuration file for river. # # If you wish to edit this, you will probably want to copy it to # $XDG_CONFIG_HOME/river/init or $HOME/.config/river/init first. # # See the river(1), riverctl(1), and rivertile(1) man pages for complete # documentation. # Set keyboard layout # riverctl keyboard-layout -variant colemak us riverctl keyboard-layout -variant colemak -options "caps:swapescape" us # Super+Return to start an instance of foot (https://codeberg.org/dnkl/foot) riverctl map normal Super Return spawn foot # Super+Shif+Q to close the focused view riverctl map normal Super+Shift Q close # Super+Shift+E to exit river riverctl map normal Super+Shift E exit # Focus should follow the mouse riverctl focus-follows-cursor normal # riverctl focus-follows-cursor disabled # Super+Tab and Super+Shift+Tab to focus the next/previous view in the layout # stack riverctl map normal Super Tab focus-view next riverctl map normal Super+Shift Tab focus-view previous # TODO focus-view has not implemeted left/down/right/left (yet) # riverctl map normal Super H focus-view left # riverctl map normal Super J focus-view down # riverctl map normal Super K focus-view up # riverctl map normal Super L focus-view right # Super+Shift+J and Super+Shift+K to swap the focused view with the next/previous # view in the layout stack riverctl map normal Super+Shift J swap next riverctl map normal Super+Shift K swap previous # Super+H and Super+L to decrease/increase the main ratio of rivertile(1) riverctl map normal Super+Shift H send-layout-cmd rivertile "main-ratio -0.05" riverctl map normal Super+Shift L send-layout-cmd rivertile "main-ratio +0.05" # Super+Period and Super+Comma to focus the next/previous output riverctl map normal Super Period focus-output right riverctl map normal Super Comma focus-output left # Super+Shift+{Period,Comma} to send the focused view to the next/previous output # TODO use -current-tags once PR has reached release # https://github.com/riverwm/river/commit/b3698150708bec276454c0ff7c707d9dab446b1e # riverctl map normal Super+Shift Period send-to-output -current-tags right # riverctl map normal Super+Shift Comma send-to-output -current-tags left riverctl map normal Super+Shift Period send-to-output right riverctl map normal Super+Shift Comma send-to-output left # Super+Return to bump the focused view to the top of the layout stack riverctl map normal Super+Shift Return zoom # Super+Shift++ and Super+Shift+- to increment/decrement the main count of rivertile(1) riverctl map normal Super+Shift + send-layout-cmd rivertile "main-count +1" riverctl map normal Super+Shift - send-layout-cmd rivertile "main-count -1" # Super+Alt+{H,J,K,L} to move views riverctl map normal Super+Alt H move left 100 riverctl map normal Super+Alt J move down 100 riverctl map normal Super+Alt K move up 100 riverctl map normal Super+Alt L move right 100 # Super+Alt+Control+{H,J,K,L} to snap views to screen edges riverctl map normal Super+Alt+Control H snap left riverctl map normal Super+Alt+Control J snap down riverctl map normal Super+Alt+Control K snap up riverctl map normal Super+Alt+Control L snap right # Super+Alt+Shift+{H,J,K,L} to resize views riverctl map normal Super+Alt+Shift H resize horizontal -100 riverctl map normal Super+Alt+Shift J resize vertical 100 riverctl map normal Super+Alt+Shift K resize vertical -100 riverctl map normal Super+Alt+Shift L resize horizontal 100 # Super + Left Mouse Button to move views riverctl map-pointer normal Super BTN_LEFT move-view # Super + Right Mouse Button to resize views riverctl map-pointer normal Super BTN_RIGHT resize-view # Super + Middle Mouse Button to toggle float riverctl map-pointer normal Super BTN_MIDDLE toggle-float for i in $(seq 1 9) do tags=$((1 << ($i - 1))) # Super+[1-9] to focus tag [0-8] riverctl map normal Super $i set-focused-tags $tags # Super+Shift+[1-9] to tag focused view with tag [0-8] riverctl map normal Super+Shift $i set-view-tags $tags # Super+Control+[1-9] to toggle focus of tag [0-8] riverctl map normal Super+Control $i toggle-focused-tags $tags # Super+Shift+Control+[1-9] to toggle tag [0-8] of focused view riverctl map normal Super+Shift+Control $i toggle-view-tags $tags done # Super+0 to focus all tags # Super+Shift+0 to tag focused view with all tags all_tags=$(((1 << 32) - 1)) riverctl map normal Super 0 set-focused-tags $all_tags riverctl map normal Super+Shift 0 set-view-tags $all_tags # Super+Shift+Space to toggle float riverctl map normal Super+Shift Space toggle-float # Super+Space to cycle layouts riverctl map normal Super Space send-layout-cmd luatile 'layout_cycle()' # Super+F to toggle fullscreen riverctl map normal Super F toggle-fullscreen # Super+{Up,Right,Down,Left} to change layout orientation riverctl map normal Super Up send-layout-cmd rivertile "main-location top" riverctl map normal Super Right send-layout-cmd rivertile "main-location right" riverctl map normal Super Down send-layout-cmd rivertile "main-location bottom" riverctl map normal Super Left send-layout-cmd rivertile "main-location left" # Declare a passthrough mode. This mode has only a single mapping to return to # normal mode. This makes it useful for testing a nested wayland compositor riverctl declare-mode passthrough # Super+F11 to enter passthrough mode riverctl map normal Super F11 enter-mode passthrough # Super+F11 to return to normal mode riverctl map passthrough Super F11 enter-mode normal # Various media key mapping examples for both normal and locked mode which do # not have a modifier for mode in normal locked do # Eject the optical drive (well if you still have one that is) # riverctl map $mode None XF86Eject spawn 'eject -T' # Control pulse audio volume with pamixer (https://github.com/cdemoulins/pamixer) riverctl map $mode None XF86AudioRaiseVolume spawn 'pamixer -i 5' riverctl map $mode None XF86AudioLowerVolume spawn 'pamixer -d 5' riverctl map $mode None XF86AudioMute spawn 'pamixer --toggle-mute' riverctl map $mode Super F12 spawn 'amixer set Capture toggle' # Mute microphone # Control MPRIS aware media players with playerctl (https://github.com/altdesktop/playerctl) riverctl map $mode None XF86AudioMedia spawn 'playerctl play-pause' riverctl map $mode None XF86AudioPlay spawn 'playerctl play-pause' riverctl map $mode None XF86AudioPrev spawn 'playerctl previous' riverctl map $mode None XF86AudioNext spawn 'playerctl next' done # Set background and border color riverctl background-color 0x #002b36 riverctl border-color-focused 0x418fdd riverctl border-color-unfocused 0x586e75 # Set keyboard repeat rate riverctl set-repeat 50 300 # Warp cursor when changing focus with keyboard riverctl set-cursor-warp on-output-change # Use lswt to get IDs of windows # Make all views with an app-id that starts with "float" and title "foo" start floating. riverctl rule-add float -app-id 'float*' -title 'foo' # Float firefox screenshare indicator riverctl rule-add float -title 'Firefox — Sharing Indicator' # Make all views with app-id "bar" and any title use client-side decorations riverctl rule-add csd -app-id "bar" # Make specific applications use server-side decorations riverctl rule-add ssd -app-id firefox riverctl rule-add ssd -app-id pavucontrol riverctl rule-add ssd -app-id thunderbird # Needed to make xdg-desktop-portal-wlr work (screensharing) dbus-update-activation-environment --systemd WAYLAND_DISPLAY WAYLAND_DESKTOP # Set the default layout generator to be river-luatile and start it. # River will send the process group of the init executable SIGTERM on exit. riverctl default-layout luatile # riverctl default-layout rivertile # rivertile -view-padding 6 -outer-padding 6 & river-luatile & # Read nixos-generated config part /home/pinpox/.config/river/init_exta ================================================ FILE: home-manager/modules/shell/default.nix ================================================ { config, lib, ... }: with lib; let cfg = config.pinpox.defaults.shell; colors = config.pinpox.colors; in { options.pinpox.defaults.shell = { enable = mkEnableOption "shell defaults"; abbrev-aliases = mkOption { type = with types; listOf (submodule { options = { alias = mkOption { type = str; }; command = mkOption { type = str; }; global = mkOption { type = bool; default = false; description = "Expand alias everywhere, not only at the beginning of a line."; }; recursive = mkOption { type = bool; default = false; description = "Expand aliases recursively"; }; eval = mkOption { type = bool; default = false; description = "Evaluate subshells on expansion"; }; }; }); example = [ { alias = "nfu"; command = "nix flake update --commit-lock-file"; } { global = true; alias = "G"; command = "| rg -i"; } ]; description = '' Aliases for abbrev-allias ZSH plugin https://github.com/momo-lab/zsh-abbrev-alias ''; }; }; imports = [ ./starship.nix ./zsh.nix # ./fish.nix ]; config = mkIf cfg.enable { programs.direnv = { enable = true; enableZshIntegration = true; nix-direnv.enable = true; # https://direnv.net/man/direnv.toml.1.html # config = {}; }; pinpox.defaults.shell.abbrev-aliases = [ # Aliases expanded only at beginning of lines { alias = "flakeinit"; command = "nix flake init -t github:pinpox/nixos"; } { alias = "g"; command = "git"; } { alias = "o"; command = "xdg-open"; } { alias = "q"; command = "exit"; } { alias = "snvim"; command = "sudo -E nvim"; } { alias = "v"; command = "nvim"; } { alias = "nfu"; command = "nix flake update --commit-lock-file"; } # { # alias = "yt-dlp-mp4"; # command = "nix run nixpkgs#yt-dlp -- -S res,ext:mp4:m4a --recode mp4 "; # } { alias = "rm-orig"; command = ''find . -name "*.orig" -type f -delete''; } # Global aliases, get expanded everywhere { global = true; alias = "G"; command = "| rg -i"; } { global = true; alias = "P"; command = "| paste"; } ]; programs.fzf = { enable = true; enableZshIntegration = true; defaultOptions = [ "--height 40%" "--layout=reverse" "--border" "--inline-info" "--color 'fg:#${colors.White}'" # Text "--color 'bg:#${colors.Black}'" # Background "--color 'preview-fg:#${colors.White}'" # Preview window text "--color 'preview-bg:#${colors.Black}'" # Preview window background "--color 'hl:#${colors.Yellow}'" # Highlighted substrings "--color 'fg+:#${colors.Blue}'" # Text (current line) "--color 'bg+:#${colors.BrightBlack}'" # Background (current line) "--color 'gutter:#${colors.BrightBlack}'" # Gutter on the left (defaults to bg+) "--color 'hl+:#${colors.Magenta}'" # Highlighted substrings (current line) "--color 'info:#${colors.Magenta}'" # Info line (match counters) "--color 'border:#${colors.Blue}'" # Border around the window (--border and --preview) "--color 'prompt:#${colors.White}'" # Prompt "--color 'pointer:#${colors.Magenta}'" # Pointer to the current line "--color 'marker:#${colors.Magenta}'" # Multi-select marker "--color 'spinner:#${colors.Magenta}'" # Streaming input indicator "--color 'header:#${colors.White}'" # Header ]; }; programs.dircolors = { enable = true; enableZshIntegration = true; }; programs.pazi = { enable = true; enableZshIntegration = true; }; programs.htop = { enable = true; settings.tree_view = true; }; programs.jq.enable = true; programs.bat = { enable = true; # TODO: This should pick up the correct colors for the generated theme. Otherwise # it is possible to generate a custom bat theme to ~/.config/bat/config config = { theme = "base16"; }; }; }; } ================================================ FILE: home-manager/modules/shell/fish.nix ================================================ { pkgs, promterm, ... }: { programs = { fzf.enableFishIntegration = true; dircolors.enableFishIntegration = true; pazi.enableFishIntegration = true; }; programs.fish = { enable = true; functions = { gitignore = "curl -sL https://www.gitignore.io/api/$argv"; fish_command_not_found = "echo Did not find command $argv[1]"; # Create and change to a directory take = ''mkdir -p -- "$1" && cd -- "$1"''; # Create and change to a new temporary directory ttake = "cd $(mktemp -d)"; # Use `line 10 /etc/hosts` to get 10th line of file line = ''awk "NR == $1" "$2"''; }; plugins = [ { # https://github.com/gazorby/fifc name = "fifc"; src = pkgs.fetchFromGitHub { owner = "gazorby"; repo = "fifc"; rev = "a01650cd432becdc6e36feeff5e8d657bd7ee84a"; sha256 = "sha256-Ynb0Yd5EMoz7tXwqF8NNKqCGbzTZn/CwLsZRQXIAVp4="; }; } ]; shellAbbrs = { o = "xdg-open"; q = "exit"; snvim = "sudo -E nvim"; v = "nvim"; # Global aliases, get expanded everywhere # abbrev-alias -g G = "| rg -i" # abbrev-alias - g P="| tb" #TODO }; shellAliases = rec { # Eza ls replacement ls = "${pkgs.eza}/bin/eza --group-directories-first"; l = "${ls} -lbF --git --icons"; ll = "${l} -G"; la = "${ls} -lbhHigmuSa@ --time-style=long-iso --git --color-scale --icons"; lt = "${ls} --tree --level=2 --icons"; # Git gs = "${pkgs.git}/bin/git status"; # Pastebin (termbin.com) tb = "${pkgs.netcat-gnu}/bin/nc termbin.com 9999"; # Frequendly used folders cdn = "cd ~/code/github.com/pinpox/nixos"; cdnh = "cd ~/code/github.com/pinpox/nixos-home"; # Other pt = "${promterm.defaultPackage.x86_64-linux}/bin/promterm 'https://vpn.prometheus.pablo.tools/api/v1/alerts'"; lsblk = "lsblk -o name,mountpoint,label,size,type,uuid"; c = "${pkgs.bat}/bin/bat -n --decorations never"; cc = "${pkgs.clang}/bin/clang -Wall -Wextra -pedantic -std=c99 -Wshadow -Weverything"; qr = "${pkgs.qrencode}/bin/qrencode -t utf8 -o-"; top = "${pkgs.htop}/bin/htop"; weather = "${pkgs.curl}/bin/curl -4 http://wttr.in/Koeln"; #radio = "${ #pkgs.mpv}/bin/mpv http://lassul.us:8000/radio.ogg"; zzz = "systemctl suspend"; serve = "${pkgs.miniserve}/bin/miniserve"; za = "${./zellij-chooser}"; upterm = "${pkgs.upterm}/bin/upterm host --server ssh://upterm.thalheim.io:2323 --force-command 'zellij attach pair-programming' -- zellij attach --create pair-programming"; }; }; } ================================================ FILE: home-manager/modules/shell/starship.nix ================================================ { ... }: { programs.starship = { enable = false; enableBashIntegration = true; enableZshIntegration = true; settings = { character = { success_symbol = "[»](bold green)"; error_symbol = "[×](bold red) "; }; aws = { disabled = true; }; python = { disabled = true; }; nix_shell = { symbol = "❄ "; }; git_status = { ahead = "↑"; behind = "↓"; diverged = "↕"; modified = "!"; staged = "±"; renamed = "→"; }; directory = { truncate_to_repo = false; fish_style_pwd_dir_length = 2; substitutions = { "~/code/github.com/pinpox/nixos" = ""; }; }; }; }; } ================================================ FILE: home-manager/modules/shell/zellij-chooser ================================================ #!/usr/bin/env bash ZJ_SESSIONS=$(zellij list-sessions -n) NO_SESSIONS=$(echo "${ZJ_SESSIONS}" | wc -l) if [ "${NO_SESSIONS}" -ge 2 ]; then zellij attach "$(echo "${ZJ_SESSIONS}" | fzf | cut -d' ' -f1)" else zellij attach -c fi ================================================ FILE: home-manager/modules/shell/zsh.nix ================================================ { flake-inputs, pkgs, promterm, lib, config, ... }: { programs.zsh = { enable = true; autosuggestion = { enable = true; highlight = "fg=8"; }; enableCompletion = true; autocd = true; dotDir = "${config.xdg.configHome}/zsh"; sessionVariables = { RPS1 = ""; # Disable the right side prompt that "walters" theme introduces ZDOTDIR = "/home/pinpox/.config/zsh"; # Override prezto's default LESS which includes -z-4 (negative scroll # window size), rejected by less >= 691 LESS = "-g -i -M -R -S -w -X -z4"; }; initContent = let abbrevs = lib.concatStrings ( map ( a: let opt = lib.strings.optionalString; in '' abbrev-alias ${opt a.global "-g "}${opt a.eval "-e "}${opt a.recursive "-r "}${a.alias}="${a.command}" '' ) config.pinpox.defaults.shell.abbrev-aliases ); functions = '' function "="() { printf "%s\n" "$@" | ${pkgs.bc}/bin/bc } function ai() { echo "$@" | $##{pkgs.shell-gpt}/bin/sgpt } function aip() { wl-paste | $##{pkgs.shell-gpt}/bin/sgpt } # Create a temporary, detached worktree of the current git repo. # Great for quick hot-fixes. twork () { local wtpath="$(mktemp -d)" git worktree add --detach $wtpath cd $wtpath } # Jump to a code project with FZF using Ctrl+j fzf_cd_widget() { local base="/home/pinpox/code" local rel full rel=$( find "$base" -maxdepth 3 -type d \ | grep -E '(/.*){6}' \ | sed "s|^$base/||" \ | fzf --preview "BASE=$base sh -c '${pkgs.eza}/bin/eza \ --group-directories-first --tree --level=2 --icons \"\$BASE/\$0\"' {}" ) || return full="$base/$rel" cd "$full" || return if [[ -n "$ZLE_NAME" ]]; then zle reset-prompt fi } zle -N fzf_cd_widget bindkey '^J' fzf_cd_widget ${lib.optionalString (pkgs.stdenv.hostPlatform.system == "x86_64-linux") '' # Power profile function using ryzenadj (x86_64 only) power-profile() { if [ $# -ne 4 ]; then echo "Usage: power-profile " echo "Example: power-profile PERFORMANCE 30000 35000 35000" return 1 fi local name=$1 local stapm=$2 # Sustained power limit local fast=$3 # Short burst power limit (instant peak response) local slow=$4 # Slow burst power limit (~5-10s) sudo ${lib.getExe pkgs.ryzenadj} --stapm-limit=$stapm --fast-limit=$fast --slow-limit=$slow && \ echo "Power profile set to: $name (''${stapm}mW/''${fast}mW/''${slow}mW)" } ''} ''; in lib.mkMerge [ (lib.mkOrder 550 ((builtins.readFile ./zshrc) + '' unsetopt nomatch '')) abbrevs (builtins.readFile ./zshrc-extra) (builtins.readFile ./zshrc-coffee) functions ]; history = { expireDuplicatesFirst = true; ignoreSpace = false; save = 15000; share = true; }; dirHashes = { # Allows addressing directorys by shortname, e.g. `cd ~notes` docs = "$HOME/Documents"; notes = "$HOME/Notes"; downloads = "$HOME/Downloads"; nix-config = "/home/pinpox/code/github.com/pinpox/nixos"; clan = "$HOME/code/git.clan.lol/clan/clan-core"; clan-infra = "$HOME/code/git.clan.lol/clan/clan-infra"; }; shellAliases = rec { remote-review = ''nixpkgs-review pr --build-args="--builders 'ssh://pinpox@build-box.nix-community.org'"''; # eza ls replacement ls = "${pkgs.eza}/bin/eza --group-directories-first"; l = "${ls} -lbF --git --icons"; ll = "${l} -G"; la = "${ls} -lbhHigmuSa@ --time-style=long-iso --git --color-scale --icons"; lt = "${ls} --tree --level=2 --icons"; nb = "nix build --no-link --print-out-paths -L"; ne = "nix eval --strict --json"; # Git gs = "${pkgs.git}/bin/git status"; # Pastebin (termbin.com) tb = "${pkgs.netcat-gnu}/bin/nc termbin.com 9999"; # Frequendly used folders cdn = "cd ~/code/github.com/pinpox/nixos"; cdnh = "cd ~/code/github.com/pinpox/nixos-home"; # Other lsblk = "lsblk -o name,mountpoint,label,size,type,uuid"; c = "${pkgs.bat}/bin/bat -n --decorations never"; cc = "${pkgs.clang}/bin/clang -Wall -Wextra -pedantic -std=c99 -Wshadow -Weverything"; qr = "${pkgs.qrencode}/bin/qrencode -t utf8 -o-"; top = "${pkgs.htop}/bin/htop"; weather = "${pkgs.curl}/bin/curl -4 http://wttr.in/Koeln"; # radio = "${ #pkgs.mpv}/bin/mpv http://lassul.us:8000/radio.ogg"; # ${pkgs.yubikey-manager}/bin/ykman oath accounts code | \ yotp = '' ${pkgs.fzf}/bin/fzf | awk '{print $2}' | ${pkgs.xclip}/bin/xclip -sel clip ''; zzz = "systemctl suspend"; picohsm-add-to-agent = "ssh-add -e ${pkgs.opensc}/lib/opensc-pkcs11.so 2>/dev/null; SSH_ASKPASS=${lib.getExe pkgs.noctalia-askpass} SSH_ASKPASS_REQUIRE=prefer ssh-add -s ${pkgs.opensc}/lib/opensc-pkcs11.so"; serve = "${pkgs.miniserve}/bin/miniserve"; za = "${./zellij-chooser}"; upterm = "${pkgs.upterm}/bin/upterm host --server ssh://upterm.thalheim.io:2323 --force-command 'zellij attach pair-programming' -- zellij attach --create pair-programming"; } // lib.optionalAttrs (pkgs.stdenv.hostPlatform.system == "x86_64-linux") { # x86_64-only aliases gif = "${flake-inputs.gif-searcher.packages.x86_64-linux.default}/bin/show-gif"; gifi = "${flake-inputs.gif-searcher.packages.x86_64-linux.gif-infinite}/bin/show-gif"; pt = "${promterm.defaultPackage.x86_64-linux}/bin/promterm 'https://vpn.prometheus.pablo.tools/api/v1/alerts'"; # Power profile aliases (requires ryzenadj) power-performance = "power-profile PERFORMANCE 30000 35000 35000"; power-balanced = "power-profile BALANCED 25000 33000 33000"; power-saver = "power-profile POWER-SAVER 26000 30000 15000"; power-ultra-saver = "power-profile ULTRA-POWER-SAVER 10000 20000 10000"; }; prezto = { enable = true; # prompt.theme = "pure"; # Case insensitive completion caseSensitive = true; # Autoconvert .... to ../.. editor.dotExpansion = true; # Prezto modules to load # pmodules = [ "utility" "editor" "directory" "completion"]; pmodules = [ "utility" "editor" "directory" # "prompt" ]; terminal.autoTitle = true; }; plugins = [ { name = "zsh-forgit"; src = pkgs.zsh-forgit; file = "share/zsh/zsh-forgit/forgit.plugin.zsh"; } { name = "fast-syntax-highlighting"; file = "fast-syntax-highlighting.plugin.zsh"; src = "${pkgs.zsh-fast-syntax-highlighting}/share/zsh/site-functions"; } { name = "zsh-nix-shell"; file = "nix-shell.plugin.zsh"; src = "${pkgs.zsh-nix-shell}/share/zsh-nix-shell"; } { name = "zsh-abbrev-alias"; file = "abbrev-alias.plugin.zsh"; src = "${pkgs.zsh-abbrev-alias}/share/zsh-abbrev-alias"; } { name = "zsh-colored-man-pages"; file = "colored-man-pages.plugin.zsh"; src = "${pkgs.zsh-colored-man-pages}/share/zsh-colored-man-pages"; } { name = "zsh-fzf-tab"; file = "fzf-tab.plugin.zsh"; src = "${pkgs.zsh-fzf-tab}/share/fzf-tab"; } { name = "zsh-async"; file = "async.zsh"; src = "${pkgs.zsh-async}/share/zsh-async"; } ]; }; } ================================================ FILE: home-manager/modules/shell/zshrc ================================================ # zsh-prompt.zsh - Nushell-style prompt for zsh # # Set HOST_COLOR before sourcing this file, e.g.: # HOST_COLOR="magenta" # HOST_COLOR="#ff79c6" # source /path/to/zsh-prompt.zsh # # Supports named colors (red, green, magenta, ...), 256-palette # numbers (0-255), and hex colors (#rrggbb). HOST_COLOR="${HOST_COLOR:-default}" zmodload zsh/datetime setopt PROMPT_SUBST # Short hostname locally, user@hostname over SSH if [[ -n "$SSH_CONNECTION" ]]; then _PROMPT_HOST="${USER}@${HOST}" else _PROMPT_HOST="${HOST%%.*}" fi # Derive HOST_COLOR from hostname hash if not explicitly set. # Maps hash bytes into the 20-180 range for readable white text on top. if [[ -z "$HOST_COLOR" || "$HOST_COLOR" == "default" ]]; then _hc_hash=$(printf '%s' "$HOST" | md5sum) _hc_r=$(( 16#${_hc_hash[1,2]} % 161 + 20 )) _hc_g=$(( 16#${_hc_hash[3,4]} % 161 + 20 )) _hc_b=$(( 16#${_hc_hash[5,6]} % 161 + 20 )) HOST_COLOR="$(printf '#%02x%02x%02x' $_hc_r $_hc_g $_hc_b)" unset _hc_r _hc_g _hc_b _hc_hash fi # Resolve HOST_COLOR into raw ANSI escape sequences so that hex # colors (#rrggbb) work alongside named/256 colors. if [[ "$HOST_COLOR" == "#"* ]]; then _hc_hex="${HOST_COLOR#\#}" _hc_r=$((16#${_hc_hex[1,2]})) _hc_g=$((16#${_hc_hex[3,4]})) _hc_b=$((16#${_hc_hex[5,6]})) _prompt_hc_fg=$'\e[38;2;'"${_hc_r};${_hc_g};${_hc_b}"'m' _prompt_hc_bg=$'\e[48;2;'"${_hc_r};${_hc_g};${_hc_b}"'m' else _prompt_hc_fg="$(print -Pn "%F{${HOST_COLOR}}")" _prompt_hc_bg="$(print -Pn "%K{${HOST_COLOR}}")" fi unset _hc_hex _hc_r _hc_g _hc_b # Vi mode indicator _vi_mode_indicator=" " function zle-keymap-select { case $KEYMAP in vicmd) _vi_mode_indicator="> " ;; viins|main) _vi_mode_indicator=" " ;; esac zle reset-prompt } zle -N zle-keymap-select function zle-line-init { _vi_mode_indicator=" " } zle -N zle-line-init # Command duration tracking _prompt_cmd_start=0 _prompt_preexec() { _prompt_cmd_start=$EPOCHREALTIME } _prompt_format_duration() { local -i ms=$1 if (( ms < 1000 )); then printf '%dms' $ms elif (( ms < 60000 )); then local -i sec=$(( ms / 1000 )) local -i rem=$(( ms % 1000 )) if (( rem > 0 )); then printf '%dsec %dms' $sec $rem else printf '%dsec' $sec fi elif (( ms < 3600000 )); then local -i min=$(( ms / 60000 )) local -i sec=$(( (ms % 60000) / 1000 )) if (( sec > 0 )); then printf '%dmin %dsec' $min $sec else printf '%dmin' $min fi else local -i hr=$(( ms / 3600000 )) local -i min=$(( (ms % 3600000) / 60000 )) if (( min > 0 )); then printf '%dhr %dmin' $hr $min else printf '%dhr' $hr fi fi } # Computed prompt segments (rebuilt each precmd) _prompt_segments="" _prompt_precmd() { local exit_code=$? # Duration local duration="" if (( _prompt_cmd_start > 0 )); then local -i elapsed_ms=$(( (EPOCHREALTIME - _prompt_cmd_start) * 1000 )) duration="$(_prompt_format_duration $elapsed_ms)" _prompt_cmd_start=0 fi # JJ branches (skip on gitbutler/* git branches) local jj_branches="" local git_branch git_branch=$(command git symbolic-ref --short HEAD 2>/dev/null) if [[ "$git_branch" != gitbutler/* ]]; then local jj_out jj_out=$(PAGER=cat command jj log -r 'trunk()..@ & bookmarks()' -T 'bookmarks.join("\n") ++ "\n"' --no-graph --ignore-working-copy 2>/dev/null) if [[ $? -eq 0 && -n "$jj_out" ]]; then jj_out="${jj_out//$'\n'/ }" jj_out="${jj_out%% }" jj_out="${jj_out## }" [[ -n "$jj_out" ]] && jj_branches="$jj_out" fi fi # Build segments # Path: bold underline, red for root if (( EUID == 0 )); then _prompt_segments="%U%B%F{red}%~%f%b%u" else _prompt_segments="%U%B%~%b%u" fi # Nix shell name [[ -n "$name" ]] && _prompt_segments+=" %F{red}${name}%f" # Duration (dark gray italic) [[ -n "$duration" ]] && \ _prompt_segments+=$' %F{240}%{\e[3m%}'"${duration}"$'%{\e[23m%}%f' # JJ branches (magenta) [[ -n "$jj_branches" ]] && \ _prompt_segments+=" %F{magenta}${jj_branches}%f" # Last exit code (red bold) (( exit_code != 0 )) && \ _prompt_segments+=" %F{red}%B×${exit_code}%b%f" # Background jobs (yellow) local njobs=${(Mw)#jobstates} (( njobs > 0 )) && \ _prompt_segments+=" %F{yellow}[${njobs}]%f" } precmd_functions+=(_prompt_precmd) preexec_functions+=(_prompt_preexec) PROMPT=$'\n''%{${_prompt_hc_fg}%}╭%{${_prompt_hc_bg}%}%F{white}%B ${_PROMPT_HOST} %b%k%f ${_prompt_segments}'$'\n''%{${_prompt_hc_fg}%}╰▶ %f${_vi_mode_indicator}' # Show nix-shell packages in right prompt if [[ -n "$IN_NIX_SHELL" ]]; then RPROMPT="%F{blue}${IN_NIX_SHELL} ${NIX_SHELL_PACKAGES}%f" else RPROMPT="" fi ================================================ FILE: home-manager/modules/shell/zshrc-coffee ================================================ function coffee { # Hide cursor tput civis # Restore cursor on exit trap 'tput cnorm; clear; return 0' EXIT INT TERM local frame1=' ) ( ( ) ) ) ( ( _______)_ .-'"'"'---------| ( C|/\/\/\/\/| '"'"'-./\/\/\/\/| '"'"'_________'"'"' '"'"'-------'"'"' pinpox went for coffee. brb. ' local frame2=' ( ) ) ( ( ( ) ) _______(_ .-'"'"'---------| ( C|/\/\/\/\/| '"'"'-./\/\/\/\/| '"'"'_________'"'"' '"'"'-------'"'"' pinpox went for coffee. brb. ' while true; do clear echo "$frame1" sleep 0.5 clear echo "$frame2" sleep 0.5 done } ================================================ FILE: home-manager/modules/shell/zshrc-extra ================================================ # Create and change to a directory take () { mkdir -p -- "$1" && cd -- "$1"; } # Create and change to a new temporary directory ttake () { cd $(mktemp -d) } # Use `line 10 /etc/hosts` to get 10th line of file line () { awk "NR == $1" "$2" } # Bind up and down keys to history matching partial input bindkey "$terminfo[kcuu1]" history-search-backward bindkey "$terminfo[kcud1]" history-search-forward # Init h (https://github.com/zimbatm/h) eval "$(h --setup ~/code)" # fzf-compete files with ctrl+t bindkey -s "^T" 'pazi_cd --pipe="fzf"^M' # Make tab-completion case-insensitive zstyle ':completion:*' matcher-list 'm:{a-z}={A-Za-z}' # fzf-tab: https://github.com/Aloxaf/fzf-tab?tab=readme-ov-file#configure # set list-colors to enable filename colorizing zstyle ':completion:*' list-colors ${(s.:.)LS_COLORS} # preview directory's content with eza when completing cd zstyle ':fzf-tab:complete:cd:*' fzf-preview 'eza -1 --color=always $realpath' # Wastebin # command | paste function paste() { jq -Rns '{text: inputs}' | curl -s -H 'Content-Type: application/json' \ --data-binary @- https://paste.0cx.de | jq -r '. | "https://paste.0cx.de\(.path)"' } # Self-hosted transfer.sh sharing. Expects ~/.netrc with crendentials in this format: # machine transfer.0cx.de login my-super-user password super-secret-password # transfer file.txt transfer () { if [ $# -eq 0 ] then echo "No arguments specified.\nUsage:\n transfer \n ... | transfer " >&2 return 1 fi if tty -s then file="$1" file_name=$(basename "$file") if [ ! -e "$file" ] then echo "$file: No such file or directory" >&2 return 1 fi if [ -d "$file" ] then file_name="$file_name.zip" , ( cd "$file" && zip -r -q - . ) | curl -n --progress-bar --upload-file "-" "https://transfer.0cx.de/$file_name" | tee /dev/null, else cat "$file" | curl -n --progress-bar --upload-file "-" "https://transfer.0cx.de/$file_name" | tee /dev/null fi else file_name=$1 curl -n --progress-bar --upload-file "-" "https://transfer.0cx.de/$file_name" | tee /dev/null fi } function delta_sidebyside { if [[ COLUMNS -ge 140 ]]; then export DELTA_FEATURES='side-by-side' else export DELTA_FEATURES='' fi } trap delta_sidebyside WINCH # Checkout a Gitea PR by number, setting up SSH remote and upstream tracking tea-pr() { local pr="$1" [[ -z "$pr" ]] && { echo "Usage: tea-pr "; return 1; } local head head=$(tea pr view "$pr" --output json --fields index,head \ | jq -r ".[] | select(.index==\"${pr}\") | .head") if [[ -z "$head" ]]; then echo "Error: PR #${pr} not found" return 1 fi local owner branch if [[ "$head" == *:* ]]; then owner="${head%%:*}" branch="${head#*:}" else owner="" branch="$head" fi if [[ -z "$owner" ]]; then git fetch origin "$branch" git checkout -b "pr-${pr}" "origin/${branch}" else local remote="pulls/${owner}" git remote add "$remote" "gitea@git.clan.lol:${owner}/clan-core.git" 2>/dev/null git fetch "$remote" "$branch" git checkout -b "pr-${pr}" "${remote}/${branch}" git branch --set-upstream-to="${remote}/${branch}" fi echo "PR #${pr}: on branch pr-${pr}" } autoload -z edit-command-line zle -N edit-command-line # Allow jumping between prompts in foot with Ctrl+Shift+x/z precmd() { print -Pn "\e]133;A\e\\" } ================================================ FILE: home-manager/modules/ssh/default.nix ================================================ { config, lib, ... }: with lib; let cfg = config.pinpox.programs.ssh; in { options.pinpox.programs.ssh.enable = mkEnableOption "SSH configuration"; config = mkIf cfg.enable { programs.ssh = { enable = true; enableDefaultConfig = false; matchBlocks = { "*" = { extraOptions = { ForwardAgent = "no"; ServerAliveInterval = "0"; ServerAliveCountMax = "3"; Compression = "no"; AddKeysToAgent = "yes"; HashKnownHosts = "no"; UserKnownHostsFile = "~/.ssh/known_hosts"; ControlMaster = "no"; ControlPath = "~/.ssh/master-%r@%n:%p"; ControlPersist = "no"; PKCS11Provider = "/run/current-system/sw/lib/opensc-pkcs11.so"; # CertificateFile = "~/.ssh/cert.pub"; CertificateFile = "${./ssh-key-cert.pub}"; }; }; "ap-oben" = { hostname = "UAP-ProOBEN.lan"; user = "pinpox"; extraOptions = { PubkeyAcceptedAlgorithms = "+ssh-rsa"; HostkeyAlgorithms = "+ssh-rsa"; }; }; "ap-mitte" = { hostname = "UAP-ProMITTE.lan"; user = "pinpox"; extraOptions = { PubkeyAcceptedAlgorithms = "+ssh-rsa"; HostkeyAlgorithms = "+ssh-rsa"; }; }; "ap-unten" = { hostname = "UAP-ProUNTEN.lan"; user = "pinpox"; extraOptions = { PubkeyAcceptedAlgorithms = "+ssh-rsa"; HostkeyAlgorithms = "+ssh-rsa"; }; }; }; }; }; } ================================================ FILE: home-manager/modules/ssh/ssh-key-cert.pub ================================================ ecdsa-sha2-nistp256-cert-v01@openssh.com AAAAKGVjZHNhLXNoYTItbmlzdHAyNTYtY2VydC12MDFAb3BlbnNzaC5jb20AAAAg+QuBDYto1/9245C9ZBOC3u0xdyrn4jJ8ZDIlhiH06N0AAAAIbmlzdHAyNTYAAABBBCdOfrnazSXp7ZmHcePXSd4leP3Qafr4fmDr3w+AxwRChSn1zzLPjV8CvD/PdMU7jQA0HS/1ItREurmZCKS/ZnQAAAAAAAAAAAAAAAEAAAAOcGlucG94QHBpY29oc20AAAASAAAABnBpbnBveAAAAARyb290AAAAAAAAAAD//////////wAAAAAAAACCAAAAFXBlcm1pdC1YMTEtZm9yd2FyZGluZwAAAAAAAAAXcGVybWl0LWFnZW50LWZvcndhcmRpbmcAAAAAAAAAFnBlcm1pdC1wb3J0LWZvcndhcmRpbmcAAAAAAAAACnBlcm1pdC1wdHkAAAAAAAAADnBlcm1pdC11c2VyLXJjAAAAAAAAAAAAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIAPcS8NMzwYLvKFOXeTZwX/W6ua0zIzs4zA0PW0xz62iAAAAUwAAAAtzc2gtZWQyNTUxOQAAAEDKiqLPktLn4KmCn5moBjxxLmEeAX18MIytub9xwVwqfWfTLXr1COR3UXQ5A1i4rZzaVS9G/G6LnTNLfU/2NJcH ssh-key ================================================ FILE: home-manager/modules/ssh/ssh-key.pub ================================================ ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBCdOfrnazSXp7ZmHcePXSd4leP3Qafr4fmDr3w+AxwRChSn1zzLPjV8CvD/PdMU7jQA0HS/1ItREurmZCKS/ZnQ= ssh-key ================================================ FILE: home-manager/modules/sway/default.nix ================================================ { config, pkgs, lib, wl-harmonograph, ... }: with lib; let cfg = config.pinpox.programs.sway; start-sway = pkgs.writeShellScriptBin "start-sway" # sh '' export WLR_DRM_NO_MODIFIERS=1 # dbus-launch --sh-syntax --exit-with-session ${pkgs.sway}/bin/sway ${pkgs.sway}/bin/sway --unsupported-gpu ''; # Wrapper that ensures only one noctalia-shell instance is running, so we can # use exec_always and pick up new versions on update start-noctalia = pkgs.writeShellApplication { name = "start-noctalia"; runtimeInputs = [ pkgs.procps pkgs.noctalia-shell pkgs.himalaya pkgs.python3 ]; text = '' # Kill any running instance (matches the quickshell process loading # noctalia-shell's shell.qml). Ignore failure if none is running. pkill -f 'quickshell.*noctalia-shell' || true # Wait for the old process(es) to actually exit so the new instance # can claim the IPC socket cleanly. for _ in $(seq 1 50); do pgrep -f 'quickshell.*noctalia-shell' >/dev/null || break sleep 0.1 done exec noctalia-shell ''; }; in { options.pinpox.programs.sway = { enable = mkEnableOption "sway window manager"; keyboardVariant = mkOption { type = types.str; default = "colemak"; description = "Keyboard variant for sway input (empty string for standard US layout)"; }; }; config = mkIf cfg.enable ( let c = config.pinpox.colors; # Dark theme color scheme (current) darkColors = '' client.focused #${c.BrightBlue} #${c.Blue} #${c.Black} #${c.BrightBlue} #${c.Blue} client.focused_inactive #${c.BrightWhite} #${c.BrightBlack} #${c.BrightWhite} #${c.BrightBlack} #${c.BrightBlack} client.unfocused #${c.BrightBlack} #${c.Black} #${c.BrightBlack} #${c.BrightBlack} #${c.Black} client.urgent #${c.BrightRed} #${c.Red} #${c.White} #${c.Red} #${c.Red} ''; # Light theme color scheme (inverted contrast) lightColors = '' client.focused #${c.Blue} #${c.BrightBlue} #${c.Black} #${c.Blue} #${c.BrightBlue} client.focused_inactive #${c.BrightBlack} #${c.White} #${c.Black} #${c.BrightBlack} #${c.BrightBlack} client.unfocused #${c.White} #${c.BrightWhite} #${c.BrightBlack} #${c.White} #${c.BrightWhite} client.urgent #${c.Red} #${c.BrightRed} #${c.Black} #${c.Red} #${c.BrightRed} ''; # Script to update Sway colors based on theme preference swayThemeSwitcher = pkgs.writeShellScript "sway-theme-switcher" '' theme="$1" if [[ "$theme" == "prefer-light" ]]; then ${pkgs.sway}/bin/swaymsg '${lightColors}' else ${pkgs.sway}/bin/swaymsg '${darkColors}' fi ''; in { # Animated harmonograph wallpaper service systemd.user.services.wl-harmonograph = let wl-harmonograph-pkg = wl-harmonograph.packages.x86_64-linux.default; fg = builtins.concatStringsSep "," [ c.Red c.Green c.Blue c.Yellow c.Cyan c.Magenta c.BrightBlue ]; in { Unit = { Description = "Animated harmonograph wallpaper"; PartOf = [ "sway-session.target" ]; After = [ "sway-session.target" "kanshi.service" ]; Wants = [ "kanshi.service" ]; }; Service = { ExecStart = "${wl-harmonograph-pkg}/bin/wl-harmonograph"; Environment = [ "HARMONOGRAPH_FG=${fg}" "HARMONOGRAPH_BG=${c.Black},${c.BrightBlack}" ]; Restart = "on-failure"; RestartSec = 2; }; Install = { WantedBy = [ "sway-session.target" ]; }; }; # Enable theme switcher and register Sway's theme script pinpox.services.theme-switcher = { scripts = [ "${swayThemeSwitcher}" ]; }; xdg.portal.config.sway = { # Use xdg-desktop-portal-gtk for every portal interface... default = [ "gtk" ]; # ... except for the ScreenCast, Screenshot and Secret "org.freedesktop.impl.portal.ScreenCast" = "wlr"; "org.freedesktop.impl.portal.Screenshot" = "wlr"; # ignore inhibit bc gtk portal always returns as success, # despite sway/the wlr portal not having an implementation, # stopping firefox from using wayland idle-inhibit "org.freedesktop.impl.portal.Inhibit" = "none"; }; # laucher programs.tofi = { enable = true; settings = { width = "100%"; height = "100%"; border-width = "0"; outline-width = "0"; padding-left = "35%"; padding-top = "35%"; result-spacing = "25"; num-results = "5"; font = config.pinpox.font.normal.family; background-color = "#${c.Black}A0"; text-color = "#${c.White}"; selection-color = "#${c.BrightBlue}"; selection-match-color = "#${c.Magenta}"; input-color = "#${c.BrightWhite}"; prompt-text = "\"\""; placeholder-text = "yes?"; placeholder-color = "#${c.Yellow}"; }; }; home.packages = with pkgs; [ wl-clipboard wlr-randr start-sway font-awesome line-awesome ]; wayland.windowManager.sway = { enable = true; config = rec { seat = { "*" = { xcursor_theme = "${config.gtk.cursorTheme.name} ${toString config.gtk.cursorTheme.size}"; }; }; keybindings = lib.mkOptionDefault { # Terminal "${modifier}+Return" = "exec rio"; # Launcher "${modifier}+p" = '' exec ${lib.getExe pkgs.noctalia-shell} ipc call launcher toggle ''; # OpenCrow chat panel "${modifier}+a" = '' exec ${lib.getExe pkgs.noctalia-shell} ipc call plugin:opencrow-chat toggle ''; # Url "${modifier}+Shift+p" = '' exec ${pkgs.firefox}/bin/firefox --new-window $(cat ~/.local/share/tofi-bookmarks | ${pkgs.tofi}/bin/tofi) ''; # Toggle microphone mute "${modifier}+m" = let mic-toggle = pkgs.writeShellScriptBin "mic-toggle" # sh '' source=$(${pkgs.pulseaudio}/bin/pactl get-default-source) ${pkgs.pulseaudio}/bin/pactl set-source-mute "$source" toggle ''; in "exec ${mic-toggle}/bin/mic-toggle"; # Cycle in tabbed with win+tab "${modifier}+Shift+Tab" = "focus prev"; "${modifier}+Tab" = "focus next"; # Screen lock "${modifier}+Shift+l" = "exec ${pkgs.swaylock}/bin/swaylock"; # Scratchpad "${modifier}+u" = "exec swaymsg '[app_id=\"dropdown\"] scratchpad show; [app_id=\"dropdown\"] resize set width 100 ppt height 100 ppt; [app_id=\"dropdown\"] move container to position 0 px 0 px'"; # Screen brightness "XF86MonBrightnessUp" = "exec ${pkgs.brightnessctl}/bin/brightnessctl set 5%+"; "XF86MonBrightnessDown" = "exec ${pkgs.brightnessctl}/bin/brightnessctl set 5%-"; # Volume key "XF86AudioMute" = "exec ${pkgs.pamixer}/bin/pamixer --toggle-mute"; "XF86AudioLowerVolume" = "exec ${pkgs.pamixer}/bin/pamixer -d 5"; "XF86AudioRaiseVolume" = "exec ${pkgs.pamixer}/bin/pamixer -i 5"; # Media keys "XF86AudioPlay" = "exec ${pkgs.playerctl}/bin/playerctl play-pause"; "XF86AudioNext" = "exec ${pkgs.playerctl}/bin/playerctl next"; "XF86AudioPrev" = "exec ${pkgs.playerctl}/bin/playerctl previous"; # "Airplane" button # "XF86RFKill" = # "Gear" button # "XF86AudioMedia" = # Screenshots "Print" = "exec screenshot-region"; "Shift+Print" = "exec screenshot-region-file"; }; modifier = "Mod4"; # Win key terminal = "rio"; floating.modifier = "Mod4"; startup = [ { command = let init-dropdown = pkgs.writeShellScript "init-dropdown" '' # Start the terminal ${pkgs.rio}/bin/rio --app-id=dropdown & # Wait for it to be moved to scratchpad by window rules sleep 0.5 # Fix position while it's in scratchpad (this ensures correct position on first show) ${pkgs.sway}/bin/swaymsg '[app_id="dropdown"] scratchpad show' ${pkgs.sway}/bin/swaymsg '[app_id="dropdown"] resize set width 100 ppt height 100 ppt' ${pkgs.sway}/bin/swaymsg '[app_id="dropdown"] move container to position 0 px 0 px' ${pkgs.sway}/bin/swaymsg '[app_id="dropdown"] move scratchpad' ''; in "${init-dropdown}"; always = true; } { command = lib.getExe start-noctalia; always = true; } ]; # Application/window specific rules window.commands = [ { command = "floating enable, border pixel 0"; criteria.class = "^Audacious$"; } { command = "floating enable"; criteria.title = "Firefox — Sharing Indicator"; } { command = "floating enable, border pixel 0, move scratchpad"; criteria.app_id = "dropdown"; } ]; input = { "*" = { xkb_layout = "us"; } // lib.optionalAttrs (cfg.keyboardVariant != "") { xkb_variant = cfg.keyboardVariant; }; }; focus.wrapping = "workspace"; colors = let c = config.pinpox.colors; in { focused = { background = "#${c.Blue}"; border = "#${c.BrightBlue}"; childBorder = "#${c.Blue}"; indicator = "#${c.BrightBlue}"; text = "#${c.Black}"; }; focusedInactive = { background = "#${c.BrightWhite}"; border = "#${c.BrightBlack}"; childBorder = "#${c.BrightWhite}"; indicator = "#${c.BrightBlack}"; text = "#${c.White}"; }; unfocused = { background = "#${c.Black}"; border = "#${c.BrightBlack}"; childBorder = "#${c.Black}"; indicator = "#${c.BrightBlack}"; text = "#${c.BrightBlack}"; }; urgent = { background = "#${c.Red}"; border = "#${c.Black}"; childBorder = "#${c.Red}"; indicator = "#${c.Red}"; text = "#${c.White}"; }; }; bars = [ ]; fonts = { names = [ "Berkeley Mono" ]; size = 11.0; }; workspaceAutoBackAndForth = true; # Default to tabbed layout workspaceLayout = "tabbed"; gaps = { smartGaps = true; smartBorders = "on"; bottom = 3; top = 3; horizontal = 3; vertical = 3; inner = 3; left = 3; right = 3; outer = 3; }; }; }; } ); } ================================================ FILE: home-manager/modules/swaylock/default.nix ================================================ { lib, config, ... }: with lib; let cfg = config.pinpox.programs.swaylock; in { options.pinpox.programs.swaylock.enable = mkEnableOption "swaylock screenlocker"; config = mkIf cfg.enable { programs.swaylock = { enable = true; settings = { # Turn the screen into the given color instead of white. color = "${config.pinpox.colors.Black}"; # Sets the indicator to show even if idle. indicator-idle-visible = false; # Sets the indicator radius. indicator-radius = 100; # Sets the color of the line between the inside and ring. line-color = "${config.pinpox.colors.White}"; # Show current count of failed authentication attempts. show-failed-attempts = true; # Sets the color of the ring of the indicator. ring-color = "${config.pinpox.colors.Green}"; # Sets the color of the text. text-color = "${config.pinpox.colors.White}"; # Sets the font of the text. font = "Berkeley Mono"; # Sets a fixed font size for the indicator text. font-size = 24; # Sets the color of the inside of the indicator when invalid. inside-wrong-color = "${config.pinpox.colors.Red}"; # When an empty password is provided, do not validate it. ignore-empty-password = true; # Sets the color of backspace highlight segments. bs-hl-color = "${config.pinpox.colors.Magenta}"; # Sets the color of the layout text. layout-text-color = "${config.pinpox.colors.White}"; }; }; }; } # -i, --image [[]:] Display the given image, optionally only on the given output. # -k, --show-keyboard-layout Display the current xkb layout while typing. # -K, --hide-keyboard-layout Hide the current xkb layout while typing. # -L, --disable-caps-lock-text Disable the Caps Lock text. # -l, --indicator-caps-lock Show the current Caps Lock state also on the indicator. # -s, --scaling Image scaling mode: stretch, fill, fit, center, tile, solid_color. # -t, --tiling Same as --scaling=tile. # -u, --no-unlock-indicator Disable the unlock indicator. # --indicator-thickness Sets the indicator thickness. # --indicator-x-position Sets the horizontal position of the indicator. # --indicator-y-position Sets the vertical position of the indicator. # --caps-lock-bs-hl-color Sets the color of backspace highlight segments when Caps Lock is active. # --caps-lock-key-hl-color Sets the color of the key press highlight segments when Caps Lock is active. # --inside-color Sets the color of the inside of the indicator. # --inside-clear-color Sets the color of the inside of the indicator when cleared. # --inside-caps-lock-color Sets the color of the inside of the indicator when Caps Lock is active. # --inside-ver-color Sets the color of the inside of the indicator when verifying. # --key-hl-color Sets the color of the key press highlight segments. # --layout-bg-color Sets the background color of the box containing the layout text. # --layout-border-color Sets the color of the border of the box containing the layout text. # --ring-clear-color Sets the color of the ring of the indicator when cleared. # --ring-caps-lock-color Sets the color of the ring of the indicator when Caps Lock is active. # --ring-ver-color Sets the color of the ring of the indicator when verifying. # --ring-wrong-color Sets the color of the ring of the indicator when invalid. # --separator-color Sets the color of the lines that separate highlight segments. # --text-clear-color Sets the color of the text when cleared. # --text-caps-lock-color Sets the color of the text when Caps Lock is active. # --text-ver-color Sets the color of the text when verifying. # --text-wrong-color Sets the color of the text when invalid. ================================================ FILE: home-manager/modules/swaylock/style.css ================================================ * { border: none; border-radius: 0; min-height: 0; font-family: "Berkeley Mono"; font-size: 15px; } window#waybar { /* background-color: rgba(43, 48, 59, 0.65); */ background-color: transparent; color: white; } /* window#waybar.hidden { */ /* opacity: 0.2; */ /* } */ #tags button { padding: 0px 5px; margin: 3px 3px; /* background-color: #161320; */ background-color: rgba(0, 0, 0, 0.2); color: #d9e0ee; /* Use box-shadow instead of border so the text isn't offset */ box-shadow: inset 0 -3px transparent; /* Avoid rounded borders under each workspace name */ border: none; border-radius: 3; } /* https://github.com/Alexays/Waybar/wiki/FAQ#the-workspace-buttons-have-a-strange-hover-effect */ #tags button:hover { /* background: rgba(0, 0, 0, 0.2); */ box-shadow: inset 0 -3px #d9e0ee; } #tags button.occupied { box-shadow: inset 0 -3px white; } #tags button.focused { background-color: black; box-shadow: inset 0 -3px green; } #tags button.urgent { background-color: #f28fad; } #mode { background-color: #64727d; border-bottom: 3px solid #d9e0ee; } #clock, #battery, #cpu, #memory, #disk, #temperature, #backlight, #network, #pulseaudio, #custom-media, #tray, #mode, #idle_inhibitor, #mpd, #language, #idle_inhibitor { padding: 5px 10px; margin: 3px 3px; color: #d9e0ee; border-radius: 3; /* background-color: #161320; */ background-color: black; } #window, #tags { margin: 0 4px; } /* If workspaces is the leftmost module, omit left margin */ .modules-left > widget:first-child > #tags { margin-left: 9px; } /* If workspaces is the rightmost module, omit right margin */ .modules-right > widget:last-child > #tags { margin-right: 0; } #clock { min-width: 45px; margin-right: 11px; } #battery { min-width: 55px; } @keyframes blink { to { background-color: #ffffff; color: #000000; } } #battery.critical:not(.charging) { background-color: #f53c3c; color: #d9e0ee; animation-name: blink; animation-duration: 0.5s; animation-timing-function: linear; animation-iteration-count: infinite; animation-direction: alternate; } label:focus { background-color: #d9e0ee; } #backlight { min-width: 55px; } #network { min-width: 150px; } #tray { /* background-color: #161320; */ background-color: black; } #tray > .passive { -gtk-icon-effect: dim; } #tray > .needs-attention { -gtk-icon-effect: highlight; background-color: #f28fad; } ================================================ FILE: home-manager/modules/taskwarrior/default.nix ================================================ { lib, config, pkgs, ... }: with lib; let cfg = config.pinpox.programs.taskwarrior; in { options.pinpox.programs.taskwarrior.enable = mkEnableOption "takswarrior configuration"; config = mkIf cfg.enable { programs.taskwarrior = { package = pkgs.taskwarrior3; # colorTheme Either one of the default provided theme as string, or a path to a theme configuration file. null or string or path # config Key-value configuration written to {file}`$XDG_CONFIG_HOME/task/taskrc`. attribute set of anything enable = true; # extraConfig Additional content written at the end of {file}`$XDG_CONFIG_HOME/task/taskrc`. strings concatenated with "\n" }; }; } ================================================ FILE: home-manager/modules/theme-switcher/default.nix ================================================ { config, lib, pkgs, ... }: with lib; let toggle-theme = pkgs.writeShellScriptBin "toggle-theme" (builtins.readFile ./toggle-theme.sh); cfg = config.pinpox.services.theme-switcher; # Build a script that calls all registered theme switch scripts themeWatcherScript = pkgs.writeShellScript "theme-watcher" '' update_theme() { local scheme=$(${pkgs.dconf}/bin/dconf read /org/gnome/desktop/interface/color-scheme 2>/dev/null) local theme="prefer-dark" if [[ "$scheme" == "'prefer-light'" ]]; then theme="prefer-light" fi ${concatStringsSep "\n" ( map (script: '' ${script} "$theme" '') cfg.scripts )} } # Set initial theme update_theme # Monitor dbus for changes to color-scheme ${pkgs.dconf}/bin/dconf watch /org/gnome/desktop/interface/color-scheme | while read -r line; do update_theme done ''; in { options.pinpox.services.theme-switcher = { enable = mkEnableOption "theme switcher service"; scripts = mkOption { type = types.listOf types.str; default = [ ]; description = '' List of scripts to call when theme changes. Each script will be called with one argument: either "prefer-light" or "prefer-dark". ''; }; }; config = mkIf cfg.enable { home.packages = [ toggle-theme ]; systemd.user.services.theme-watcher = { Unit = { Description = "Theme watcher - updates applications on dbus theme change"; PartOf = [ "graphical-session.target" ]; After = [ "graphical-session.target" ]; }; Service = { Type = "simple"; ExecStart = "${themeWatcherScript}"; Restart = "on-failure"; RestartSec = 3; }; Install = { WantedBy = [ "graphical-session.target" ]; }; }; }; } ================================================ FILE: home-manager/modules/theme-switcher/toggle-theme.sh ================================================ #!/usr/bin/env bash # Read current setting current=$(dconf read /org/gnome/desktop/interface/color-scheme 2>/dev/null) if [[ $current == "'prefer-dark'" ]] || [[ $current == "" ]]; then dconf write /org/gnome/desktop/interface/color-scheme "'prefer-light'" echo "Switched to light theme" else dconf write /org/gnome/desktop/interface/color-scheme "'prefer-dark'" echo "Switched to dark theme" fi # Verify the change echo "Current setting:" dbus-send --session --print-reply \ --dest=org.freedesktop.portal.Desktop \ /org/freedesktop/portal/desktop \ org.freedesktop.portal.Settings.Read \ string:'org.freedesktop.appearance' \ string:'color-scheme' 2>/dev/null | grep -A1 variant ================================================ FILE: home-manager/modules/tmux/default.nix ================================================ { lib, config, pkgs, ... }: with lib; let cfg = config.pinpox.programs.tmux; in { options.pinpox.programs.tmux.enable = mkEnableOption "tmux terminal mutliplexer"; config = mkIf cfg.enable { programs.tmux = { enable = true; # Set the prefix key. Overrules the "shortcut" option when set. prefix = "C-a"; # Automatically spawn a session if trying to attach and none are running. newSession = true; # Base index for windows and panes. baseIndex = 1; # Use 24 hour clock. clock24 = true; # Maximum number of lines held in window history. historyLimit = 8000; # Less command delay escapeTime = 20; # Set the $TERM variable. terminal = "screen-256color"; plugins = with pkgs.tmuxPlugins; [ tmux-fzf ]; extraConfig = builtins.readFile ./tmux.conf; }; }; } ================================================ FILE: home-manager/modules/tmux/tmux.conf ================================================ # Set the emulator's title set -g set-titles on set -g set-titles-string "#W" # Forward focus events from emulator set -s focus-events on # Watch for activity setw -g monitor-activity on set -g activity-action none # Copy to real clipboard set -g set-clipboard on #### # Look and feel #### # 24 bit colors set -sa terminal-overrides ',xterm-256color*:Tc' # Status bar set -g status-style fg=white,bg=black,default set -g window-status-current-style fg=red set -gw window-status-activity-style bg=colour162,none # Make inactive panes grey setw -g window-style 'bg=#313547' setw -g window-active-style 'bg=black' setw -g pane-active-border-style "" # Pane border gray set -g pane-border-style fg=colour235 set -g pane-active-border-style fg=yellow # Pane number display colors set -g display-panes-active-colour red set -g display-panes-colour blue set -g display-panes-time 500 # Enable mouse support set -g mouse on #### # Keys #### # # Clients & Sessions bind-key r source-file $HOME/.config/tmux/tmux.conf\; display "Reloaded" # # Windows & Panes # bind-key Space copy-mode # bind-key b break-pane # bind-key m join-pane -h # bind-key C-m join-pane # bind-key o last-pane # bind-key i last-window # bind-key c new-window -c "#{pane_current_path}" # bind-key -r C-h resize-pane -L # bind-key -r C-j resize-pane -D # bind-key -r C-k resize-pane -U # bind-key -r C-l resize-pane -R # bind-key . command-prompt "select-pane -t '%%'" # Select panes with hjkl instead of arrows unbind-key Left unbind-key Down unbind-key Up unbind-key Right bind-key h select-pane -L bind-key j select-pane -D bind-key k select-pane -U bind-key l select-pane -R bind-key - split-window -c "#{pane_current_path}" bind-key '|' split-window -h -c "#{pane_current_path}" # bind-key s swap-window # bind-key C-a send-prefix # bind-key a send-prefix # bind-key , select-pane -m # # Copy-mode # bind-key -T copy-mode-vi v send -X begin-selection # bind-key -T copy-mode-vi y send -X copy-selection # # set-option -g prefix C-a # # vi keys # setw -g mode-keys vi # set -g status-keys vi # # # Basic theme # https://github.com/jimeh/tmux-themepack/blob/master/basic.tmuxtheme # Right status #set -g status-right "\ # #[fg=colour231,bg=colour04]#{?client_prefix, ^A ,}#[default]\ # #[fg=colour231,bg=colour09]#{?pane_in_mode, Copy ,}#[default]\ # #[fg=colour002]#([ $(tmux show-option -qv key-table) = off ] && echo '(off) ')#[default]\ # https://man7.org/linux/man-pages/man1/tmux.1.html # #I: Index of window # #P: Index of pane # #W: Name of window # #F: Window flags with # escaped as ## # #S: Session name set -gF display-panes-active-colour "default" set -gF display-panes-colour "default" set -gF message-command-style "fg=default,bg=default" set -gF message-style "fg=default,bg=default" set -gF status-interval "1" set -gF status-justify "centre" set -gF status-left "#S #[fg=white]» #[fg=yellow]#I #[fg=cyan]#P" set -gF status-left-length "40" set -gF status-left-style "fg=green,bg=black" set -gF status-right "\ #{?#(echo $IN_NIX_SHELL), ($IN_NIX_SHELL ❄ $NIX_SHELL_PACKAGES) ,} $USER@#H \ #[fg=white]« #[fg=yellow]%H:%M:%S #[fg=green] %Y-%m-%d" # set -gF status-right-length "40" set -gF status-right-style "fg=cyan,bg=black" set -gF status-style "fg=cyan,bg=black" set -gwF clock-mode-colour "red" set -gwF clock-mode-style "24" set -gwF mode-style "fg=default,bg=red" set -gwF pane-active-border-style "fg=green,bg=default" set -gwF pane-border-style "fg=default,bg=default" set -gwF window-status-activity-style "fg=yellow,bg=black" # set -gwF window-status-current-format "#I:#W" set -gwF window-status-current-style "fg=black,bg=red" # set -gwF window-status-format "#I:#W#F" set -gwF window-status-separator " " ================================================ FILE: home-manager/modules/waybar/default.nix ================================================ { config, lib, pkgs, ... }: with lib; let cfg = config.pinpox.programs.waybar; # source=$(${pkgs.pulseaudio}/bin/pactl get-default-source) # writeShellApplication { # name = "show-nixos-org"; # # runtimeInputs = [ # curl # w3m # ]; # # text = '' # curl -s 'https://nixos.org' | w3m -dump -T text/html # ''; # } mic-exec-if = # Returns 0 if mic is not muted and there are clients listening, # 1 otherwise. pkgs.writeShellScriptBin "mic-exec-if" # sh '' source=$(${pkgs.pulseaudio}/bin/pactl get-default-source) mute_state=$(${pkgs.pulseaudio}/bin/pactl get-source-mute "$source" | awk '{print $2}') if [ "$mute_state" = "yes" ]; then exit 1 fi clients=$(${pkgs.pulseaudio}/bin/pactl list source-outputs | grep "application\.name" | uniq | awk -F'"' '{print $2}' | tr '\n' ',' | sed 's/,$/\n/') if [ -n "$clients" ]; then exit 0 fi exit 1 ''; mic-toggle = # Toggle microphone on/off pkgs.writeShellScriptBin "mic-toggle" # sh '' source=$(${pkgs.pulseaudio}/bin/pactl get-default-source) ${pkgs.pulseaudio}/bin/pactl set-source-mute "$source" toggle ''; mic-status-apps = # List applications currently accessing the microphone pkgs.writeShellScriptBin "mic-status-apps" # sh '' # List all input streams clients=$(${pkgs.pulseaudio}/bin/pactl list source-outputs | grep "application\.name" | uniq | awk -F'"' '{print $2}' | tr '\n' ',' | sed 's/,$/\n/') # Remove duplicates and format if [ -n "$clients" ]; then unique_clients=$(echo "$clients" | sort | uniq | paste -sd, -) echo " $unique_clients" fi ''; in { options.pinpox.programs.waybar.enable = mkEnableOption "waybar configuration"; config = mkIf cfg.enable { programs.waybar = { enable = true; style = let c = config.pinpox.colors; in '' @define-color Black #${c.Black}; @define-color BrightBlack #${c.BrightBlack}; @define-color White #${c.White}; @define-color BrightWhite #${c.BrightWhite}; @define-color Yellow #${c.Yellow}; @define-color BrightYellow #${c.BrightYellow}; @define-color Green #${c.Green}; @define-color BrightGreen #${c.BrightGreen}; @define-color Cyan #${c.Cyan}; @define-color BrightCyan #${c.BrightCyan}; @define-color Blue #${c.Blue}; @define-color BrightBlue #${c.BrightBlue}; @define-color Magenta #${c.Magenta}; @define-color BrightMagenta #${c.BrightMagenta}; @define-color Red #${c.Red}; @define-color BrightRed #${c.BrightRed}; ${fileContents ./style.css} ''; settings.mainbar = { layer = "top"; position = "bottom"; # height = 20; spacing = 4; # Gaps between modules (4px) modules-left = [ "sway/workspaces" "sway/mode" # "river/tags" ]; # modules-center = ["river/mode", "river/window"], # modules-right = ["idle_inhibitor", "backlight", "cpu","memory", "temperature"], modules-center = [ "custom/mic" "mpris" ]; modules-right = [ "tray" "network" "pulseaudio" "battery" "clock" ]; mpris = { player-icons = { "default" = "🎵"; "strawberry" = "🍓"; }; format = "{player_icon} {artist} - {title}"; max-length = 40; ignored-players = [ "firefox" "chromium" ]; }; # "river/tags" = { # "num-tags" = 9; # }; idle_inhibitor = { format = "{icon}"; format-icons = { activated = " "; deactivated = " "; }; }; tray = { # "icon-size": 21, spacing = 10; }; clock = { format = "{:%Y-%m-%d %H:%M}"; tooltip-format = "{:%Y %B}\n{calendar}"; }; # "cpu": { # "format": "{usage}% ", # "tooltip": false # }, # "memory": { # "format": "{}% " # }, # "temperature": { # // "thermal-zone": 2, # // "hwmon-path": "/sys/class/hwmon/hwmon2/temp1_input", # "critical-threshold": 80, # // "format-critical": "{temperatureC}°C {icon}", # "format": "{temperatureC}°C {icon}", # "format-icons": ["", "", ""] # }, # "backlight": { # // "device": "acpi_video1", # "format": "{percent}% {icon}", # "format-icons": [""] # }, battery = { states = { good = 95; warning = 30; critical = 15; }; format = "{capacity}% ▼"; format-charging = "{capacity}% ▲"; format-plugged = "{capacity}% ▲"; format-alt = "{time}"; format-full = "{capacity}% ▲"; }; network = { format-wifi = "{essid} ({signalStrength}%)"; format-ethernet = "{ipaddr}/{cidr}"; tooltip-format = "{ifname} via {gwaddr}"; format-linked = "{ifname} (No IP)"; format-disconnected = "Disconnected ✕"; format-alt = "{ifname}: {ipaddr}/{cidr}"; }; pulseaudio = { scroll-step = 1; # %, can be a float format = "{volume}% {format_source}"; format-bluetooth = "{volume}% {format_source}"; format-bluetooth-muted = "✕ {format_source}"; format-muted = "{volume}% {format_source}"; format-source = "{volume}% "; format-source-muted = "{volume}% "; "on-click" = "${pkgs.pulseaudio}/bin/pactl set-sink-mute @DEFAULT_SINK@ toggle"; "on-click-right" = "${mic-toggle}/bin/mic-toggle"; }; "custom/mic" = { "format" = "{}"; "max-length" = 40; "tooltip" = false; "interval" = 1; "exec" = "${mic-status-apps}/bin/mic-status-apps"; "exec-if" = "${mic-exec-if}/bin/mic-exec-if"; "on-click" = "${mic-toggle}/bin/mic-toggle"; }; }; }; }; } ================================================ FILE: home-manager/modules/waybar/style.css ================================================ /*************** * GENERAL * ***************/ * { border: none; border-radius: 0; min-height: 0; font-family: "Berkeley Mono", "Font Awesome 6 Free"; font-weight: 400; font-size: 16px; } window#waybar { background-color: transparent; color: @White; padding: 4px 0px; } #mode { border-bottom: 3px solid @White; } #clock, #battery, #cpu, #memory, #disk, #temperature, #backlight, #network, #pulseaudio, #custom-media, #tray, #mode, #idle_inhibitor, #custom-mic, #workspaces button, #mpd, #language, #idle_inhibitor { padding: 2px 8px; margin: 4px 3px; color: @White; border-radius: 0px; background-color: @Black; } #custom-mic, #mpris { padding: 2px 8px; margin: 3px 3px; color: @White; background-color: transparent; } #custom-mic { color: @Red; } #window { background-color: @Black; color: @White; } /********************* * WORKSPACES/TAGS * *********************/ #workspaces, #tags { margin-left: 7px; } #workspaces button, #tags button { background: @Black; color: @White; padding: 0px 0px; margin: 4px 0px; } #workspaces button.focused, #tags button.focused { background: @Blue; color: @Black; } #workspaces button.urgent, #tags button.urgent { background-color: @Red; color: @Black; } #workspaces button:hover, #tags button:hover { background: rgba(0, 0, 0, 0.2); box-shadow: inherit; text-shadow: inherit; } /* /1* If workspaces is the leftmost module, omit left margin *1/ */ /* .modules-left > widget:first-child > #tags { */ /* margin-left: 9px; */ /* } */ /* .modules-right > widget:last-child > #tags { */ /* margin-right: 0; */ /* } */ /********************* * CLOCK * *********************/ #clock { min-width: 50px; margin-right: 7px; } /********************* * BATTERY * *********************/ #battery { } #pulseaudio { } @keyframes blink { to { background-color: @White; color: @Black; } } #battery.critical:not(.charging) { background-color: @Red; color: @White; animation-name: blink; animation-duration: 0.5s; animation-timing-function: linear; animation-iteration-count: infinite; animation-direction: alternate; } /* label:focus { */ /* background-color: @White; */ /* } */ /********************* * BACKLIGHT * *********************/ #backlight { min-width: 60px; } /* #network { */ /* min-width: 150px; */ /* } */ /********************* * TRAY * *********************/ #tray { background-color: @Black; } #tray > .passive { -gtk-icon-effect: dim; } #tray > .needs-attention { -gtk-icon-effect: highlight; background-color: @Red; } ================================================ FILE: home-manager/modules/xdg/default.nix ================================================ { config, lib, ... }: with lib; let cfg = config.pinpox.defaults.xdg; in { options.pinpox.defaults.xdg = { enable = mkEnableOption "xdg defaults"; }; config = mkIf cfg.enable { xdg = { enable = true; configFile = { }; }; }; } ================================================ FILE: home-manager/modules/zed/default.nix ================================================ { config, lib, pkgs, ... }: with lib; let cfg = config.pinpox.programs.zed; in { options.pinpox.programs.zed = { enable = mkEnableOption "Zed editor configuration"; }; config = mkIf cfg.enable { services.gnome-keyring.enable = true; # Add nixd (Nix language server) for better Nix support home.packages = with pkgs; [ nixd ]; programs.zed-editor = { enable = true; extensions = [ "nix" ]; userSettings = { telemetry = { metrics = false; diagnostics = false; }; vim_mode = true; ui_font_size = 15; buffer_font_size = 15; buffer_font_family = "Berkeley Mono"; ui_font_family = "Berkeley Mono"; theme = { mode = "dark"; light = "Ayu Light"; dark = "One Dark"; }; language_overrides = { nix = { language_server_id = "nixd"; }; }; }; }; }; } ================================================ FILE: home-manager/modules/zellij/default.nix ================================================ { lib, config, ... }: with lib; let cfg = config.pinpox.programs.zellij; in { options.pinpox.programs.zellij.enable = mkEnableOption "zellij terminal mutliplexer"; config = mkIf cfg.enable { programs.zellij = { enable = true; # Don't auto-start zellij on new shells enableZshIntegration = false; settings = { keybinds.unbind = "Ctrl q"; session_serialization = false; show_startup_tips = false; simplified_ui = true; theme = "custom"; themes.custom = { fg = "#${config.pinpox.colors.White}"; bg = "#${config.pinpox.colors.BrightBlack}"; black = "#${config.pinpox.colors.Black}"; red = "#${config.pinpox.colors.Red}"; green = "#${config.pinpox.colors.Green}"; yellow = "#${config.pinpox.colors.BrightYellow}"; blue = "#${config.pinpox.colors.Blue}"; magenta = "#${config.pinpox.colors.Magenta}"; cyan = "#${config.pinpox.colors.Cyan}"; white = "#${config.pinpox.colors.White}"; orange = "#${config.pinpox.colors.Yellow}"; }; }; }; }; } ================================================ FILE: home-manager/modules/zk/config.toml ================================================ # vim: filetype=dosini # zk configuration file # # Uncomment the properties you want to customize. # NOTE SETTINGS # # Defines the default options used when generating new notes. [note] # Language used when writing notes. # This is used to generate slugs or with date formats. language = "en" # The default title used for new note, if no `--title` flag is provided. default-title = "Untitled" # Template used to generate a note's filename, without extension. filename = "{{format-date now '%Y-%m-%d-%H%M%S' }}" # The file extension used for the notes. extension = "md" # Template used to generate a note's content. # If not an absolute path, it is relative to .zk/templates/ template = "default.md" # Configure random ID generation. # The charset used for random IDs. You can use: # * letters: only letters from a to z. # * numbers: 0 to 9 # * alphanum: letters + numbers # * hex: hexadecimal, from a to f and 0 to 9 # * custom string: will use any character from the provided value #id-charset = "alphanum" # Length of the generated IDs. #id-length = 4 # Letter case for the random IDs, among lower, upper or mixed. #id-case = "lower" # EXTRA VARIABLES # # A dictionary of variables you can use for any custom values when generating # new notes. They are accessible in templates with {{extra.}} [extra] #key = "value" # GROUP OVERRIDES # # You can override global settings from [note] and [extra] for a particular # group of notes by declaring a [group.""] section. # # Specify the list of directories which will automatically belong to the group # with the optional `paths` property. # # Omitting `paths` is equivalent to providing a single path equal to the name of # the group. This can be useful to quickly declare a group by the name of the # directory it applies to. #[group.""] #paths = ["", ""] #[group."".note] #filename = "{{date now}}" #[group."".extra] #key = "value" [group.journal] paths = ["journal"] [group.journal.note] filename = "{{format-date now}}" template = "journal.md" # MARKDOWN SETTINGS [format.markdown] # Format used to generate links between notes. # Either "wiki", "markdown" or a custom template. Default is "markdown". link-format = "markdown" # Indicates whether a link's path will be percent-encoded. # Defaults to true for "markdown" format and false for "wiki" format. #link-encode-path = true # Indicates whether a link's path file extension will be removed. # Defaults to true. #link-drop-extension = true # Enable support for #hashtags. hashtags = true # Enable support for :colon:separated:tags:. colon-tags = true # Enable support for Bear's #multi-word tags# # Hashtags must be enabled for multi-word tags to work. multiword-tags = false # EXTERNAL TOOLS [tool] # Default editor used to open notes. When not set, the EDITOR or VISUAL # environment variables are used. editor = "nvim" # Pager used to scroll through long output. If you want to disable paging # altogether, set it to an empty string "". #pager = "less -FIRX" # Command used to preview a note during interactive fzf mode. # Set it to an empty string "" to disable preview. # bat is a great tool to render Markdown document with syntax highlighting. #https://github.com/sharkdp/bat fzf-preview = "bat -p --color always {-1}" # LSP # # Configure basic editor integration for LSP-compatible editors. # See https://github.com/mickael-menu/zk/blob/main/docs/editors-integration.md # [lsp] [lsp.diagnostics] # Each diagnostic can have for value: none, hint, info, warning, error # Report titles of wiki-links as hints. wiki-title = "hint" # Warn for dead links between notes. dead-link = "error" # NAMED FILTERS # # A named filter is a set of note filtering options used frequently together. # [filter] # Matches the notes created the last two weeks. For example: # $ zk list recent --limit 15 # $ zk edit recent --interactive recent = "--sort created- --created-after 'last two weeks'" # COMMAND ALIASES # # Aliases are user commands called with `zk [] []`. # # The alias will be executed with `$SHELL -c`, please refer to your shell's # man page to see the available syntax. In most shells: # * $@ can be used to expand all the provided flags and arguments # * you can pipe commands together with the usual | character # [alias] # Here are a few aliases to get you started. # Shortcut to a command. ls = "zk list $@" # Default flags for an existing command. #list = "zk list --quiet $@" # Edit the last modified note. edlast = "zk edit --limit 1 --sort modified- $@" # Edit the notes selected interactively among the notes created the last two weeks. # This alias doesn't take any argument, so we don't use $@. recent = "zk edit --sort created- --created-after 'last two weeks' --interactive" # Print paths separated with colons for the notes found with the given # arguments. This can be useful to expand a complex search query into a flag # taking only paths. For example: # zk list --link-to "`zk path -m potatoe`" path = "zk list --quiet --format {{path}} --delimiter , $@" # Show a random note. #lucky = "zk list --quiet --format full --sort random --limit 1" # Returns the Git history for the notes found with the given arguments. # Note the use of a pipe and the location of $@. #hist = "zk list --format path --delimiter0 --quiet $@ | xargs -t -0 git log --patch --" # Edit this configuration file. conf = '$EDITOR "~/code/github.com/pinpox/nixos/home-manager/modules/zk/config.toml"' # Edit journal journal = 'zk new --no-input "$ZK_NOTEBOOK_DIR/journal"' ================================================ FILE: home-manager/modules/zk/default.md ================================================ --- title: {{title}} created: {{format-date now}} visibility: private language: en tags: - {{slug title}} --- {{content}} # References ================================================ FILE: home-manager/modules/zk/default.nix ================================================ { lib, pkgs, config, ... }: with lib; let cfg = config.pinpox.programs.zk; in { options.pinpox.programs.zk.enable = mkEnableOption "zk zettelkasten client"; config = mkIf cfg.enable { home.packages = with pkgs; [ zk ]; programs.zsh = { sessionVariables.ZK_NOTEBOOK_DIR = "/home/pinpox/Notes"; shellAliases = { # Edit notes zke = "${pkgs.zk}/bin/zk edit --interactive -x journal"; zkn = "${pkgs.zk}/bin/zk new inbox"; }; }; xdg = { enable = true; configFile = { # zk configuration file zk_config = { target = "zk/config.toml"; source = ./config.toml; }; # Template for default notes zk_template_default = { target = "zk/templates/default.md"; source = ./default.md; }; # Template for juornal/dairy notes zk_template_journal = { target = "zk/templates/journal.md"; source = ./journal.md; }; }; }; }; } ================================================ FILE: home-manager/modules/zk/journal.md ================================================ --- title: {{title}} created: {{format-date now}} visibility: private language: de tags: - {{slug title}} - journal --- # {{format-date now "long"}} - {{title}} What did I do today? {{content}} ================================================ FILE: home-manager/profiles/common.nix ================================================ { lib, pkgs, nur, flake-self, ... }: with lib; { imports = [ ../colorscheme.nix ]; config = { # Home-manager nixpkgs config nixpkgs = { # Allow "unfree" licenced packages config = { allowUnfree = true; }; overlays = [ flake-self.overlays.default nur.overlays.default ]; }; # Extra arguments to pass to modules _module.args = { flake-inputs = flake-self.inputs; pinpox-utils = import ../../utils { inherit pkgs; }; }; # Include man-pages manual.manpages.enable = true; # Environment variables systemd.user.sessionVariables = { ZDOTDIR = "/home/pinpox/.config/zsh"; }; home = { # Install these packages for my user packages = with pkgs; [ delta eza htop machine-report nixfmt pkg-config tealdeer unzip ]; sessionVariables = { ZDOTDIR = "/home/pinpox/.config/zsh"; }; # This value determines the Home Manager release that your # configuration is compatible with. This helps avoid breakage # when a new Home Manager release introduces backwards # incompatible changes. # # You can update Home Manager without changing this value. See # the Home Manager release notes for a list of state version # changes in each release. stateVersion = "25.05"; }; # Let Home Manager install and manage itself. programs.home-manager.enable = true; }; } ================================================ FILE: home-manager/profiles/desktop/default.nix ================================================ { pkgs, ... }: let screenshot-region = pkgs.writeShellScriptBin "screenshot-region" # sh '' ${pkgs.slurp}/bin/slurp | ${pkgs.grim}/bin/grim -g - - | ${pkgs.wl-clipboard}/bin/wl-copy -t image/png ''; screenshot-region-file = pkgs.writeShellScriptBin "screenshot-region-file" # sh '' ${pkgs.grim}/bin/grim -g "$(${pkgs.slurp}/bin/slurp)" $(date +'%s_grim.png') ''; in { imports = [ ../common.nix ]; config = { home.keyboard = { variant = "colemak"; layout = "us"; }; pinpox = { defaults = { xdg.enable = true; calendar.enable = false; shell.enable = true; gtk.enable = true; fonts.enable = true; credentials.enable = true; email.enable = true; git.enable = true; }; services.theme-switcher.enable = true; programs.ssh = { enable = true; }; programs = { pi.enable = true; games.enable = true; obs-studio.enable = false; pandoc.enable = true; k9s.enable = false; zed.enable = false; helix.enable = false; easyeffects.enable = false; zellij.enable = true; claude-code.enable = true; chromium.enable = true; firefox.enable = true; tmux.enable = true; zk.enable = true; taskwarrior.enable = true; go.enable = true; foot.enable = true; rio.enable = true; sway.enable = true; swaylock.enable = true; # river.enable = true; # TODO: broken in nixpkgs (Zig build failure) waybar.enable = true; kanshi.enable = true; }; }; # Install these packages for my user home.packages = with pkgs; [ screenshot-region screenshot-region-file spotify mpv sysz thunderbird-bin deluge (audacious.override { withPlugins = true; }) file-roller imagemagick swaynotificationcenter tea but gitbutler chafa asciinema duf evince eza fd gcc # gimp adwaita-icon-theme gtk_engines h # https://github.com/zimbatm/h htop iputils libnotify manix matcha-gtk-theme meld ncdu networkmanagerapplet nextcloud-client nix-index nmap papirus-icon-theme pavucontrol pkg-config playerctl pre-commit signal-desktop sqlite tealdeer unzip viewnior vlc xarchiver xdg-utils # xfce-exo # thunar "open terminal here" thunar-archive-plugin thunar-volman tumbler # thunar thumbnails xfce4-volumed-pulse xfconf # thunar save settings # yubioath-desktop # thunar noto-fonts-color-emoji (thunar.override { thunarPlugins = with pkgs; [ thunar-volman thunar-archive-plugin thunar-media-tags-plugin ]; }) ]; xdg = { enable = true; configFile = { thunar_actions = { target = "Thunar/uca.xml"; text = '' utilities-terminal Open Terminal Here 1604472351415438-1 foot -D %f Open terminal in current directory * ''; }; }; }; }; } ================================================ FILE: home-manager/profiles/mobile/default.nix ================================================ { pkgs, ... }: { imports = [ ../common.nix ]; config = { home.keyboard = { # variant = "colemak"; layout = "us"; }; pinpox = { defaults = { xdg.enable = true; calendar.enable = false; shell.enable = true; gtk.enable = true; fonts.enable = false; # Disabled - noto-fonts-color-emoji can't cross-compile credentials.enable = true; git.enable = true; }; services.theme-switcher.enable = true; programs.ssh = { enable = true; }; programs = { chromium.enable = true; mpv.enable = true; zellij.enable = true; tmux.enable = true; foot.enable = true; rio.enable = true; sway.enable = true; sway.keyboardVariant = ""; # Use standard US QWERTY on mobile devices swaylock.enable = true; waybar.enable = true; mako.enable = true; }; }; # Install these packages for my user home.packages = with pkgs; [ swaynotificationcenter tea chafa duf evince eza fd gcc adwaita-icon-theme gtk_engines htop iputils libnotify ncdu networkmanagerapplet nix-index pavucontrol playerctl sqlite tealdeer unzip xdg-utils thunar-archive-plugin thunar-volman tumbler # thunar thumbnails xfconf # thunar save settings (thunar.override { thunarPlugins = with pkgs; [ thunar-volman thunar-archive-plugin thunar-media-tags-plugin ]; }) ]; xdg = { enable = true; configFile = { thunar_actions = { target = "Thunar/uca.xml"; text = '' utilities-terminal Open Terminal Here 1604472351415438-1 foot -D %f Open terminal in current directory * ''; }; }; }; services = { # Applets, shown in tray # Networking network-manager-applet.enable = true; # Bluetooth blueman-applet.enable = true; # Pulseaudio pasystray.enable = true; }; }; } ================================================ FILE: home-manager/profiles/server/default.nix ================================================ { pkgs, ... }: { imports = [ ../common.nix ]; config = { # Install these packages for my user home.packages = with pkgs; [ exa htop httpie pkg-config tealdeer unzip ]; pinpox = { defaults = { credentials.enable = true; git.enable = true; shell.enable = true; xdg.enable = true; }; programs = { tmux.enable = true; }; }; }; } ================================================ FILE: images/configuration.nix ================================================ { config, pkgs, lib, modulesPath, ... }: with lib; { imports = [ (modulesPath + "/profiles/qemu-guest.nix") ]; config = { # Filesystems fileSystems."/" = { device = "/dev/disk/by-label/nixos"; fsType = "ext4"; autoResize = true; }; # Bootloader boot.growPartition = true; boot.kernelParams = [ "console=ttyS0" ]; boot.loader.grub.device = "/dev/vda"; boot.loader.timeout = 0; # Locale settings i18n.defaultLocale = "en_US.UTF-8"; console = { font = "Lat2-Terminus16"; keyMap = "colemak"; }; # TODO set hostname networking.hostName = "my-nixos-host"; # Openssh programs.ssh.startAgent = false; services.openssh = { enable = true; passwordAuthentication = false; startWhenNeeded = true; kbdInteractiveAuthentication = false; permitRootLogin = "yes"; }; users = { users.root = { openssh.authorizedKeys.keyFiles = [ (pkgs.fetchurl { url = "https://github.com/pinpox.keys"; sha256 = "sha256-Cf/PSZemROU/Y0EEnr6A+FXE0M3+Kso5VqJgomGST/U="; }) ]; }; }; # Enable flakes nix.package = pkgs.nixVersions.stable; # Install some basic utilities environment.systemPackages = [ pkgs.git pkgs.ag pkgs.htop ]; # Let 'nixos-version --json' know about the Git revision # of this flake. # system.configurationRevision = pkgs.lib.mkIf (self ? rev) self.rev; }; } ================================================ FILE: images/raspi.nix ================================================ # # nix build '.#base-image' # raspi-image = # let # system = "aarch64-linux"; # in # import "${nixpkgs}/nixos/lib/make-disk-image.nix" { # pkgs = nixpkgs.legacyPackages."${system}"; # lib = nixpkgs.lib; # config = # (nixpkgs.lib.nixosSystem { # inherit system; # modules = [ ./images/raspi.nix ]; # }).config; # format = "qcow2"; # diskSize = 4096; # name = "raspi-image"; # }; # # base-image = # let # system = "x86_64-linux"; # in # import "${nixpkgs}/nixos/lib/make-disk-image.nix" { # pkgs = nixpkgs.legacyPackages."${system}"; # lib = nixpkgs.lib; # config = # (nixpkgs.lib.nixosSystem { # inherit system; # modules = [ ./images/configuration.nix ]; # }).config; # format = "qcow2"; # diskSize = 2048; # name = "base-image"; # }; # { config, pkgs, lib, ... }: { # Filesystems fileSystems."/" = { device = "/dev/disk/by-label/nixos"; fsType = "ext4"; autoResize = true; }; # This configuration worked on 09-03-2021 nixos-unstable @ commit 102eb68ceec # The image used https://hydra.nixos.org/build/134720986 boot = { kernelPackages = pkgs.linuxPackages_rpi4; tmpOnTmpfs = true; initrd.availableKernelModules = [ "usbhid" "usb_storage" ]; # ttyAMA0 is the serial console broken out to the GPIO kernelParams = [ "8250.nr_uarts=1" "console=ttyAMA0,115200" "console=tty1" # A lot GUI programs need this, nearly all wayland applications "cma=128M" ]; }; # Openssh programs.ssh.startAgent = false; services.openssh = { enable = true; passwordAuthentication = false; startWhenNeeded = true; kbdInteractiveAuthentication = false; permitRootLogin = "yes"; }; boot.growPartition = true; boot.loader.raspberryPi = { enable = true; version = 4; }; boot.loader.grub.enable = false; # Required for the Wireless firmware hardware.enableRedistributableFirmware = true; networking = { hostName = "nixos-raspi-4"; # Define your hostname. networkmanager = { enable = true; }; }; # Locale settings i18n.defaultLocale = "en_US.UTF-8"; console = { font = "Lat2-Terminus16"; keyMap = "colemak"; }; users = { users.root = { openssh.authorizedKeys.keyFiles = [ (pkgs.fetchurl { url = "https://github.com/pinpox.keys"; sha256 = "sha256-V0ek+L0axLt8v1sdyPXHfZgkbOxqwE3Zw8vOT2aNDcE="; }) ]; }; }; nix = { autoOptimiseStore = true; gc = { automatic = true; dates = "weekly"; options = "--delete-older-than 30d"; }; # Free up to 1GiB whenever there is less than 100MiB left. extraOptions = '' min-free = ${toString (100 * 1024 * 1024)} max-free = ${toString (1024 * 1024 * 1024)} ''; }; system.stateVersion = "20.09"; } ================================================ FILE: inventory.json ================================================ { "machines": { "fichte": { "installedAt": 1760275487 }, "clementine": { "installedAt": 1772618310 }, "traube": { "installedAt": 1773917352 } } } ================================================ FILE: inventory.nix ================================================ { self }: { machines = { kiwi.tags = [ "desktop" ]; tanne.tags = [ "desktop" ]; fichte.tags = [ "desktop" ]; kartoffel.tags = [ "desktop" ]; limette.tags = [ "desktop" ]; uconsole.tags = [ "mobile" ]; birne.tags = [ "server" ]; clementine.tags = [ "server" ]; kfbox.tags = [ "server" ]; porree.tags = [ "server" ]; traube.tags = [ "server" ]; }; meta.name = "pinpox-clan"; # My clan's top-level domain. All my service shall be accessible from within # the clan at https://.pin meta.domain = "pin"; instances = { # A service, which exports a endpoint: "music" # The goal is to be able to access https://music.pin from everywhere in the # clan and reach the navidrome server navidrome = { module.input = "self"; module.name = "@pinpox/navidrome"; roles.default.machines.kfbox = { settings.host = "music.0cx.de"; # settings.host = "music.pin"; }; }; # nostr = { # module.input = "clan-community"; # module.name = "opencrow"; # roles.default.machines.kiwi = { }; # roles.llm.machines.kiwi = { }; # roles.nostr-relay.machines.kiwi = { }; # }; # nostr = { # module.input = "clan-community"; # module.name = "nostr"; # roles.relay.machines.kfbox = { # settings.host = "nostr.0cx.de"; # }; # roles.groups-relay.machines.kfbox = { # settings.host = "groups.0cx.de"; # settings.relayName = "0cx.de NIP-29 Groups"; # settings.relayDescription = "NIP-29 group chat relay for 0cx.de"; # }; # }; thelounge = { module.input = "self"; module.name = "@pinpox/thelounge"; roles.default.machines.kfbox = { }; }; # Collects all "endpoint" exports from all services and generates a file # with CNAME entries. # The dm-dns services has an export of type "dataMesher" which signals "I # want the file 'dns/cnames' to be distributed via data-mesher". dm-dns = { module.name = "dm-dns"; roles.push.machines.kiwi = { }; roles.default.tags = [ "all" ]; }; # Also collects all "endpoint" exports from all services and uses them to # set up PKI. Only generators are used, no step-ca or otherwise # centralized service. The architecture is: # - A clan-wide CA is created (shared generater with deploy = false) # - Each host in the clan with the role additionally gets a Host CA, which # is signed by the Root CA (generator dependand on the root-ca, deployed # on each host) # - Each endpoint gets a certificate, signed by the Host CA (generator # dependant on the Host CA) # - All hosts trust the Clan-wide Root CA # With this, every host can just visit the endpoint and is presented with a # certificate that is automatically trusted, because there is a chain of # trust up to the Root CA. If a host adds a new service/endpoint no # re-deployment of other hosts is required. pki = { module.name = "pki"; roles.default.tags = [ "all" ]; }; # Pull-based NixOS deployment via data-mesher. Push machines send a flake # ref, all machines rebuild themselves from it. dm-pull-deploy = { module.input = "clan-community"; module.name = "dm-pull-deploy"; roles.push.machines.kiwi.settings.gitUrl = "https://github.com/pinpox/nixos.git"; roles.default.tags = [ "all" ]; roles.default.machines.tanne.settings.action = "build"; }; # The actual data-mesher. It collects all exports of type "dataMesher" from # all services and configures itself to distribute the files accordingly. data-mesher = { roles.bootstrap.tags = [ "server" ]; roles.default.tags = [ "all" ]; roles.default.settings.interfaces = [ "ygg" ]; }; internet = { module.name = "internet"; roles.default.tags = [ "server" ]; roles.default.machines = { kfbox.settings.host = "46.38.242.17"; porree.settings.host = "94.16.108.229"; clementine.settings.host = "152.53.139.179"; }; }; tor = { module.name = "tor"; # Add all machines to tor # Add smokeping to test if yggdrasil uses the best way roles.client.machines.kiwi = { }; roles.client.machines.porree = { }; roles.server.machines = { kiwi.settings = { secretHostname = false; portMapping = [ { port = 6443; target.port = 6443; } { port = 6446; target.port = 6446; } ]; }; porree.settings = { secretHostname = false; portMapping = [ { port = 6443; target.port = 6443; } { port = 6446; target.port = 6446; } ]; }; }; }; yggdrasil = { module.name = "yggdrasil"; roles.default.tags = [ "all" ]; }; desktop = { module.input = "clan-community"; module.name = "desktop"; roles.sway.tags.desktop = { }; roles.kde.machines.fichte = { }; }; user-root = { module.name = "users"; roles.default.tags.all = { }; roles.default.settings = { user = "root"; share = true; # no identity = no IdP account }; roles.default.extraModules = [ ./users/root.nix ]; }; user-pinpox = { module.name = "users"; roles.default.tags.all = { }; roles.default.settings = { user = "pinpox"; share = true; identity.main = { email = "mail@pablo.tools"; groups = [ "admins" "users" "miniflux-users" "opencloud-users" "paperless-users" ]; }; }; roles.default.extraModules = [ ./users/pinpox.nix ]; }; # Identity-only user: IdP account but no Unix account user-berber = { module.name = "users"; roles.default.tags.all = { }; roles.default.settings = { user = "berber"; systemUser = false; identity.main = { groups = [ "users" "opencloud-users" "miniflux-users" ]; }; }; }; user-lislon = { module.name = "users"; roles.default.machines.fichte = { }; roles.default.settings = { user = "lislon"; share = true; identity.main = { }; }; }; localsend = { module.input = "clan-community"; module.name = "localsend"; roles.default.tags = [ "desktop" ]; }; machine-type = { module.input = "self"; module.name = "@pinpox/machine-type"; roles.desktop.tags.desktop = { }; roles.server.tags.server = { }; roles.mobile.tags.mobile = { }; }; main = { module.input = "clan-community"; module.name = "authelia"; roles.default.machines.porree.settings = { publicHost = "auth.pablo.tools"; domain = "pablo.tools"; # Additional ACL rules (prepended before the auto-generated wildcard) accessControlRules = [ { domain = "paper.pablo.tools"; policy = "one_factor"; subject = "group:paperless-users"; } ]; # Restrict these clients to pinpox only. # Unlisted clients use the default: any authenticated user. clientAccess = { grafana = [ "user:pinpox" ]; prometheus = [ "user:pinpox" ]; }; # OIDC clients for non-clan-service consumers (miniflux, forgejo, # opencloud are still NixOS modules, not clan services, so they # can't export auth.client themselves yet). extraClients = { miniflux = { redirect_uris = [ "https://news.0cx.de/oauth2/oidc/callback" ]; scopes = [ "openid" "profile" "email" ]; token_endpoint_auth_method = "client_secret_basic"; }; forgejo = { client_name = "Forgejo"; authorization_policy = "two_factor"; require_pkce = true; pkce_challenge_method = "S256"; redirect_uris = [ "https://git.pinpox.com/user/oauth2/authelia/callback" ]; scopes = [ "openid" "email" "profile" "groups" ]; response_types = [ "code" ]; grant_types = [ "authorization_code" ]; token_endpoint_auth_method = "client_secret_basic"; }; opencloud = { client_name = "OpenCloud"; public = true; require_pkce = true; pkce_challenge_method = "S256"; scopes = [ "openid" "offline_access" "groups" "profile" "email" ]; redirect_uris = [ "https://cloud.pablo.tools/" "https://cloud.pablo.tools/oidc-callback.html" "https://cloud.pablo.tools/oidc-silent-redirect.html" ]; response_types = [ "code" ]; grant_types = [ "authorization_code" "refresh_token" ]; token_endpoint_auth_method = "none"; }; }; }; }; punchcard1 = { module.input = "clan-community"; module.name = "punchcard"; roles.default.machines.clementine.settings = { publicHost = "punchcard.megaclan3000.de"; environmentFile = "/run/secrets/punchcard/envfile"; }; roles.default.extraModules = [ ( { pinpox-utils, ... }: { clan.core.vars.generators."punchcard" = pinpox-utils.mkEnvGenerator [ "OIDC_ISSUER_URL" "OIDC_CLIENT_ID" "OIDC_CLIENT_SECRET" ]; } ) ]; }; punchcard2 = { module.input = "clan-community"; module.name = "punchcard"; roles.default.machines.clementine.settings = { publicHost = "punchcard2.megaclan3000.de"; port = 8100; environmentFile = "/run/secrets/punchcard2/envfile"; }; roles.default.extraModules = [ ( { pinpox-utils, ... }: { clan.core.vars.generators."punchcard2" = pinpox-utils.mkEnvGenerator [ "OIDC_ISSUER_URL" "OIDC_CLIENT_ID" "OIDC_CLIENT_SECRET" ]; } ) ]; }; monitoring = { module.input = "self"; module.name = "@pinpox/monitoring"; # node-exporter on every host # roles.node-exporter.tags.all = { }; # # # Centralized monitoring server lives on porree roles.prometheus.machines.porree.settings = { blackboxTargets = [ "https://pablo.tools" # "https://megaclan3000.de" # "https://build.lounge.rocks" # "https://pass.pablo.tools" # Vaultwarden # "https://pinpox.github.io/nixos/" # "https://cache.lounge.rocks/nix-cache/nix-cache-info" # "https://news.0cx.de" # "https://git.0cx.de" # Gitea # "https://irc.0cx.de" ]; }; # roles.loki.machines.porree = { }; roles.grafana.machines.porree.settings = { oidc = { enable = true; issuer = "https://auth.pablo.tools"; clientId = "grafana"; }; }; # roles.blackbox.machines.porree = { }; # roles.alertmanager-irc-relay.machines.porree = { }; }; importer = { module.name = "importer"; roles.default.tags.all = { }; # Import all modules from ./modules/ on all machines roles.default.extraModules = ( map (m: ./modules + "/${m}") ( builtins.filter (m: m != "opencrow") (builtins.attrNames self.nixosModules) ) ); }; zerotier = { roles.controller.machines.clementine = { }; roles.peer.machines.kiwi = { }; # roles.peer.tags.all = { }; }; wg-star = { module.input = "clan-community"; module.name = "dm-wireguard-star"; roles.controller.machines.porree.settings = { endpoint = "vpn.pablo.tools"; listenPort = 51821; }; roles.peer.machines.kiwi = { }; roles.peer.machines.kfbox = { }; }; wg-clan = { module.input = "clan-community"; module.name = "wireguard-star"; roles.controller.machines.porree.settings = { endpoint = "vpn.pablo.tools:51820"; }; roles.peer.machines = { kartoffel = { }; birne.settings.allowedIPs = [ "10.100.0.0/24" "192.168.101.0/24" ]; kfbox = { }; uconsole = { }; clementine = { }; kiwi = { }; limette = { }; }; }; }; } ================================================ FILE: machines/birne/configuration.nix ================================================ # Configuration for birne { config, lib, ... }: { imports = [ ./hardware-configuration.nix ]; # clan.core.networking.targetHost = "192.168.101.221"; # The global useDHCP flag is deprecated, therefore explicitly set to false here. # Per-interface useDHCP will be mandatory in the future, so this generated config # replicates the default behaviour. networking.useDHCP = false; networking.interfaces.eno1.useDHCP = true; # Host forwards incoming wg connections to the local network so we can reach LAN devices via wireguard. E.g. for retrieving stats directly from smart-home devices boot.kernel.sysctl."net.ipv4.ip_forward" = 1; boot.supportedFilesystems = { btrfs = true; zfs = true; }; networking.hostName = "birne"; pinpox = { services = { unifi-controller.enable = true; minio.enable = true; home-assistant.enable = false; }; defaults = { lvm-grub.enable = true; environment.enable = true; locale.enable = true; nix.enable = true; storagebox.enable = true; }; }; networking.firewall = { allowedUDPPorts = [ 3478 ]; allowedTCPPorts = [ 80 443 ]; }; } ================================================ FILE: machines/birne/hardware-configuration.nix ================================================ # Do not modify this file! It was generated by ‘nixos-generate-config’ # and may be overwritten by future invocations. Please make changes # to /etc/nixos/configuration.nix instead. { config, lib, pkgs, modulesPath, ... }: { imports = [ (modulesPath + "/installer/scan/not-detected.nix") ]; boot.initrd.availableKernelModules = [ "ahci" "xhci_pci" "usb_storage" "usbhid" "sd_mod" ]; boot.initrd.kernelModules = [ "dm-snapshot" ]; boot.kernelModules = [ "kvm-intel" ]; boot.extraModulePackages = [ ]; # ZFS support boot.supportedFilesystems = [ "zfs" ]; # Needed for ZFS # head -c4 /dev/urandom | od -A none -t x4 networking.hostId = "887bde8c"; # Efi partition (SSD) fileSystems."/boot" = { device = "/dev/disk/by-uuid/E45C-8185"; fsType = "vfat"; }; # Root drive (SSD) fileSystems."/" = { device = "/dev/disk/by-uuid/74866c52-5077-44aa-afb2-88ce9e72ab47"; fsType = "ext4"; }; # Swap partition swapDevices = [ { device = "/dev/disk/by-uuid/8551b399-6866-40e0-b8f5-266b5475ffa9"; } ]; # Data drive for seafile fileSystems."/mnt/data" = { device = "/dev/disk/by-uuid/426645bc-dbf6-4c4d-b389-16bbb55d7a14"; fsType = "ext4"; }; # Backup drive for restic fileSystems."/mnt/backup" = { device = "/dev/disk/by-uuid/9961fd1b-3162-474d-9e2e-7cb7d269cd0e"; fsType = "ext4"; }; fileSystems."/mnt/backup-old" = { device = "/dev/disk/by-uuid/a6a101de-0238-4b87-ada2-76653ce51cfc"; fsType = "ext4"; }; powerManagement.cpuFreqGovernor = lib.mkDefault "powersave"; } ================================================ FILE: machines/clementine/configuration.nix ================================================ { mc3000, trippy-track, pinpox-utils, ... }: { imports = [ trippy-track.nixosModules.default ]; clan.core.networking.targetHost = "152.53.139.179"; networking.hostName = "clementine"; networking.interfaces.ens3 = { ipv6.addresses = [ { address = "2a0a:4cc0:c0:f339::"; prefixLength = 64; } ]; }; pinpox.services.twitch-first.enable = true; clan.core.vars.generators."trippy-track" = pinpox-utils.mkEnvGenerator [ "OIDC_ISSUER_URL" "OIDC_CLIENT_ID" "OIDC_CLIENT_SECRET" "OIDC_REDIRECT_URL" ]; services.trippy-track = { enable = true; port = 8090; environmentFile = "/run/secrets/trippy-track/envfile"; }; services.qemuGuest.enable = true; networking.firewall = { enable = true; allowPing = true; allowedTCPPorts = [ 80 443 22 ]; }; services.caddy = { enable = true; virtualHosts = { "megaclan3000.de".extraConfig = '' root * ${mc3000.packages.x86_64-linux.mc3000} file_server encode zstd gzip ''; "travel.pinpox.com".extraConfig = '' reverse_proxy localhost:8090 ''; }; }; } ================================================ FILE: machines/clementine/disko.nix ================================================ # --- # schema = "single-disk" # [placeholders] # mainDisk = "/dev/disk/by-path/pci-0000:00:10.0" # --- # This file was automatically generated! # CHANGING this configuration requires wiping and reinstalling the machine { boot.loader.grub.efiSupport = true; boot.loader.grub.efiInstallAsRemovable = true; boot.loader.grub.enable = true; disko.devices = { disk = { main = { name = "main-9d6c201ebd034bc0800f78df376f4829"; device = "/dev/disk/by-path/pci-0000:00:10.0"; type = "disk"; content = { type = "gpt"; partitions = { "boot" = { size = "1M"; type = "EF02"; # for grub MBR priority = 1; }; ESP = { type = "EF00"; size = "500M"; content = { type = "filesystem"; format = "vfat"; mountpoint = "/boot"; mountOptions = [ "umask=0077" ]; }; }; root = { size = "100%"; content = { type = "filesystem"; format = "ext4"; mountpoint = "/"; }; }; }; }; }; }; }; } ================================================ FILE: machines/clementine/facter.json ================================================ { "version": 1, "system": "x86_64-linux", "virtualisation": "kvm", "hardware": { "bios": { "apm_info": { "supported": false, "enabled": false, "version": 0, "sub_version": 0, "bios_flags": 0 }, "vbe_info": { "version": 0, "video_memory": 0 }, "pnp": true, "pnp_id": 0, "lba_support": false, "low_memory_size": 654336, "smbios_version": 520 }, "bridge": [ { "index": 9, "attached_to": 0, "class_list": [ "pci", "bridge" ], "bus_type": { "hex": "0004", "name": "PCI", "value": 4 }, "slot": { "bus": 0, "number": 1 }, "base_class": { "hex": "0006", "name": "Bridge", "value": 6 }, "sub_class": { "hex": "0001", "name": "ISA bridge", "value": 1 }, "vendor": { "hex": "8086", "name": "Intel Corporation", "value": 32902 }, "sub_vendor": { "hex": "1af4", "value": 6900 }, "device": { "hex": "7000", "value": 28672 }, "sub_device": { "hex": "1100", "value": 4352 }, "model": "Intel ISA bridge", "sysfs_id": "/devices/pci0000:00/0000:00:01.0", "sysfs_bus_id": "0000:00:01.0", "detail": { "function": 0, "command": 259, "header_type": 0, "secondary_bus": 0, "irq": 0, "prog_if": 0 }, "module_alias": "pci:v00008086d00007000sv00001AF4sd00001100bc06sc01i00" }, { "index": 11, "attached_to": 0, "class_list": [ "pci", "bridge" ], "bus_type": { "hex": "0004", "name": "PCI", "value": 4 }, "slot": { "bus": 0, "number": 0 }, "base_class": { "hex": "0006", "name": "Bridge", "value": 6 }, "sub_class": { "hex": "0000", "name": "Host bridge", "value": 0 }, "vendor": { "hex": "8086", "name": "Intel Corporation", "value": 32902 }, "sub_vendor": { "hex": "1af4", "value": 6900 }, "device": { "hex": "1237", "value": 4663 }, "sub_device": { "hex": "1100", "value": 4352 }, "revision": { "hex": "0002", "value": 2 }, "model": "Intel Host bridge", "sysfs_id": "/devices/pci0000:00/0000:00:00.0", "sysfs_bus_id": "0000:00:00.0", "detail": { "function": 0, "command": 259, "header_type": 0, "secondary_bus": 0, "irq": 0, "prog_if": 0 }, "module_alias": "pci:v00008086d00001237sv00001AF4sd00001100bc06sc00i00" }, { "index": 12, "attached_to": 0, "class_list": [ "pci", "bridge" ], "bus_type": { "hex": "0004", "name": "PCI", "value": 4 }, "slot": { "bus": 0, "number": 1 }, "base_class": { "hex": "0006", "name": "Bridge", "value": 6 }, "sub_class": { "hex": "0080", "name": "Bridge", "value": 128 }, "vendor": { "hex": "8086", "name": "Intel Corporation", "value": 32902 }, "sub_vendor": { "hex": "1af4", "value": 6900 }, "device": { "hex": "7113", "value": 28947 }, "sub_device": { "hex": "1100", "value": 4352 }, "revision": { "hex": "0003", "value": 3 }, "model": "Intel Bridge", "sysfs_id": "/devices/pci0000:00/0000:00:01.3", "sysfs_bus_id": "0000:00:01.3", "resources": [ { "type": "irq", "base": 9, "triggered": 0, "enabled": true } ], "detail": { "function": 3, "command": 259, "header_type": 0, "secondary_bus": 0, "irq": 9, "prog_if": 0 }, "driver": "piix4_smbus", "driver_module": "i2c_piix4", "drivers": [ "piix4_smbus" ], "driver_modules": [ "i2c_piix4" ], "module_alias": "pci:v00008086d00007113sv00001AF4sd00001100bc06sc80i00" } ], "cdrom": [ { "index": 21, "attached_to": 14, "class_list": [ "cdrom", "scsi", "block_device" ], "bus_type": { "hex": "0084", "name": "SCSI", "value": 132 }, "slot": { "bus": 0, "number": 0 }, "base_class": { "hex": "0106", "name": "Mass Storage Device", "value": 262 }, "sub_class": { "hex": "0002", "name": "CD-ROM", "value": 2 }, "pci_interface": { "hex": "0003", "name": "DVD", "value": 3 }, "vendor": { "hex": "0000", "name": "QEMU", "value": 0 }, "device": { "hex": "0000", "name": "QEMU DVD-ROM", "value": 0 }, "revision": { "hex": "0000", "name": "2.5+", "value": 0 }, "model": "QEMU DVD-ROM", "sysfs_id": "/class/block/sr0", "sysfs_bus_id": "0:0:0:0", "sysfs_device_link": "/devices/pci0000:00/0000:00:01.1/ata1/host0/target0:0:0/0:0:0:0", "unix_device_name": "/dev/sr0", "unix_device_number": { "type": 98, "major": 11, "minor": 0, "range": 1 }, "unix_device_names": [ "/dev/cdrom", "/dev/disk/by-id/ata-QEMU_DVD-ROM_QM00001", "/dev/disk/by-path/pci-0000:00:01.1-ata-1", "/dev/disk/by-path/pci-0000:00:01.1-ata-1.0", "/dev/sr0" ], "unix_device_name2": "/dev/sg0", "unix_device_number2": { "type": 99, "major": 21, "minor": 0, "range": 1 }, "driver": "ata_piix", "driver_module": "ata_piix", "drivers": [ "ata_piix", "sr" ], "driver_modules": [ "ata_piix", "sr_mod" ] } ], "cpu": [ { "architecture": "x86_64", "vendor_name": "AuthenticAMD", "family": 25, "model": 17, "stepping": 0, "features": [ "fpu", "vme", "de", "pse", "tsc", "msr", "pae", "mce", "cx8", "apic", "sep", "mtrr", "pge", "mca", "cmov", "pat", "pse36", "clflush", "mmx", "fxsr", "sse", "sse2", "syscall", "nx", "mmxext", "fxsr_opt", "pdpe1gb", "rdtscp", "lm", "rep_good", "nopl", "xtopology", "cpuid", "extd_apicid", "tsc_known_freq", "pni", "pclmulqdq", "ssse3", "fma", "cx16", "pcid", "sse4_1", "sse4_2", "x2apic", "movbe", "popcnt", "tsc_deadline_timer", "aes", "xsave", "avx", "f16c", "rdrand", "hypervisor", "lahf_lm", "cmp_legacy", "cr8_legacy", "abm", "sse4a", "misalignsse", "3dnowprefetch", "osvw", "topoext", "perfctr_core", "ssbd", "ibrs", "ibpb", "stibp", "ibrs_enhanced", "vmmcall", "fsgsbase", "tsc_adjust", "bmi1", "avx2", "smep", "bmi2", "erms", "invpcid", "avx512f", "avx512dq", "rdseed", "adx", "smap", "avx512ifma", "clflushopt", "clwb", "avx512cd", "sha_ni", "avx512bw", "avx512vl", "xsaveopt", "xsavec", "xgetbv1", "xsaves", "avx512_bf16", "clzero", "xsaveerptr", "wbnoinvd", "arat", "avx512vbmi", "umip", "pku", "ospke", "avx512_vbmi2", "gfni", "vaes", "vpclmulqdq", "avx512_vnni", "avx512_bitalg", "avx512_vpopcntdq", "la57", "rdpid", "fsrm", "flush_l1d", "arch_capabilities" ], "bugs": [ "sysret_ss_attrs", "spectre_v1", "spectre_v2", "spec_store_bypass", "srso", "ibpb_no_ret" ], "bogo": 4493.24, "cache": 1024, "physical_id": 0, "siblings": 1, "cores": 1, "fpu": true, "fpu_exception": true, "cpuid_level": 13, "write_protect": false, "tlb_size": 1024, "clflush_size": 64, "cache_alignment": 64, "address_sizes": { "physical": "0x28", "virtual": "0x39" } }, { "architecture": "x86_64", "vendor_name": "AuthenticAMD", "family": 25, "model": 17, "stepping": 0, "features": [ "fpu", "vme", "de", "pse", "tsc", "msr", "pae", "mce", "cx8", "apic", "sep", "mtrr", "pge", "mca", "cmov", "pat", "pse36", "clflush", "mmx", "fxsr", "sse", "sse2", "syscall", "nx", "mmxext", "fxsr_opt", "pdpe1gb", "rdtscp", "lm", "rep_good", "nopl", "xtopology", "cpuid", "extd_apicid", "tsc_known_freq", "pni", "pclmulqdq", "ssse3", "fma", "cx16", "pcid", "sse4_1", "sse4_2", "x2apic", "movbe", "popcnt", "tsc_deadline_timer", "aes", "xsave", "avx", "f16c", "rdrand", "hypervisor", "lahf_lm", "cmp_legacy", "cr8_legacy", "abm", "sse4a", "misalignsse", "3dnowprefetch", "osvw", "topoext", "perfctr_core", "ssbd", "ibrs", "ibpb", "stibp", "ibrs_enhanced", "vmmcall", "fsgsbase", "tsc_adjust", "bmi1", "avx2", "smep", "bmi2", "erms", "invpcid", "avx512f", "avx512dq", "rdseed", "adx", "smap", "avx512ifma", "clflushopt", "clwb", "avx512cd", "sha_ni", "avx512bw", "avx512vl", "xsaveopt", "xsavec", "xgetbv1", "xsaves", "avx512_bf16", "clzero", "xsaveerptr", "wbnoinvd", "arat", "avx512vbmi", "umip", "pku", "ospke", "avx512_vbmi2", "gfni", "vaes", "vpclmulqdq", "avx512_vnni", "avx512_bitalg", "avx512_vpopcntdq", "la57", "rdpid", "fsrm", "flush_l1d", "arch_capabilities" ], "bugs": [ "sysret_ss_attrs", "spectre_v1", "spectre_v2", "spec_store_bypass", "srso", "ibpb_no_ret" ], "bogo": 4493.24, "cache": 1024, "physical_id": 1, "siblings": 1, "cores": 1, "fpu": true, "fpu_exception": true, "cpuid_level": 13, "write_protect": false, "tlb_size": 1024, "clflush_size": 64, "cache_alignment": 64, "address_sizes": { "physical": "0x28", "virtual": "0x39" } } ], "disk": [ { "index": 22, "attached_to": 16, "class_list": [ "disk", "block_device" ], "base_class": { "hex": "0106", "name": "Mass Storage Device", "value": 262 }, "sub_class": { "hex": "0000", "name": "Disk", "value": 0 }, "model": "Disk", "sysfs_id": "/class/block/vda", "sysfs_bus_id": "virtio1", "sysfs_device_link": "/devices/pci0000:00/0000:00:10.0/virtio1", "unix_device_name": "/dev/vda", "unix_device_number": { "type": 98, "major": 253, "minor": 0, "range": 16 }, "unix_device_names": [ "/dev/disk/by-path/pci-0000:00:10.0", "/dev/disk/by-path/virtio-pci-0000:00:10.0", "/dev/vda" ], "rom_id": "0x80", "resources": [ { "type": "disk_geo", "cylinders": 266305, "heads": 16, "sectors": 63, "size": "0x0", "geo_type": "logical" }, { "type": "size", "unit": "sectors", "value_1": 268435456, "value_2": 512 } ], "driver": "virtio-pci", "driver_module": "virtio_pci", "drivers": [ "virtio-pci", "virtio_blk" ], "driver_modules": [ "virtio_blk", "virtio_pci" ] } ], "graphics_card": [ { "index": 15, "attached_to": 0, "class_list": [ "graphics_card", "pci" ], "bus_type": { "hex": "0004", "name": "PCI", "value": 4 }, "slot": { "bus": 0, "number": 2 }, "base_class": { "hex": "0003", "name": "Display controller", "value": 3 }, "sub_class": { "hex": "0000", "name": "VGA compatible controller", "value": 0 }, "pci_interface": { "hex": "0000", "name": "VGA", "value": 0 }, "vendor": { "hex": "1234", "value": 4660 }, "sub_vendor": { "hex": "1af4", "value": 6900 }, "device": { "hex": "1111", "value": 4369 }, "sub_device": { "hex": "1100", "value": 4352 }, "revision": { "hex": "0002", "value": 2 }, "model": "VGA compatible controller", "sysfs_id": "/devices/pci0000:00/0000:00:02.0", "sysfs_bus_id": "0000:00:02.0", "resources": [ { "type": "mem", "base": 4261412864, "range": 8388608, "enabled": true, "access": "read_only", "prefetch": "no" }, { "type": "mem", "base": 4273799168, "range": 4096, "enabled": true, "access": "read_write", "prefetch": "no" }, { "type": "mem", "base": 786432, "range": 131072, "enabled": false, "access": "read_write", "prefetch": "no" } ], "detail": { "function": 0, "command": 3, "header_type": 0, "secondary_bus": 0, "irq": 0, "prog_if": 0 }, "driver": "bochs-drm", "driver_module": "bochs", "drivers": [ "bochs-drm" ], "driver_modules": [ "bochs" ], "module_alias": "pci:v00001234d00001111sv00001AF4sd00001100bc03sc00i00" } ], "hub": [ { "index": 23, "attached_to": 7, "class_list": [ "usb", "hub" ], "bus_type": { "hex": "0086", "name": "USB", "value": 134 }, "slot": { "bus": 0, "number": 0 }, "base_class": { "hex": "010a", "name": "Hub", "value": 266 }, "vendor": { "hex": "1d6b", "name": "Linux 6.14.10 uhci_hcd", "value": 7531 }, "device": { "hex": "0001", "name": "UHCI Host Controller", "value": 1 }, "revision": { "hex": "0000", "name": "6.14", "value": 0 }, "serial": "0000:00:01.2", "model": "Linux 6.14.10 uhci_hcd UHCI Host Controller", "sysfs_id": "/devices/pci0000:00/0000:00:01.2/usb1/1-0:1.0", "sysfs_bus_id": "1-0:1.0", "resources": [ { "type": "baud", "speed": 12000000, "bits": 0, "stop_bits": 0, "parity": 0, "handshake": 0 } ], "detail": { "device_class": { "hex": "0009", "name": "hub", "value": 9 }, "device_subclass": { "hex": "0000", "name": "per_interface", "value": 0 }, "device_protocol": 0, "interface_class": { "hex": "0009", "name": "hub", "value": 9 }, "interface_subclass": { "hex": "0000", "name": "per_interface", "value": 0 }, "interface_protocol": 0, "interface_number": 0, "interface_alternate_setting": 0 }, "hotplug": "usb", "driver": "hub", "driver_module": "usbcore", "drivers": [ "hub" ], "driver_modules": [ "usbcore" ], "module_alias": "usb:v1D6Bp0001d0614dc09dsc00dp00ic09isc00ip00in00" } ], "memory": [ { "index": 5, "attached_to": 0, "class_list": [ "memory" ], "base_class": { "hex": "0101", "name": "Internally Used Class", "value": 257 }, "sub_class": { "hex": "0002", "name": "Main Memory", "value": 2 }, "model": "Main Memory", "resources": [ { "type": "mem", "base": 0, "range": 4107390976, "enabled": true, "access": "read_write", "prefetch": "unknown" }, { "type": "phys_mem", "range": 4026531840 } ] } ], "monitor": [ { "index": 20, "attached_to": 15, "class_list": [ "monitor" ], "base_class": { "hex": "0100", "name": "Monitor", "value": 256 }, "sub_class": { "hex": "0002", "name": "LCD Monitor", "value": 2 }, "vendor": { "hex": "4914", "value": 18708 }, "device": { "hex": "1234", "name": "QEMU Monitor", "value": 4660 }, "serial": "0", "model": "QEMU Monitor", "resources": [ { "type": "monitor", "width": 1024, "height": 768, "vertical_frequency": 60, "interlaced": false }, { "type": "monitor", "width": 1280, "height": 800, "vertical_frequency": 60, "interlaced": false }, { "type": "monitor", "width": 1600, "height": 1200, "vertical_frequency": 60, "interlaced": false }, { "type": "monitor", "width": 1920, "height": 1080, "vertical_frequency": 60, "interlaced": false }, { "type": "monitor", "width": 2048, "height": 1152, "vertical_frequency": 60, "interlaced": false }, { "type": "monitor", "width": 640, "height": 480, "vertical_frequency": 60, "interlaced": false }, { "type": "monitor", "width": 800, "height": 600, "vertical_frequency": 60, "interlaced": false }, { "type": "size", "unit": "mm", "value_1": 325, "value_2": 203 } ], "detail": { "manufacture_year": 2014, "manufacture_week": 42, "vertical_sync": { "min": 50, "max": 125 }, "horizontal_sync": { "min": 30, "max": 160 }, "horizontal_sync_timings": { "disp": 1280, "sync_start": 1600, "sync_end": 1638, "total": 1728 }, "vertical_sync_timings": { "disp": 800, "sync_start": 804, "sync_end": 808, "total": 828 }, "clock": 107300, "width": 1280, "height": 800, "width_millimetres": 325, "height_millimetres": 203, "horizontal_flag": 45, "vertical_flag": 45, "vendor": "", "name": "QEMU Monitor" }, "driver_info": { "type": "display", "width": 2048, "height": 1152, "vertical_sync": { "min": 50, "max": 125 }, "horizontal_sync": { "min": 30, "max": 160 }, "bandwidth": 0, "horizontal_sync_timings": { "disp": 1280, "sync_start": 1600, "sync_end": 1638, "total": 1728 }, "vertical_sync_timings": { "disp": 800, "sync_start": 804, "sync_end": 808, "total": 828 }, "horizontal_flag": 45, "vertical_flag": 45 } } ], "mouse": [ { "index": 24, "attached_to": 23, "class_list": [ "mouse", "usb" ], "bus_type": { "hex": "0086", "name": "USB", "value": 134 }, "slot": { "bus": 0, "number": 0 }, "base_class": { "hex": "0105", "name": "Mouse", "value": 261 }, "sub_class": { "hex": "0003", "name": "USB Mouse", "value": 3 }, "vendor": { "hex": "0627", "name": "QEMU", "value": 1575 }, "device": { "hex": "0001", "name": "QEMU USB Tablet", "value": 1 }, "serial": "28754-0000:00:01.2-1", "compat_vendor": "Unknown", "compat_device": "Generic USB Mouse", "model": "QEMU USB Tablet", "sysfs_id": "/devices/pci0000:00/0000:00:01.2/usb1/1-1/1-1:1.0", "sysfs_bus_id": "1-1:1.0", "unix_device_name": "/dev/input/mice", "unix_device_number": { "type": 99, "major": 13, "minor": 63, "range": 1 }, "unix_device_names": [ "/dev/input/mice" ], "unix_device_name2": "/dev/input/mouse0", "unix_device_number2": { "type": 99, "major": 13, "minor": 32, "range": 1 }, "resources": [ { "type": "baud", "speed": 12000000, "bits": 0, "stop_bits": 0, "parity": 0, "handshake": 0 } ], "detail": { "device_class": { "hex": "0000", "name": "per_interface", "value": 0 }, "device_subclass": { "hex": "0000", "name": "per_interface", "value": 0 }, "device_protocol": 0, "interface_class": { "hex": "0003", "name": "hid", "value": 3 }, "interface_subclass": { "hex": "0000", "name": "per_interface", "value": 0 }, "interface_protocol": 0, "interface_number": 0, "interface_alternate_setting": 0 }, "hotplug": "usb", "driver": "usbhid", "driver_module": "usbhid", "drivers": [ "usbhid" ], "driver_modules": [ "usbhid" ], "driver_info": { "type": "mouse", "db_entry_0": [ "explorerps/2", "exps2" ], "xf86": "explorerps/2", "gpm": "exps2", "buttons": -1, "wheels": -1 }, "module_alias": "usb:v0627p0001d0000dc00dsc00dp00ic03isc00ip00in00" } ], "network_controller": [ { "index": 18, "attached_to": 13, "class_list": [ "network_controller" ], "bus_type": { "hex": "008f", "name": "Virtio", "value": 143 }, "slot": { "bus": 0, "number": 0 }, "base_class": { "hex": "0002", "name": "Network controller", "value": 2 }, "sub_class": { "hex": "0000", "name": "Ethernet controller", "value": 0 }, "vendor": "Virtio", "device": "Ethernet Card 0", "model": "Virtio Ethernet Card 0", "sysfs_id": "/devices/pci0000:00/0000:00:03.0/virtio0", "sysfs_bus_id": "virtio0", "unix_device_name": "ens3", "unix_device_names": [ "ens3" ], "resources": [ { "type": "hwaddr", "address": 50 }, { "type": "phwaddr", "address": 50 } ], "driver": "virtio_net", "driver_module": "virtio_net", "drivers": [ "virtio_net" ], "driver_modules": [ "virtio_net" ], "module_alias": "virtio:d00000001v00001AF4" } ], "network_interface": [ { "index": 25, "attached_to": 0, "class_list": [ "network_interface" ], "base_class": { "hex": "0107", "name": "Network Interface", "value": 263 }, "sub_class": { "hex": "0000", "name": "Loopback", "value": 0 }, "model": "Loopback network interface", "sysfs_id": "/class/net/lo", "unix_device_name": "lo", "unix_device_names": [ "lo" ] }, { "index": 26, "attached_to": 18, "class_list": [ "network_interface" ], "base_class": { "hex": "0107", "name": "Network Interface", "value": 263 }, "sub_class": { "hex": "0001", "name": "Ethernet", "value": 1 }, "model": "Ethernet network interface", "sysfs_id": "/class/net/ens3", "sysfs_device_link": "/devices/pci0000:00/0000:00:03.0/virtio0", "unix_device_name": "ens3", "unix_device_names": [ "ens3" ], "resources": [ { "type": "hwaddr", "address": 50 }, { "type": "phwaddr", "address": 50 } ], "driver": "virtio_net", "driver_module": "virtio_net", "drivers": [ "virtio_net" ], "driver_modules": [ "virtio_net" ] } ], "pci": [ { "index": 6, "attached_to": 0, "class_list": [ "pci", "unknown" ], "bus_type": { "hex": "0004", "name": "PCI", "value": 4 }, "slot": { "bus": 0, "number": 28 }, "base_class": { "hex": "0007", "name": "Communication controller", "value": 7 }, "sub_class": { "hex": "0080", "name": "Communication controller", "value": 128 }, "vendor": { "hex": "1af4", "value": 6900 }, "sub_vendor": { "hex": "1af4", "value": 6900 }, "device": { "hex": "1003", "value": 4099 }, "sub_device": { "hex": "0003", "value": 3 }, "model": "Communication controller", "sysfs_id": "/devices/pci0000:00/0000:00:1c.0", "sysfs_bus_id": "0000:00:1c.0", "resources": [ { "type": "io", "base": 49344, "range": 64, "enabled": true, "access": "read_write" }, { "type": "irq", "base": 11, "triggered": 0, "enabled": true }, { "type": "mem", "base": 4273811456, "range": 4096, "enabled": true, "access": "read_write", "prefetch": "no" }, { "type": "mem", "base": 824633753600, "range": 16384, "enabled": true, "access": "read_only", "prefetch": "no" } ], "detail": { "function": 0, "command": 1287, "header_type": 0, "secondary_bus": 0, "irq": 11, "prog_if": 0 }, "driver": "virtio-pci", "driver_module": "virtio_pci", "drivers": [ "virtio-pci" ], "driver_modules": [ "virtio_pci" ], "module_alias": "pci:v00001AF4d00001003sv00001AF4sd00000003bc07sc80i00" }, { "index": 8, "attached_to": 0, "class_list": [ "pci", "unknown" ], "bus_type": { "hex": "0004", "name": "PCI", "value": 4 }, "slot": { "bus": 0, "number": 16 }, "base_class": { "hex": "0001", "name": "Mass storage controller", "value": 1 }, "sub_class": { "hex": "0000", "name": "SCSI storage controller", "value": 0 }, "vendor": { "hex": "1af4", "value": 6900 }, "sub_vendor": { "hex": "1af4", "value": 6900 }, "device": { "hex": "1001", "value": 4097 }, "sub_device": { "hex": "0002", "value": 2 }, "model": "SCSI storage controller", "sysfs_id": "/devices/pci0000:00/0000:00:10.0", "sysfs_bus_id": "0000:00:10.0", "resources": [ { "type": "io", "base": 49152, "range": 128, "enabled": true, "access": "read_write" }, { "type": "irq", "base": 11, "triggered": 0, "enabled": true }, { "type": "mem", "base": 4273807360, "range": 4096, "enabled": true, "access": "read_write", "prefetch": "no" }, { "type": "mem", "base": 824633737216, "range": 16384, "enabled": true, "access": "read_only", "prefetch": "no" } ], "detail": { "function": 0, "command": 1287, "header_type": 0, "secondary_bus": 0, "irq": 11, "prog_if": 0 }, "driver": "virtio-pci", "driver_module": "virtio_pci", "drivers": [ "virtio-pci" ], "driver_modules": [ "virtio_pci" ], "module_alias": "pci:v00001AF4d00001001sv00001AF4sd00000002bc01sc00i00" }, { "index": 10, "attached_to": 0, "class_list": [ "pci", "unknown" ], "bus_type": { "hex": "0004", "name": "PCI", "value": 4 }, "slot": { "bus": 0, "number": 30 }, "base_class": { "hex": "0000", "name": "Unclassified device", "value": 0 }, "sub_class": { "hex": "00ff", "value": 255 }, "vendor": { "hex": "1af4", "value": 6900 }, "sub_vendor": { "hex": "1af4", "value": 6900 }, "device": { "hex": "1002", "value": 4098 }, "sub_device": { "hex": "0005", "value": 5 }, "model": "Unclassified device", "sysfs_id": "/devices/pci0000:00/0000:00:1e.0", "sysfs_bus_id": "0000:00:1e.0", "resources": [ { "type": "io", "base": 49408, "range": 64, "enabled": true, "access": "read_write" }, { "type": "irq", "base": 10, "triggered": 0, "enabled": true }, { "type": "mem", "base": 824633769984, "range": 16384, "enabled": true, "access": "read_only", "prefetch": "no" } ], "detail": { "function": 0, "command": 263, "header_type": 0, "secondary_bus": 0, "irq": 10, "prog_if": 0 }, "driver": "virtio-pci", "driver_module": "virtio_pci", "drivers": [ "virtio-pci" ], "driver_modules": [ "virtio_pci" ], "module_alias": "pci:v00001AF4d00001002sv00001AF4sd00000005bc00scFFi00" }, { "index": 13, "attached_to": 0, "class_list": [ "pci", "unknown" ], "bus_type": { "hex": "0004", "name": "PCI", "value": 4 }, "slot": { "bus": 0, "number": 3 }, "base_class": { "hex": "0002", "name": "Network controller", "value": 2 }, "sub_class": { "hex": "0000", "name": "Ethernet controller", "value": 0 }, "vendor": { "hex": "1af4", "value": 6900 }, "sub_vendor": { "hex": "1af4", "value": 6900 }, "device": { "hex": "1000", "value": 4096 }, "sub_device": { "hex": "0001", "value": 1 }, "model": "Ethernet controller", "sysfs_id": "/devices/pci0000:00/0000:00:03.0", "sysfs_bus_id": "0000:00:03.0", "resources": [ { "type": "io", "base": 49280, "range": 64, "enabled": true, "access": "read_write" }, { "type": "irq", "base": 10, "triggered": 0, "enabled": true }, { "type": "mem", "base": 4273471488, "range": 262144, "enabled": false, "access": "read_only", "prefetch": "no" }, { "type": "mem", "base": 4273803264, "range": 4096, "enabled": true, "access": "read_write", "prefetch": "no" }, { "type": "mem", "base": 824633720832, "range": 16384, "enabled": true, "access": "read_only", "prefetch": "no" } ], "detail": { "function": 0, "command": 1287, "header_type": 0, "secondary_bus": 0, "irq": 10, "prog_if": 0 }, "driver": "virtio-pci", "driver_module": "virtio_pci", "drivers": [ "virtio-pci" ], "driver_modules": [ "virtio_pci" ], "module_alias": "pci:v00001AF4d00001000sv00001AF4sd00000001bc02sc00i00" } ], "storage_controller": [ { "index": 14, "attached_to": 0, "class_list": [ "storage_controller", "pci" ], "bus_type": { "hex": "0004", "name": "PCI", "value": 4 }, "slot": { "bus": 0, "number": 1 }, "base_class": { "hex": "0001", "name": "Mass storage controller", "value": 1 }, "sub_class": { "hex": "0001", "name": "IDE interface", "value": 1 }, "pci_interface": { "hex": "0080", "value": 128 }, "vendor": { "hex": "8086", "name": "Intel Corporation", "value": 32902 }, "sub_vendor": { "hex": "1af4", "value": 6900 }, "device": { "hex": "7010", "value": 28688 }, "sub_device": { "hex": "1100", "value": 4352 }, "model": "Intel IDE interface", "sysfs_id": "/devices/pci0000:00/0000:00:01.1", "sysfs_bus_id": "0000:00:01.1", "resources": [ { "type": "io", "base": 1014, "range": 1, "enabled": true, "access": "read_write" }, { "type": "io", "base": 368, "range": 8, "enabled": true, "access": "read_write" }, { "type": "io", "base": 49504, "range": 16, "enabled": true, "access": "read_write" }, { "type": "io", "base": 496, "range": 8, "enabled": true, "access": "read_write" }, { "type": "io", "base": 886, "range": 1, "enabled": true, "access": "read_write" } ], "detail": { "function": 1, "command": 263, "header_type": 0, "secondary_bus": 0, "irq": 0, "prog_if": 128 }, "driver": "ata_piix", "driver_module": "ata_piix", "drivers": [ "ata_piix" ], "driver_modules": [ "ata_piix" ], "module_alias": "pci:v00008086d00007010sv00001AF4sd00001100bc01sc01i80" }, { "index": 16, "attached_to": 8, "class_list": [ "storage_controller" ], "bus_type": { "hex": "008f", "name": "Virtio", "value": 143 }, "slot": { "bus": 0, "number": 0 }, "base_class": { "hex": "0001", "name": "Mass storage controller", "value": 1 }, "sub_class": { "hex": "0080", "name": "Storage controller", "value": 128 }, "vendor": "Virtio", "device": "Storage 0", "model": "Virtio Storage 0", "sysfs_id": "/devices/pci0000:00/0000:00:10.0/virtio1", "sysfs_bus_id": "virtio1", "driver": "virtio_blk", "driver_module": "virtio_blk", "drivers": [ "virtio_blk" ], "driver_modules": [ "virtio_blk" ], "module_alias": "virtio:d00000002v00001AF4" } ], "system": { "form_factor": "desktop" }, "unknown": [ { "index": 17, "attached_to": 6, "class_list": [ "unknown" ], "base_class": { "hex": "0000", "name": "Unclassified device", "value": 0 }, "sub_class": { "hex": "0000", "name": "Unclassified device", "value": 0 }, "vendor": "Virtio", "device": "", "model": "Virtio Unclassified device", "sysfs_id": "/devices/pci0000:00/0000:00:1c.0/virtio2", "sysfs_bus_id": "virtio2", "driver": "virtio_console", "driver_module": "virtio_console", "drivers": [ "virtio_console" ], "driver_modules": [ "virtio_console" ], "module_alias": "virtio:d00000003v00001AF4" }, { "index": 19, "attached_to": 10, "class_list": [ "unknown" ], "base_class": { "hex": "0000", "name": "Unclassified device", "value": 0 }, "sub_class": { "hex": "0000", "name": "Unclassified device", "value": 0 }, "vendor": "Virtio", "device": "", "model": "Virtio Unclassified device", "sysfs_id": "/devices/pci0000:00/0000:00:1e.0/virtio3", "sysfs_bus_id": "virtio3", "driver": "virtio_balloon", "driver_module": "virtio_balloon", "drivers": [ "virtio_balloon" ], "driver_modules": [ "virtio_balloon" ], "module_alias": "virtio:d00000005v00001AF4" } ], "usb_controller": [ { "index": 7, "attached_to": 0, "class_list": [ "usb_controller", "pci" ], "bus_type": { "hex": "0004", "name": "PCI", "value": 4 }, "slot": { "bus": 0, "number": 1 }, "base_class": { "hex": "000c", "name": "Serial bus controller", "value": 12 }, "sub_class": { "hex": "0003", "name": "USB Controller", "value": 3 }, "pci_interface": { "hex": "0000", "name": "UHCI", "value": 0 }, "vendor": { "hex": "8086", "name": "Intel Corporation", "value": 32902 }, "sub_vendor": { "hex": "1af4", "value": 6900 }, "device": { "hex": "7020", "value": 28704 }, "sub_device": { "hex": "1100", "value": 4352 }, "revision": { "hex": "0001", "value": 1 }, "model": "Intel USB Controller", "sysfs_id": "/devices/pci0000:00/0000:00:01.2", "sysfs_bus_id": "0000:00:01.2", "resources": [ { "type": "io", "base": 49472, "range": 32, "enabled": true, "access": "read_write" }, { "type": "irq", "base": 11, "triggered": 0, "enabled": true } ], "detail": { "function": 2, "command": 263, "header_type": 0, "secondary_bus": 0, "irq": 11, "prog_if": 0 }, "driver": "uhci_hcd", "driver_module": "uhci_hcd", "drivers": [ "uhci_hcd" ], "driver_modules": [ "uhci_hcd" ], "driver_info": { "type": "module", "db_entry_0": [ "uhci-hcd" ], "active": true, "modprobe": true, "names": [ "uhci-hcd" ], "module_args": [ "" ], "conf": "" }, "module_alias": "pci:v00008086d00007020sv00001AF4sd00001100bc0Csc03i00" } ] }, "smbios": { "bios": { "handle": 0, "vendor": "netcup", "version": "VPS 500 G12", "date": "03/04/2026", "features": null, "start_address": "0xe8000", "rom_size": 65536 }, "chassis": [ { "handle": 768, "manufacturer": "QEMU", "version": "pc-i440fx-9.2", "chassis_type": { "hex": "0001", "name": "Other", "value": 1 }, "lock_present": false, "bootup_state": { "hex": "0003", "name": "Safe", "value": 3 }, "power_state": { "hex": "0003", "name": "Safe", "value": 3 }, "thermal_state": { "hex": "0003", "name": "Safe", "value": 3 }, "security_state": { "hex": "0002", "name": "Unknown", "value": 2 }, "oem": "0x0" } ], "memory_array": [ { "handle": 4096, "location": { "hex": "0001", "name": "Other", "value": 1 }, "usage": { "hex": "0003", "name": "System memory", "value": 3 }, "ecc": { "hex": "0006", "name": "Multi-bit", "value": 6 }, "max_size": "0x400000", "error_handle": 65534, "slots": 1 } ], "memory_array_mapped_address": [ { "handle": 4864, "array_handle": 4096, "start_address": "0x0", "end_address": "0xc0000000", "part_width": 1 }, { "handle": 4865, "array_handle": 4096, "start_address": "0x100000000", "end_address": "0x140000000", "part_width": 1 } ], "memory_device": [ { "handle": 4352, "location": "DIMM 0", "bank_location": "", "manufacturer": "QEMU", "part_number": "", "array_handle": 4096, "error_handle": 65534, "width": 0, "ecc_bits": 0, "size": 4194304, "form_factor": { "hex": "0009", "name": "DIMM", "value": 9 }, "set": 0, "memory_type": { "hex": "0007", "name": "RAM", "value": 7 }, "memory_type_details": [ "Other" ], "speed": 0 } ], "processor": [ { "handle": 1024, "socket": "CPU 0", "socket_type": { "hex": "0001", "name": "Other", "value": 1 }, "socket_populated": true, "manufacturer": "QEMU", "version": "pc-i440fx-9.2", "part": "", "processor_type": { "hex": "0003", "name": "CPU", "value": 3 }, "processor_family": { "hex": "00fe", "name": "Other", "value": 254 }, "processor_status": { "hex": "0001", "name": "Enabled", "value": 1 }, "clock_ext": 0, "clock_max": 2000, "cache_handle_l1": 0, "cache_handle_l2": 0, "cache_handle_l3": 0 }, { "handle": 1025, "socket": "CPU 1", "socket_type": { "hex": "0001", "name": "Other", "value": 1 }, "socket_populated": true, "manufacturer": "QEMU", "version": "pc-i440fx-9.2", "part": "", "processor_type": { "hex": "0003", "name": "CPU", "value": 3 }, "processor_family": { "hex": "00fe", "name": "Other", "value": 254 }, "processor_status": { "hex": "0001", "name": "Enabled", "value": 1 }, "clock_ext": 0, "clock_max": 2000, "cache_handle_l1": 0, "cache_handle_l2": 0, "cache_handle_l3": 0 } ], "system": { "handle": 256, "manufacturer": "netcup", "product": "KVM Server", "version": "VPS 500 G12", "wake_up": { "hex": "0006", "name": "Power Switch", "value": 6 } } } } ================================================ FILE: machines/fichte/configuration.nix ================================================ { pkgs, nixos-hardware, lib, ... }: { imports = [ ./disko-config-btrfs.nix nixos-hardware.nixosModules.lenovo-thinkpad-t490 ]; environment.systemPackages = with pkgs; [ python3 ]; programs.vscode.enable = true; networking.hostName = "fichte"; # Set keymap to DE on this device console.keyMap = lib.mkForce "de"; services.xserver = { layout = "de"; xkbOptions = "eurosign:e"; }; } ================================================ FILE: machines/fichte/disko-config-btrfs.nix ================================================ { boot.growPartition = true; boot.supportedFilesystems.btrfs = true; services.btrfs.autoScrub = { enable = true; interval = "weekly"; # Defaults to all # fileSystems = [ "/" ]; }; disko.devices = { disk = { main = { type = "disk"; device = "/dev/nvme0n1"; content = { type = "gpt"; partitions = { ESP = { size = "512M"; type = "EF00"; content = { type = "filesystem"; format = "vfat"; mountpoint = "/boot"; mountOptions = [ "umask=0077" ]; }; }; luks = { size = "100%"; content = { type = "luks"; name = "crypted"; # disable settings.keyFile if you want to use interactive password entry #passwordFile = "/tmp/secret.key"; # Interactive settings = { allowDiscards = true; # keyFile = "/tmp/secret.key"; }; # additionalKeyFiles = [ "/tmp/additionalSecret.key" ]; content = { type = "btrfs"; extraArgs = [ "-f" ]; subvolumes = { "/root" = { mountpoint = "/"; mountOptions = [ "compress=zstd" "noatime" ]; }; "/home" = { mountpoint = "/home"; mountOptions = [ "compress=zstd" "noatime" ]; }; "/nix" = { mountpoint = "/nix"; mountOptions = [ "compress=zstd" "noatime" ]; }; "/swap" = { mountpoint = "/.swapvol"; swap.swapfile.size = "8G"; }; }; }; }; }; }; }; }; }; }; } ================================================ FILE: machines/fichte/facter.json ================================================ { "version": 1, "system": "x86_64-linux", "virtualisation": "none", "hardware": { "bios": { "apm_info": { "supported": false, "enabled": false, "version": 0, "sub_version": 0, "bios_flags": 0 }, "vbe_info": { "version": 0, "video_memory": 0 }, "pnp": false, "pnp_id": 0, "lba_support": false, "low_memory_size": 0, "smbios_version": 769 }, "bluetooth": [ { "index": 37, "attached_to": 42, "class_list": ["usb", "bluetooth"], "bus_type": { "hex": "0086", "name": "USB", "value": 134 }, "slot": { "bus": 0, "number": 0 }, "base_class": { "hex": "0115", "name": "Bluetooth Device", "value": 277 }, "vendor": { "hex": "8087", "value": 32903 }, "device": { "hex": "0aaa", "value": 2730 }, "revision": { "hex": "0000", "name": "0.02", "value": 0 }, "model": "Bluetooth Device", "sysfs_id": "/devices/pci0000:00/0000:00:14.0/usb1/1-10/1-10:1.0", "sysfs_bus_id": "1-10:1.0", "resources": [ { "type": "baud", "speed": 12000000, "bits": 0, "stop_bits": 0, "parity": 0, "handshake": 0 } ], "detail": { "device_class": { "hex": "00e0", "name": "wireless", "value": 224 }, "device_subclass": { "hex": "0001", "name": "audio", "value": 1 }, "device_protocol": 1, "interface_class": { "hex": "00e0", "name": "wireless", "value": 224 }, "interface_subclass": { "hex": "0001", "name": "audio", "value": 1 }, "interface_protocol": 1, "interface_number": 0, "interface_alternate_setting": 0 }, "hotplug": "usb", "driver": "btusb", "driver_module": "btusb", "drivers": ["btusb"], "driver_modules": ["btusb"], "module_alias": "usb:v8087p0AAAd0002dcE0dsc01dp01icE0isc01ip01in00" }, { "index": 41, "attached_to": 42, "class_list": ["usb", "bluetooth"], "bus_type": { "hex": "0086", "name": "USB", "value": 134 }, "slot": { "bus": 0, "number": 0 }, "base_class": { "hex": "0115", "name": "Bluetooth Device", "value": 277 }, "vendor": { "hex": "8087", "value": 32903 }, "device": { "hex": "0aaa", "value": 2730 }, "revision": { "hex": "0000", "name": "0.02", "value": 0 }, "model": "Bluetooth Device", "sysfs_id": "/devices/pci0000:00/0000:00:14.0/usb1/1-10/1-10:1.1", "sysfs_bus_id": "1-10:1.1", "resources": [ { "type": "baud", "speed": 12000000, "bits": 0, "stop_bits": 0, "parity": 0, "handshake": 0 } ], "detail": { "device_class": { "hex": "00e0", "name": "wireless", "value": 224 }, "device_subclass": { "hex": "0001", "name": "audio", "value": 1 }, "device_protocol": 1, "interface_class": { "hex": "00e0", "name": "wireless", "value": 224 }, "interface_subclass": { "hex": "0001", "name": "audio", "value": 1 }, "interface_protocol": 1, "interface_number": 1, "interface_alternate_setting": 0 }, "hotplug": "usb", "driver": "btusb", "driver_module": "btusb", "drivers": ["btusb"], "driver_modules": ["btusb"], "module_alias": "usb:v8087p0AAAd0002dcE0dsc01dp01icE0isc01ip01in01" } ], "bridge": [ { "index": 12, "attached_to": 0, "class_list": ["pci", "bridge"], "bus_type": { "hex": "0004", "name": "PCI", "value": 4 }, "slot": { "bus": 0, "number": 28 }, "base_class": { "hex": "0006", "name": "Bridge", "value": 6 }, "sub_class": { "hex": "0004", "name": "PCI bridge", "value": 4 }, "pci_interface": { "hex": "0000", "name": "Normal decode", "value": 0 }, "vendor": { "hex": "8086", "name": "Intel Corporation", "value": 32902 }, "sub_vendor": { "hex": "17aa", "value": 6058 }, "device": { "hex": "9db8", "value": 40376 }, "sub_device": { "hex": "2279", "value": 8825 }, "revision": { "hex": "00f0", "value": 240 }, "model": "Intel PCI bridge", "sysfs_id": "/devices/pci0000:00/0000:00:1c.0", "sysfs_bus_id": "0000:00:1c.0", "detail": { "function": 0, "command": 1031, "header_type": 1, "secondary_bus": 1, "prog_if": 0 }, "driver": "pcieport", "driver_module": "pcieportdrv", "drivers": ["pcieport"], "driver_modules": ["pcieportdrv"], "module_alias": "pci:v00008086d00009DB8sv000017AAsd00002279bc06sc04i00" }, { "index": 14, "attached_to": 0, "class_list": ["pci", "bridge"], "bus_type": { "hex": "0004", "name": "PCI", "value": 4 }, "slot": { "bus": 0, "number": 31 }, "base_class": { "hex": "0006", "name": "Bridge", "value": 6 }, "sub_class": { "hex": "0001", "name": "ISA bridge", "value": 1 }, "vendor": { "hex": "8086", "name": "Intel Corporation", "value": 32902 }, "sub_vendor": { "hex": "17aa", "value": 6058 }, "device": { "hex": "9d84", "value": 40324 }, "sub_device": { "hex": "2279", "value": 8825 }, "revision": { "hex": "0030", "value": 48 }, "model": "Intel ISA bridge", "sysfs_id": "/devices/pci0000:00/0000:00:1f.0", "sysfs_bus_id": "0000:00:1f.0", "detail": { "function": 0, "command": 7, "header_type": 0, "secondary_bus": 0, "prog_if": 0 }, "module_alias": "pci:v00008086d00009D84sv000017AAsd00002279bc06sc01i00" }, { "index": 21, "attached_to": 0, "class_list": ["pci", "bridge"], "bus_type": { "hex": "0004", "name": "PCI", "value": 4 }, "slot": { "bus": 0, "number": 0 }, "base_class": { "hex": "0006", "name": "Bridge", "value": 6 }, "sub_class": { "hex": "0000", "name": "Host bridge", "value": 0 }, "vendor": { "hex": "8086", "name": "Intel Corporation", "value": 32902 }, "sub_vendor": { "hex": "17aa", "value": 6058 }, "device": { "hex": "3e34", "value": 15924 }, "sub_device": { "hex": "2279", "value": 8825 }, "revision": { "hex": "000c", "value": 12 }, "model": "Intel Host bridge", "sysfs_id": "/devices/pci0000:00/0000:00:00.0", "sysfs_bus_id": "0000:00:00.0", "detail": { "function": 0, "command": 6, "header_type": 0, "secondary_bus": 0, "prog_if": 0 }, "driver": "skl_uncore", "driver_module": "intel_uncore", "drivers": ["skl_uncore"], "driver_modules": ["intel_uncore"], "module_alias": "pci:v00008086d00003E34sv000017AAsd00002279bc06sc00i00" }, { "index": 23, "attached_to": 0, "class_list": ["pci", "bridge"], "bus_type": { "hex": "0004", "name": "PCI", "value": 4 }, "slot": { "bus": 0, "number": 29 }, "base_class": { "hex": "0006", "name": "Bridge", "value": 6 }, "sub_class": { "hex": "0004", "name": "PCI bridge", "value": 4 }, "pci_interface": { "hex": "0000", "name": "Normal decode", "value": 0 }, "vendor": { "hex": "8086", "name": "Intel Corporation", "value": 32902 }, "sub_vendor": { "hex": "17aa", "value": 6058 }, "device": { "hex": "9db4", "value": 40372 }, "sub_device": { "hex": "2279", "value": 8825 }, "revision": { "hex": "00f0", "value": 240 }, "model": "Intel PCI bridge", "sysfs_id": "/devices/pci0000:00/0000:00:1d.4", "sysfs_bus_id": "0000:00:1d.4", "detail": { "function": 4, "command": 1031, "header_type": 1, "secondary_bus": 61, "prog_if": 0 }, "driver": "pcieport", "driver_module": "pcieportdrv", "drivers": ["pcieport"], "driver_modules": ["pcieportdrv"], "module_alias": "pci:v00008086d00009DB4sv000017AAsd00002279bc06sc04i00" }, { "index": 26, "attached_to": 0, "class_list": ["pci", "bridge"], "bus_type": { "hex": "0004", "name": "PCI", "value": 4 }, "slot": { "bus": 0, "number": 29 }, "base_class": { "hex": "0006", "name": "Bridge", "value": 6 }, "sub_class": { "hex": "0004", "name": "PCI bridge", "value": 4 }, "pci_interface": { "hex": "0000", "name": "Normal decode", "value": 0 }, "vendor": { "hex": "8086", "name": "Intel Corporation", "value": 32902 }, "sub_vendor": { "hex": "17aa", "value": 6058 }, "device": { "hex": "9db0", "value": 40368 }, "sub_device": { "hex": "2279", "value": 8825 }, "revision": { "hex": "00f0", "value": 240 }, "model": "Intel PCI bridge", "sysfs_id": "/devices/pci0000:00/0000:00:1d.0", "sysfs_bus_id": "0000:00:1d.0", "detail": { "function": 0, "command": 1031, "header_type": 1, "secondary_bus": 60, "prog_if": 0 }, "driver": "pcieport", "driver_module": "pcieportdrv", "drivers": ["pcieport"], "driver_modules": ["pcieportdrv"], "module_alias": "pci:v00008086d00009DB0sv000017AAsd00002279bc06sc04i00" }, { "index": 29, "attached_to": 0, "class_list": ["pci", "bridge"], "bus_type": { "hex": "0004", "name": "PCI", "value": 4 }, "slot": { "bus": 0, "number": 28 }, "base_class": { "hex": "0006", "name": "Bridge", "value": 6 }, "sub_class": { "hex": "0004", "name": "PCI bridge", "value": 4 }, "pci_interface": { "hex": "0000", "name": "Normal decode", "value": 0 }, "vendor": { "hex": "8086", "name": "Intel Corporation", "value": 32902 }, "sub_vendor": { "hex": "17aa", "value": 6058 }, "device": { "hex": "9dbc", "value": 40380 }, "sub_device": { "hex": "2279", "value": 8825 }, "revision": { "hex": "00f0", "value": 240 }, "model": "Intel PCI bridge", "sysfs_id": "/devices/pci0000:00/0000:00:1c.4", "sysfs_bus_id": "0000:00:1c.4", "detail": { "function": 4, "command": 1031, "header_type": 1, "secondary_bus": 2, "prog_if": 0 }, "driver": "pcieport", "driver_module": "pcieportdrv", "drivers": ["pcieport"], "driver_modules": ["pcieportdrv"], "module_alias": "pci:v00008086d00009DBCsv000017AAsd00002279bc06sc04i00" } ], "camera": [ { "index": 38, "attached_to": 42, "class_list": ["camera", "usb"], "bus_type": { "hex": "0086", "name": "USB", "value": 134 }, "slot": { "bus": 0, "number": 0 }, "base_class": { "hex": "010f", "name": "Camera", "value": 271 }, "vendor": { "hex": "30c9", "name": "8SSC20X55495L1GZ05W0SA1", "value": 12489 }, "device": { "hex": "001b", "name": "Integrated Camera", "value": 27 }, "revision": { "hex": "0000", "name": "0.02", "value": 0 }, "serial": "0001", "model": "8SSC20X55495L1GZ05W0SA1 Integrated Camera", "sysfs_id": "/devices/pci0000:00/0000:00:14.0/usb1/1-8/1-8:1.1", "sysfs_bus_id": "1-8:1.1", "resources": [ { "type": "baud", "speed": 480000000, "bits": 0, "stop_bits": 0, "parity": 0, "handshake": 0 } ], "detail": { "device_class": { "hex": "00ef", "name": "miscellaneous", "value": 239 }, "device_subclass": { "hex": "0002", "name": "comm", "value": 2 }, "device_protocol": 1, "interface_class": { "hex": "000e", "name": "video", "value": 14 }, "interface_subclass": { "hex": "0002", "name": "comm", "value": 2 }, "interface_protocol": 0, "interface_number": 1, "interface_alternate_setting": 0, "interface_association": { "function_class": { "hex": "000e", "name": "video", "value": 14 }, "function_subclass": { "hex": "0003", "name": "hid", "value": 3 }, "function_protocol": 0, "interface_count": 2, "first_interface": 0 } }, "hotplug": "usb", "driver": "uvcvideo", "driver_module": "uvcvideo", "drivers": ["uvcvideo"], "driver_modules": ["uvcvideo"], "module_alias": "usb:v30C9p001Bd0002dcEFdsc02dp01ic0Eisc02ip00in01" }, { "index": 43, "attached_to": 42, "class_list": ["camera", "usb"], "bus_type": { "hex": "0086", "name": "USB", "value": 134 }, "slot": { "bus": 0, "number": 0 }, "base_class": { "hex": "010f", "name": "Camera", "value": 271 }, "vendor": { "hex": "30c9", "name": "8SSC20X55495L1GZ05W0SA1", "value": 12489 }, "device": { "hex": "001b", "name": "Integrated Camera", "value": 27 }, "revision": { "hex": "0000", "name": "0.02", "value": 0 }, "serial": "0001", "model": "8SSC20X55495L1GZ05W0SA1 Integrated Camera", "sysfs_id": "/devices/pci0000:00/0000:00:14.0/usb1/1-8/1-8:1.0", "sysfs_bus_id": "1-8:1.0", "resources": [ { "type": "baud", "speed": 480000000, "bits": 0, "stop_bits": 0, "parity": 0, "handshake": 0 } ], "detail": { "device_class": { "hex": "00ef", "name": "miscellaneous", "value": 239 }, "device_subclass": { "hex": "0002", "name": "comm", "value": 2 }, "device_protocol": 1, "interface_class": { "hex": "000e", "name": "video", "value": 14 }, "interface_subclass": { "hex": "0001", "name": "audio", "value": 1 }, "interface_protocol": 0, "interface_number": 0, "interface_alternate_setting": 0, "interface_association": { "function_class": { "hex": "000e", "name": "video", "value": 14 }, "function_subclass": { "hex": "0003", "name": "hid", "value": 3 }, "function_protocol": 0, "interface_count": 2, "first_interface": 0 } }, "hotplug": "usb", "driver": "uvcvideo", "driver_module": "uvcvideo", "drivers": ["uvcvideo"], "driver_modules": ["uvcvideo"], "module_alias": "usb:v30C9p001Bd0002dcEFdsc02dp01ic0Eisc01ip00in00" } ], "chip_card": [ { "index": 45, "attached_to": 42, "class_list": ["chip_card", "usb"], "bus_type": { "hex": "0086", "name": "USB", "value": 134 }, "slot": { "bus": 0, "number": 0 }, "base_class": { "hex": "010e", "name": "Chipcard Reader", "value": 270 }, "vendor": { "hex": "058f", "name": "Alcor Micro, Inc.", "value": 1423 }, "device": { "hex": "9540", "name": "EMV Smartcard Reader", "value": 38208 }, "revision": { "hex": "0000", "name": "1.20", "value": 0 }, "model": "Alcor Micro EMV Smartcard Reader", "sysfs_id": "/devices/pci0000:00/0000:00:14.0/usb1/1-1/1-1:1.0", "sysfs_bus_id": "1-1:1.0", "resources": [ { "type": "baud", "speed": 12000000, "bits": 0, "stop_bits": 0, "parity": 0, "handshake": 0 } ], "detail": { "device_class": { "hex": "0000", "name": "per_interface", "value": 0 }, "device_subclass": { "hex": "0000", "name": "per_interface", "value": 0 }, "device_protocol": 0, "interface_class": { "hex": "000b", "name": "smart_card", "value": 11 }, "interface_subclass": { "hex": "0000", "name": "per_interface", "value": 0 }, "interface_protocol": 0, "interface_number": 0, "interface_alternate_setting": 0 }, "hotplug": "usb", "module_alias": "usb:v058Fp9540d0120dc00dsc00dp00ic0Bisc00ip00in00" } ], "cpu": [ { "architecture": "x86_64", "vendor_name": "GenuineIntel", "family": 6, "model": 142, "stepping": 12, "features": [ "fpu", "vme", "de", "pse", "tsc", "msr", "pae", "mce", "cx8", "apic", "sep", "mtrr", "pge", "mca", "cmov", "pat", "pse36", "clflush", "dts", "acpi", "mmx", "fxsr", "sse", "sse2", "ss", "ht", "tm", "pbe", "syscall", "nx", "pdpe1gb", "rdtscp", "lm", "constant_tsc", "art", "arch_perfmon", "pebs", "bts", "rep_good", "nopl", "xtopology", "nonstop_tsc", "cpuid", "aperfmperf", "pni", "pclmulqdq", "dtes64", "monitor", "ds_cpl", "vmx", "est", "tm2", "ssse3", "sdbg", "fma", "cx16", "xtpr", "pdcm", "pcid", "sse4_1", "sse4_2", "x2apic", "movbe", "popcnt", "tsc_deadline_timer", "aes", "xsave", "avx", "f16c", "rdrand", "lahf_lm", "abm", "3dnowprefetch", "cpuid_fault", "epb", "ssbd", "ibrs", "ibpb", "stibp", "ibrs_enhanced", "tpr_shadow", "flexpriority", "ept", "vpid", "ept_ad", "fsgsbase", "tsc_adjust", "bmi1", "avx2", "smep", "bmi2", "erms", "invpcid", "mpx", "rdseed", "adx", "smap", "clflushopt", "intel_pt", "xsaveopt", "xsavec", "xgetbv1", "xsaves", "dtherm", "ida", "arat", "pln", "pts", "hwp", "hwp_notify", "hwp_act_window", "hwp_epp", "vnmi", "md_clear", "flush_l1d", "arch_capabilities" ], "bugs": [ "spectre_v1", "spectre_v2", "spec_store_bypass", "swapgs", "itlb_multihit", "srbds", "mmio_stale_data", "retbleed", "eibrs_pbrsb", "gds", "bhi", "spectre_v2_user", "old_microcode", "its", "vmscape" ], "bogo": 3999, "cache": 8192, "units": 16, "physical_id": 0, "siblings": 8, "cores": 4, "fpu": true, "fpu_exception": true, "cpuid_level": 22, "write_protect": false, "tlb_size": 32766, "clflush_size": 64, "cache_alignment": 64, "address_sizes": { "physical": "0x27", "virtual": "0x30" } } ], "disk": [ { "index": 34, "attached_to": 25, "class_list": ["disk", "block_device", "nvme"], "bus_type": { "hex": "0096", "name": "NVME", "value": 150 }, "slot": { "bus": 0, "number": 0 }, "base_class": { "hex": "0106", "name": "Mass Storage Device", "value": 262 }, "sub_class": { "hex": "0000", "name": "Disk", "value": 0 }, "vendor": { "hex": "1e4b", "value": 7755 }, "sub_vendor": { "hex": "1e4b", "value": 7755 }, "device": { "hex": "1202", "name": "512GB PCIe SSD", "value": 4610 }, "sub_device": { "hex": "1202", "value": 4610 }, "serial": "2024121103000964", "model": "512GB PCIe SSD", "sysfs_id": "/class/block/nvme0n1", "sysfs_bus_id": "nvme0", "sysfs_device_link": "/devices/pci0000:00/0000:00:1d.4/0000:3d:00.0/nvme/nvme0", "unix_device_names": [ "/dev/disk/by-id/nvme-512GB_PCIe_SSD_2024121103000964", "/dev/disk/by-id/nvme-512GB_PCIe_SSD_2024121103000964_1", "/dev/disk/by-id/nvme-nvme.1e4b-32303234313231313033303030393634-3531324742205043496520535344-00000001", "/dev/disk/by-path/pci-0000:3d:00.0-nvme-1", "/dev/nvme0n1" ], "resources": [ { "type": "disk_geo", "cylinders": 488386, "heads": 64, "sectors": 32, "size": "0x0", "geo_type": "logical" }, { "type": "size", "unit": "sectors", "value_1": 1000215216, "value_2": 512 } ], "driver": "nvme", "driver_module": "nvme", "drivers": ["nvme"], "driver_modules": ["nvme"] }, { "index": 35, "attached_to": 31, "class_list": ["disk", "usb", "scsi", "block_device"], "bus_type": { "hex": "0084", "name": "SCSI", "value": 132 }, "slot": { "bus": 0, "number": 0 }, "base_class": { "hex": "0106", "name": "Mass Storage Device", "value": 262 }, "sub_class": { "hex": "0000", "name": "Disk", "value": 0 }, "vendor": { "hex": "0781", "name": "USB", "value": 1921 }, "device": { "hex": "5583", "name": "SanDisk 3.2Gen1", "value": 21891 }, "revision": { "hex": "0000", "name": "1.00", "value": 0 }, "serial": "01017a33f8c1d6bbbfe7", "model": "USB SanDisk 3.2Gen1", "sysfs_id": "/class/block/sda", "sysfs_bus_id": "0:0:0:0", "sysfs_device_link": "/devices/pci0000:00/0000:00:14.0/usb2/2-4/2-4:1.0/host0/target0:0:0/0:0:0:0", "unix_device_names": [ "/dev/disk/by-id/usb-USB_SanDisk_3.2Gen1_01017a33f8c1d6bbbfe70a7b26cd37feae47d7bdcd66539fbda070dffc8874bcaa990000000000000000000020b7a975008a4b00835581076e2f52e7-0:0", "/dev/disk/by-path/pci-0000:00:14.0-usb-0:4:1.0-scsi-0:0:0:0", "/dev/disk/by-path/pci-0000:00:14.0-usbv3-0:4:1.0-scsi-0:0:0:0", "/dev/sda" ], "unix_device_name2": "/dev/sg0", "resources": [ { "type": "disk_geo", "cylinders": 58656, "heads": 64, "sectors": 32, "size": "0x0", "geo_type": "logical" }, { "type": "size", "unit": "sectors", "value_1": 120127488, "value_2": 512 } ], "driver": "usb-storage", "driver_module": "usb_storage", "drivers": ["sd", "usb-storage"], "driver_modules": ["sd_mod", "usb_storage"], "module_alias": "usb:v0781p5583d0100dc00dsc00dp00ic08isc06ip50in00" } ], "graphics_card": [ { "index": 30, "attached_to": 0, "class_list": ["graphics_card", "pci"], "bus_type": { "hex": "0004", "name": "PCI", "value": 4 }, "slot": { "bus": 0, "number": 2 }, "base_class": { "hex": "0003", "name": "Display controller", "value": 3 }, "sub_class": { "hex": "0000", "name": "VGA compatible controller", "value": 0 }, "pci_interface": { "hex": "0000", "name": "VGA", "value": 0 }, "vendor": { "hex": "8086", "name": "Intel Corporation", "value": 32902 }, "sub_vendor": { "hex": "17aa", "value": 6058 }, "device": { "hex": "3ea0", "value": 16032 }, "sub_device": { "hex": "2279", "value": 8825 }, "revision": { "hex": "0002", "value": 2 }, "model": "Intel VGA compatible controller", "sysfs_id": "/devices/pci0000:00/0000:00:02.0", "sysfs_bus_id": "0000:00:02.0", "resources": [ { "type": "io", "base": 12288, "range": 64, "enabled": true, "access": "read_write" } ], "detail": { "function": 0, "command": 1031, "header_type": 0, "secondary_bus": 0, "prog_if": 0 }, "driver": "i915", "driver_module": "i915", "drivers": ["i915"], "driver_modules": ["i915"], "module_alias": "pci:v00008086d00003EA0sv000017AAsd00002279bc03sc00i00" } ], "hub": [ { "index": 42, "attached_to": 31, "class_list": ["usb", "hub"], "bus_type": { "hex": "0086", "name": "USB", "value": 134 }, "slot": { "bus": 0, "number": 0 }, "base_class": { "hex": "010a", "name": "Hub", "value": 266 }, "vendor": { "hex": "1d6b", "name": "Linux 6.16.7 xhci-hcd", "value": 7531 }, "device": { "hex": "0002", "name": "xHCI Host Controller", "value": 2 }, "revision": { "hex": "0000", "name": "6.16", "value": 0 }, "serial": "0000:00:14.0", "model": "Linux 6.16.7 xhci-hcd xHCI Host Controller", "sysfs_id": "/devices/pci0000:00/0000:00:14.0/usb1/1-0:1.0", "sysfs_bus_id": "1-0:1.0", "resources": [ { "type": "baud", "speed": 480000000, "bits": 0, "stop_bits": 0, "parity": 0, "handshake": 0 } ], "detail": { "device_class": { "hex": "0009", "name": "hub", "value": 9 }, "device_subclass": { "hex": "0000", "name": "per_interface", "value": 0 }, "device_protocol": 1, "interface_class": { "hex": "0009", "name": "hub", "value": 9 }, "interface_subclass": { "hex": "0000", "name": "per_interface", "value": 0 }, "interface_protocol": 0, "interface_number": 0, "interface_alternate_setting": 0 }, "hotplug": "usb", "driver": "hub", "driver_module": "usbcore", "drivers": ["hub"], "driver_modules": ["usbcore"], "module_alias": "usb:v1D6Bp0002d0616dc09dsc00dp01ic09isc00ip00in00" }, { "index": 47, "attached_to": 31, "class_list": ["usb", "hub"], "bus_type": { "hex": "0086", "name": "USB", "value": 134 }, "slot": { "bus": 0, "number": 0 }, "base_class": { "hex": "010a", "name": "Hub", "value": 266 }, "vendor": { "hex": "1d6b", "name": "Linux 6.16.7 xhci-hcd", "value": 7531 }, "device": { "hex": "0003", "name": "xHCI Host Controller", "value": 3 }, "revision": { "hex": "0000", "name": "6.16", "value": 0 }, "serial": "0000:00:14.0", "model": "Linux 6.16.7 xhci-hcd xHCI Host Controller", "sysfs_id": "/devices/pci0000:00/0000:00:14.0/usb2/2-0:1.0", "sysfs_bus_id": "2-0:1.0", "detail": { "device_class": { "hex": "0009", "name": "hub", "value": 9 }, "device_subclass": { "hex": "0000", "name": "per_interface", "value": 0 }, "device_protocol": 3, "interface_class": { "hex": "0009", "name": "hub", "value": 9 }, "interface_subclass": { "hex": "0000", "name": "per_interface", "value": 0 }, "interface_protocol": 0, "interface_number": 0, "interface_alternate_setting": 0 }, "hotplug": "usb", "driver": "hub", "driver_module": "usbcore", "drivers": ["hub"], "driver_modules": ["usbcore"], "module_alias": "usb:v1D6Bp0003d0616dc09dsc00dp03ic09isc00ip00in00" } ], "memory": [ { "index": 11, "attached_to": 0, "class_list": ["memory"], "base_class": { "hex": "0101", "name": "Internally Used Class", "value": 257 }, "sub_class": { "hex": "0002", "name": "Main Memory", "value": 2 }, "model": "Main Memory", "resources": [ { "type": "phys_mem", "range": 25769803776 } ] } ], "monitor": [ { "index": 33, "attached_to": 30, "class_list": ["monitor"], "base_class": { "hex": "0100", "name": "Monitor", "value": 256 }, "sub_class": { "hex": "0002", "name": "LCD Monitor", "value": 2 }, "vendor": { "hex": "30e4", "name": "LG Display", "value": 12516 }, "device": { "hex": "05fa", "value": 1530 }, "serial": "0", "model": "LG Display LCD Monitor", "resources": [ { "type": "monitor", "width": 1920, "height": 1080, "vertical_frequency": 60, "interlaced": false }, { "type": "size", "unit": "mm", "value_1": 309, "value_2": 174 } ], "detail": { "manufacture_year": 2018, "manufacture_week": 0, "vertical_sync": { "min": 0, "max": 0 }, "horizontal_sync": { "min": 0, "max": 0 }, "horizontal_sync_timings": { "disp": 1920, "sync_start": 1968, "sync_end": 2000, "total": 2164 }, "vertical_sync_timings": { "disp": 1080, "sync_start": 1083, "sync_end": 1088, "total": 1102 }, "clock": 114460, "width": 1920, "height": 1080, "width_millimetres": 309, "height_millimetres": 174, "horizontal_flag": 45, "vertical_flag": 43, "vendor": "LG Display", "name": "" } } ], "mouse": [ { "index": 52, "attached_to": 0, "bus_type": { "hex": "0080", "name": "ps2", "value": 128 }, "base_class": { "hex": "0118", "name": "touchpad", "value": 280 }, "sub_class": { "hex": "0000", "name": "ps2", "value": 0 }, "vendor": { "hex": "0002", "value": 2 }, "device": { "hex": "0007", "value": 7 }, "sysfs_id": "/devices/platform/i8042/serio1/input/input5", "unix_device_names": ["/dev/input/event5", "/dev/input/ + handler"] } ], "network_controller": [ { "index": 16, "attached_to": 0, "class_list": ["network_controller", "pci", "wlan_card"], "bus_type": { "hex": "0004", "name": "PCI", "value": 4 }, "slot": { "bus": 0, "number": 20 }, "base_class": { "hex": "0002", "name": "Network controller", "value": 2 }, "sub_class": { "hex": "0082", "name": "WLAN controller", "value": 130 }, "vendor": { "hex": "8086", "name": "Intel Corporation", "value": 32902 }, "sub_vendor": { "hex": "8086", "name": "Intel Corporation", "value": 32902 }, "device": { "hex": "9df0", "value": 40432 }, "sub_device": { "hex": "0034", "value": 52 }, "revision": { "hex": "0030", "value": 48 }, "model": "Intel WLAN controller", "sysfs_id": "/devices/pci0000:00/0000:00:14.3", "sysfs_bus_id": "0000:00:14.3", "unix_device_names": ["wlan0"], "resources": [ { "type": "hwaddr", "address": 50 }, { "type": "phwaddr", "address": 99 }, { "type": "wlan", "channels": [ "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "36", "40", "44", "48", "52", "56", "60", "64", "100", "104", "108", "112", "116", "120", "124", "128", "132", "136", "140" ], "frequencies": [ "2.412", "2.417", "2.422", "2.427", "2.432", "2.437", "2.442", "2.447", "2.452", "2.457", "2.462", "2.467", "2.472", "5.18", "5.2", "5.22", "5.24", "5.26", "5.28", "5.3", "5.32", "5.5", "5.52", "5.54", "5.56", "5.58", "5.6", "5.62", "5.64", "5.66", "5.68", "5.7" ], "auth_modes": ["open", "sharedkey", "wpa-psk", "wpa-eap"], "enc_modes": ["WEP40", "WEP104", "TKIP", "CCMP"] } ], "detail": { "function": 3, "command": 1030, "header_type": 0, "secondary_bus": 0, "prog_if": 0 }, "driver": "iwlwifi", "driver_module": "iwlwifi", "drivers": ["iwlwifi"], "driver_modules": ["iwlwifi"], "module_alias": "pci:v00008086d00009DF0sv00008086sd00000034bc02sc80i00" }, { "index": 28, "attached_to": 0, "class_list": ["network_controller", "pci"], "bus_type": { "hex": "0004", "name": "PCI", "value": 4 }, "slot": { "bus": 0, "number": 31 }, "base_class": { "hex": "0002", "name": "Network controller", "value": 2 }, "sub_class": { "hex": "0000", "name": "Ethernet controller", "value": 0 }, "vendor": { "hex": "8086", "name": "Intel Corporation", "value": 32902 }, "sub_vendor": { "hex": "17aa", "value": 6058 }, "device": { "hex": "15be", "value": 5566 }, "sub_device": { "hex": "2279", "value": 8825 }, "revision": { "hex": "0030", "value": 48 }, "model": "Intel Ethernet controller", "sysfs_id": "/devices/pci0000:00/0000:00:1f.6", "sysfs_bus_id": "0000:00:1f.6", "unix_device_names": ["enp0s31f6"], "resources": [ { "type": "hwaddr", "address": 48 }, { "type": "phwaddr", "address": 48 } ], "detail": { "function": 6, "command": 1030, "header_type": 0, "secondary_bus": 0, "prog_if": 0 }, "driver": "e1000e", "driver_module": "e1000e", "drivers": ["e1000e"], "driver_modules": ["e1000e"], "module_alias": "pci:v00008086d000015BEsv000017AAsd00002279bc02sc00i00" }, { "index": 40, "attached_to": 42, "class_list": ["network_controller", "usb"], "bus_type": { "hex": "0086", "name": "USB", "value": 134 }, "slot": { "bus": 0, "number": 0 }, "base_class": { "hex": "0002", "name": "Network controller", "value": 2 }, "sub_class": { "hex": "0000", "name": "Ethernet controller", "value": 0 }, "vendor": { "hex": "2cb7", "name": "FIBOCOM", "value": 11447 }, "device": { "hex": "0210", "name": "L830-EB", "value": 528 }, "revision": { "hex": "0000", "name": "3.33", "value": 0 }, "serial": "004999010640000", "model": "FIBOCOM L830-EB", "sysfs_id": "/devices/pci0000:00/0000:00:14.0/usb1/1-7/1-7:1.0", "sysfs_bus_id": "1-7:1.0", "unix_device_names": ["wwp0s20f0u7"], "resources": [ { "type": "baud", "speed": 480000000, "bits": 0, "stop_bits": 0, "parity": 0, "handshake": 0 }, { "type": "hwaddr", "address": 99 } ], "detail": { "device_class": { "hex": "00ef", "name": "miscellaneous", "value": 239 }, "device_subclass": { "hex": "0002", "name": "comm", "value": 2 }, "device_protocol": 1, "interface_class": { "hex": "0002", "name": "comm", "value": 2 }, "interface_subclass": { "hex": "000e", "name": "video", "value": 14 }, "interface_protocol": 0, "interface_number": 0, "interface_alternate_setting": 0, "interface_association": { "function_class": { "hex": "0002", "name": "comm", "value": 2 }, "function_subclass": { "hex": "000e", "name": "video", "value": 14 }, "function_protocol": 0, "interface_count": 2, "first_interface": 0 } }, "hotplug": "usb", "driver": "cdc_mbim", "driver_module": "cdc_mbim", "drivers": ["cdc_mbim"], "driver_modules": ["cdc_mbim"], "module_alias": "usb:v2CB7p0210d0333dcEFdsc02dp01ic02isc0Eip00in00" } ], "network_interface": [ { "index": 48, "attached_to": 40, "class_list": ["network_interface"], "base_class": { "hex": "0107", "name": "Network Interface", "value": 263 }, "sub_class": { "hex": "0001", "name": "Ethernet", "value": 1 }, "model": "Ethernet network interface", "sysfs_id": "/class/net/wwp0s20f0u7", "sysfs_device_link": "/devices/pci0000:00/0000:00:14.0/usb1/1-7/1-7:1.0", "unix_device_names": ["wwp0s20f0u7"], "resources": [ { "type": "hwaddr", "address": 99 } ], "driver": "cdc_mbim", "driver_module": "cdc_mbim", "drivers": ["cdc_mbim"], "driver_modules": ["cdc_mbim"] }, { "index": 49, "attached_to": 28, "class_list": ["network_interface"], "base_class": { "hex": "0107", "name": "Network Interface", "value": 263 }, "sub_class": { "hex": "0001", "name": "Ethernet", "value": 1 }, "model": "Ethernet network interface", "sysfs_id": "/class/net/enp0s31f6", "sysfs_device_link": "/devices/pci0000:00/0000:00:1f.6", "unix_device_names": ["enp0s31f6"], "resources": [ { "type": "hwaddr", "address": 48 }, { "type": "phwaddr", "address": 48 } ], "driver": "e1000e", "driver_module": "e1000e", "drivers": ["e1000e"], "driver_modules": ["e1000e"] }, { "index": 50, "attached_to": 16, "class_list": ["network_interface"], "base_class": { "hex": "0107", "name": "Network Interface", "value": 263 }, "sub_class": { "hex": "000a", "name": "WLAN", "value": 10 }, "model": "WLAN network interface", "sysfs_id": "/class/net/wlan0", "sysfs_device_link": "/devices/pci0000:00/0000:00:14.3", "unix_device_names": ["wlan0"], "resources": [ { "type": "hwaddr", "address": 50 }, { "type": "phwaddr", "address": 99 } ], "driver": "iwlwifi", "driver_module": "iwlwifi", "drivers": ["iwlwifi"], "driver_modules": ["iwlwifi"] }, { "index": 51, "attached_to": 0, "class_list": ["network_interface"], "base_class": { "hex": "0107", "name": "Network Interface", "value": 263 }, "sub_class": { "hex": "0000", "name": "Loopback", "value": 0 }, "model": "Loopback network interface", "sysfs_id": "/class/net/lo", "unix_device_names": ["lo"] } ], "pci": [ { "index": 13, "attached_to": 0, "class_list": ["pci", "unknown"], "bus_type": { "hex": "0004", "name": "PCI", "value": 4 }, "slot": { "bus": 0, "number": 8 }, "base_class": { "hex": "0008", "name": "Generic system peripheral", "value": 8 }, "sub_class": { "hex": "0080", "name": "System peripheral", "value": 128 }, "vendor": { "hex": "8086", "name": "Intel Corporation", "value": 32902 }, "sub_vendor": { "hex": "17aa", "value": 6058 }, "device": { "hex": "1911", "value": 6417 }, "sub_device": { "hex": "2279", "value": 8825 }, "model": "Intel System peripheral", "sysfs_id": "/devices/pci0000:00/0000:00:08.0", "sysfs_bus_id": "0000:00:08.0", "detail": { "function": 0, "command": 0, "header_type": 0, "secondary_bus": 0, "prog_if": 0 }, "module_alias": "pci:v00008086d00001911sv000017AAsd00002279bc08sc80i00" }, { "index": 15, "attached_to": 0, "class_list": ["pci", "unknown"], "bus_type": { "hex": "0004", "name": "PCI", "value": 4 }, "slot": { "bus": 0, "number": 4 }, "base_class": { "hex": "0011", "name": "Signal processing controller", "value": 17 }, "sub_class": { "hex": "0080", "name": "Signal processing controller", "value": 128 }, "vendor": { "hex": "8086", "name": "Intel Corporation", "value": 32902 }, "sub_vendor": { "hex": "17aa", "value": 6058 }, "device": { "hex": "1903", "value": 6403 }, "sub_device": { "hex": "2279", "value": 8825 }, "revision": { "hex": "000c", "value": 12 }, "model": "Intel Signal processing controller", "sysfs_id": "/devices/pci0000:00/0000:00:04.0", "sysfs_bus_id": "0000:00:04.0", "detail": { "function": 0, "command": 2, "header_type": 0, "secondary_bus": 0, "prog_if": 0 }, "driver": "proc_thermal", "driver_module": "processor_thermal_device_pci_legacy", "drivers": ["proc_thermal"], "driver_modules": ["processor_thermal_device_pci_legacy"], "module_alias": "pci:v00008086d00001903sv000017AAsd00002279bc11sc80i00" }, { "index": 17, "attached_to": 0, "class_list": ["pci", "unknown"], "bus_type": { "hex": "0004", "name": "PCI", "value": 4 }, "slot": { "bus": 0, "number": 22 }, "base_class": { "hex": "0007", "name": "Communication controller", "value": 7 }, "sub_class": { "hex": "0080", "name": "Communication controller", "value": 128 }, "vendor": { "hex": "8086", "name": "Intel Corporation", "value": 32902 }, "sub_vendor": { "hex": "17aa", "value": 6058 }, "device": { "hex": "9de0", "value": 40416 }, "sub_device": { "hex": "2279", "value": 8825 }, "revision": { "hex": "0030", "value": 48 }, "model": "Intel Communication controller", "sysfs_id": "/devices/pci0000:00/0000:00:16.0", "sysfs_bus_id": "0000:00:16.0", "detail": { "function": 0, "command": 1030, "header_type": 0, "secondary_bus": 0, "prog_if": 0 }, "driver": "mei_me", "driver_module": "mei_me", "drivers": ["mei_me"], "driver_modules": ["mei_me"], "module_alias": "pci:v00008086d00009DE0sv000017AAsd00002279bc07sc80i00" }, { "index": 18, "attached_to": 12, "class_list": ["pci", "unknown"], "bus_type": { "hex": "0004", "name": "PCI", "value": 4 }, "slot": { "bus": 1, "number": 0 }, "base_class": { "hex": "00ff", "name": "Unclassified device", "value": 255 }, "vendor": { "hex": "10ec", "value": 4332 }, "sub_vendor": { "hex": "17aa", "value": 6058 }, "device": { "hex": "522a", "value": 21034 }, "sub_device": { "hex": "2279", "value": 8825 }, "revision": { "hex": "0001", "value": 1 }, "model": "Unclassified device", "sysfs_id": "/devices/pci0000:00/0000:00:1c.0/0000:01:00.0", "sysfs_bus_id": "0000:01:00.0", "detail": { "function": 0, "command": 1030, "header_type": 0, "secondary_bus": 0, "prog_if": 0 }, "driver": "rtsx_pci", "driver_module": "rtsx_pci", "drivers": ["rtsx_pci"], "driver_modules": ["rtsx_pci"], "module_alias": "pci:v000010ECd0000522Asv000017AAsd00002279bcFFsc00i00" }, { "index": 19, "attached_to": 0, "class_list": ["pci", "unknown"], "bus_type": { "hex": "0004", "name": "PCI", "value": 4 }, "slot": { "bus": 0, "number": 31 }, "base_class": { "hex": "000c", "name": "Serial bus controller", "value": 12 }, "sub_class": { "hex": "0080", "value": 128 }, "vendor": { "hex": "8086", "name": "Intel Corporation", "value": 32902 }, "sub_vendor": { "hex": "17aa", "value": 6058 }, "device": { "hex": "9da4", "value": 40356 }, "sub_device": { "hex": "2279", "value": 8825 }, "revision": { "hex": "0030", "value": 48 }, "model": "Intel Serial bus controller", "sysfs_id": "/devices/pci0000:00/0000:00:1f.5", "sysfs_bus_id": "0000:00:1f.5", "detail": { "function": 5, "command": 1026, "header_type": 0, "secondary_bus": 0, "prog_if": 0 }, "driver": "intel-spi", "driver_module": "spi_intel_pci", "drivers": ["intel-spi"], "driver_modules": ["spi_intel_pci"], "module_alias": "pci:v00008086d00009DA4sv000017AAsd00002279bc0Csc80i00" }, { "index": 22, "attached_to": 0, "class_list": ["pci", "unknown"], "bus_type": { "hex": "0004", "name": "PCI", "value": 4 }, "slot": { "bus": 0, "number": 18 }, "base_class": { "hex": "0011", "name": "Signal processing controller", "value": 17 }, "sub_class": { "hex": "0080", "name": "Signal processing controller", "value": 128 }, "vendor": { "hex": "8086", "name": "Intel Corporation", "value": 32902 }, "sub_vendor": { "hex": "17aa", "value": 6058 }, "device": { "hex": "9df9", "value": 40441 }, "sub_device": { "hex": "2279", "value": 8825 }, "revision": { "hex": "0030", "value": 48 }, "model": "Intel Signal processing controller", "sysfs_id": "/devices/pci0000:00/0000:00:12.0", "sysfs_bus_id": "0000:00:12.0", "detail": { "function": 0, "command": 2, "header_type": 0, "secondary_bus": 0, "prog_if": 0 }, "driver": "intel_pch_thermal", "driver_module": "intel_pch_thermal", "drivers": ["intel_pch_thermal"], "driver_modules": ["intel_pch_thermal"], "module_alias": "pci:v00008086d00009DF9sv000017AAsd00002279bc11sc80i00" }, { "index": 24, "attached_to": 0, "class_list": ["pci", "unknown"], "bus_type": { "hex": "0004", "name": "PCI", "value": 4 }, "slot": { "bus": 0, "number": 21 }, "base_class": { "hex": "000c", "name": "Serial bus controller", "value": 12 }, "sub_class": { "hex": "0080", "value": 128 }, "vendor": { "hex": "8086", "name": "Intel Corporation", "value": 32902 }, "sub_vendor": { "hex": "17aa", "value": 6058 }, "device": { "hex": "9de8", "value": 40424 }, "sub_device": { "hex": "2279", "value": 8825 }, "revision": { "hex": "0030", "value": 48 }, "model": "Intel Serial bus controller", "sysfs_id": "/devices/pci0000:00/0000:00:15.0", "sysfs_bus_id": "0000:00:15.0", "detail": { "function": 0, "command": 6, "header_type": 0, "secondary_bus": 0, "prog_if": 0 }, "driver": "intel-lpss", "driver_module": "intel_lpss_pci", "drivers": ["intel-lpss"], "driver_modules": ["intel_lpss_pci"], "module_alias": "pci:v00008086d00009DE8sv000017AAsd00002279bc0Csc80i00" }, { "index": 27, "attached_to": 0, "class_list": ["pci", "unknown"], "bus_type": { "hex": "0004", "name": "PCI", "value": 4 }, "slot": { "bus": 0, "number": 20 }, "base_class": { "hex": "0005", "name": "Memory controller", "value": 5 }, "sub_class": { "hex": "0000", "name": "RAM memory", "value": 0 }, "vendor": { "hex": "8086", "name": "Intel Corporation", "value": 32902 }, "sub_vendor": { "hex": "17aa", "value": 6058 }, "device": { "hex": "9def", "value": 40431 }, "sub_device": { "hex": "2279", "value": 8825 }, "revision": { "hex": "0030", "value": 48 }, "model": "Intel RAM memory", "sysfs_id": "/devices/pci0000:00/0000:00:14.2", "sysfs_bus_id": "0000:00:14.2", "detail": { "function": 2, "command": 0, "header_type": 0, "secondary_bus": 0, "prog_if": 0 }, "module_alias": "pci:v00008086d00009DEFsv000017AAsd00002279bc05sc00i00" }, { "index": 32, "attached_to": 0, "class_list": ["pci", "unknown"], "bus_type": { "hex": "0004", "name": "PCI", "value": 4 }, "slot": { "bus": 0, "number": 31 }, "base_class": { "hex": "000c", "name": "Serial bus controller", "value": 12 }, "sub_class": { "hex": "0005", "name": "SMBus", "value": 5 }, "vendor": { "hex": "8086", "name": "Intel Corporation", "value": 32902 }, "sub_vendor": { "hex": "17aa", "value": 6058 }, "device": { "hex": "9da3", "value": 40355 }, "sub_device": { "hex": "2279", "value": 8825 }, "revision": { "hex": "0030", "value": 48 }, "model": "Intel SMBus", "sysfs_id": "/devices/pci0000:00/0000:00:1f.4", "sysfs_bus_id": "0000:00:1f.4", "resources": [ { "type": "io", "base": 61344, "range": 32, "enabled": true, "access": "read_write" } ], "detail": { "function": 4, "command": 3, "header_type": 0, "secondary_bus": 0, "prog_if": 0 }, "driver": "i801_smbus", "driver_module": "i2c_i801", "drivers": ["i801_smbus"], "driver_modules": ["i2c_i801"], "module_alias": "pci:v00008086d00009DA3sv000017AAsd00002279bc0Csc05i00" } ], "sound": [ { "index": 20, "attached_to": 0, "class_list": ["sound", "pci"], "bus_type": { "hex": "0004", "name": "PCI", "value": 4 }, "slot": { "bus": 0, "number": 31 }, "base_class": { "hex": "0004", "name": "Multimedia controller", "value": 4 }, "sub_class": { "hex": "0003", "value": 3 }, "pci_interface": { "hex": "0080", "value": 128 }, "vendor": { "hex": "8086", "name": "Intel Corporation", "value": 32902 }, "sub_vendor": { "hex": "17aa", "value": 6058 }, "device": { "hex": "9dc8", "value": 40392 }, "sub_device": { "hex": "2279", "value": 8825 }, "revision": { "hex": "0030", "value": 48 }, "model": "Intel Multimedia controller", "sysfs_id": "/devices/pci0000:00/0000:00:1f.3", "sysfs_bus_id": "0000:00:1f.3", "detail": { "function": 3, "command": 1030, "header_type": 0, "secondary_bus": 0, "prog_if": 128 }, "driver": "snd_hda_intel", "driver_module": "snd_hda_intel", "drivers": ["snd_hda_intel"], "driver_modules": ["snd_hda_intel"], "module_alias": "pci:v00008086d00009DC8sv000017AAsd00002279bc04sc03i80" } ], "storage_controller": [ { "index": 25, "attached_to": 23, "class_list": ["storage_controller", "pci"], "bus_type": { "hex": "0004", "name": "PCI", "value": 4 }, "slot": { "bus": 61, "number": 0 }, "base_class": { "hex": "0001", "name": "Mass storage controller", "value": 1 }, "sub_class": { "hex": "0008", "value": 8 }, "pci_interface": { "hex": "0002", "value": 2 }, "vendor": { "hex": "1e4b", "value": 7755 }, "sub_vendor": { "hex": "1e4b", "value": 7755 }, "device": { "hex": "1202", "value": 4610 }, "sub_device": { "hex": "1202", "value": 4610 }, "revision": { "hex": "0001", "value": 1 }, "model": "Mass storage controller", "sysfs_id": "/devices/pci0000:00/0000:00:1d.4/0000:3d:00.0", "sysfs_bus_id": "0000:3d:00.0", "detail": { "function": 0, "command": 1030, "header_type": 0, "secondary_bus": 0, "prog_if": 2 }, "driver": "nvme", "driver_module": "nvme", "drivers": ["nvme"], "driver_modules": ["nvme"], "module_alias": "pci:v00001E4Bd00001202sv00001E4Bsd00001202bc01sc08i02" } ], "system": { "form_factor": "laptop" }, "usb": [ { "index": 39, "attached_to": 42, "class_list": ["usb", "unknown"], "bus_type": { "hex": "0086", "name": "USB", "value": 134 }, "slot": { "bus": 0, "number": 0 }, "base_class": { "hex": "0000", "name": "Unclassified device", "value": 0 }, "sub_class": { "hex": "0000", "name": "Unclassified device", "value": 0 }, "vendor": { "hex": "2cb7", "name": "FIBOCOM", "value": 11447 }, "device": { "hex": "0210", "name": "L830-EB", "value": 528 }, "revision": { "hex": "0000", "name": "3.33", "value": 0 }, "serial": "004999010640000", "model": "FIBOCOM L830-EB", "sysfs_id": "/devices/pci0000:00/0000:00:14.0/usb1/1-7/1-7:1.2", "sysfs_bus_id": "1-7:1.2", "resources": [ { "type": "baud", "speed": 480000000, "bits": 0, "stop_bits": 0, "parity": 0, "handshake": 0 } ], "detail": { "device_class": { "hex": "00ef", "name": "miscellaneous", "value": 239 }, "device_subclass": { "hex": "0002", "name": "comm", "value": 2 }, "device_protocol": 1, "interface_class": { "hex": "0002", "name": "comm", "value": 2 }, "interface_subclass": { "hex": "0002", "name": "comm", "value": 2 }, "interface_protocol": 0, "interface_number": 2, "interface_alternate_setting": 0, "interface_association": { "function_class": { "hex": "0002", "name": "comm", "value": 2 }, "function_subclass": { "hex": "0002", "name": "comm", "value": 2 }, "function_protocol": 0, "interface_count": 2, "first_interface": 2 } }, "hotplug": "usb", "driver": "cdc_acm", "driver_module": "cdc_acm", "drivers": ["cdc_acm"], "driver_modules": ["cdc_acm"], "module_alias": "usb:v2CB7p0210d0333dcEFdsc02dp01ic02isc02ip00in02" }, { "index": 44, "attached_to": 42, "class_list": ["usb", "unknown"], "bus_type": { "hex": "0086", "name": "USB", "value": 134 }, "slot": { "bus": 0, "number": 0 }, "base_class": { "hex": "0000", "name": "Unclassified device", "value": 0 }, "sub_class": { "hex": "0000", "name": "Unclassified device", "value": 0 }, "vendor": { "hex": "2cb7", "name": "FIBOCOM", "value": 11447 }, "device": { "hex": "0210", "name": "L830-EB", "value": 528 }, "revision": { "hex": "0000", "name": "3.33", "value": 0 }, "serial": "004999010640000", "model": "FIBOCOM L830-EB", "sysfs_id": "/devices/pci0000:00/0000:00:14.0/usb1/1-7/1-7:1.3", "sysfs_bus_id": "1-7:1.3", "resources": [ { "type": "baud", "speed": 480000000, "bits": 0, "stop_bits": 0, "parity": 0, "handshake": 0 } ], "detail": { "device_class": { "hex": "00ef", "name": "miscellaneous", "value": 239 }, "device_subclass": { "hex": "0002", "name": "comm", "value": 2 }, "device_protocol": 1, "interface_class": { "hex": "000a", "name": "data", "value": 10 }, "interface_subclass": { "hex": "0000", "name": "per_interface", "value": 0 }, "interface_protocol": 0, "interface_number": 3, "interface_alternate_setting": 0, "interface_association": { "function_class": { "hex": "0002", "name": "comm", "value": 2 }, "function_subclass": { "hex": "0002", "name": "comm", "value": 2 }, "function_protocol": 0, "interface_count": 2, "first_interface": 2 } }, "hotplug": "usb", "driver": "cdc_acm", "driver_module": "cdc_acm", "drivers": ["cdc_acm"], "driver_modules": ["cdc_acm"], "module_alias": "usb:v2CB7p0210d0333dcEFdsc02dp01ic0Aisc00ip00in03" }, { "index": 46, "attached_to": 42, "class_list": ["usb", "unknown"], "bus_type": { "hex": "0086", "name": "USB", "value": 134 }, "slot": { "bus": 0, "number": 0 }, "base_class": { "hex": "0000", "name": "Unclassified device", "value": 0 }, "sub_class": { "hex": "0000", "name": "Unclassified device", "value": 0 }, "vendor": { "hex": "2cb7", "name": "FIBOCOM", "value": 11447 }, "device": { "hex": "0210", "name": "L830-EB", "value": 528 }, "revision": { "hex": "0000", "name": "3.33", "value": 0 }, "serial": "004999010640000", "model": "FIBOCOM L830-EB", "sysfs_id": "/devices/pci0000:00/0000:00:14.0/usb1/1-7/1-7:1.1", "sysfs_bus_id": "1-7:1.1", "resources": [ { "type": "baud", "speed": 480000000, "bits": 0, "stop_bits": 0, "parity": 0, "handshake": 0 } ], "detail": { "device_class": { "hex": "00ef", "name": "miscellaneous", "value": 239 }, "device_subclass": { "hex": "0002", "name": "comm", "value": 2 }, "device_protocol": 1, "interface_class": { "hex": "000a", "name": "data", "value": 10 }, "interface_subclass": { "hex": "0000", "name": "per_interface", "value": 0 }, "interface_protocol": 2, "interface_number": 1, "interface_alternate_setting": 1, "interface_association": { "function_class": { "hex": "0002", "name": "comm", "value": 2 }, "function_subclass": { "hex": "000e", "name": "video", "value": 14 }, "function_protocol": 0, "interface_count": 2, "first_interface": 0 } }, "hotplug": "usb", "driver": "cdc_mbim", "driver_module": "cdc_mbim", "drivers": ["cdc_mbim"], "driver_modules": ["cdc_mbim"], "module_alias": "usb:v2CB7p0210d0333dcEFdsc02dp01ic0Aisc00ip02in01" } ], "usb_controller": [ { "index": 31, "attached_to": 0, "class_list": ["usb_controller", "pci"], "bus_type": { "hex": "0004", "name": "PCI", "value": 4 }, "slot": { "bus": 0, "number": 20 }, "base_class": { "hex": "000c", "name": "Serial bus controller", "value": 12 }, "sub_class": { "hex": "0003", "name": "USB Controller", "value": 3 }, "pci_interface": { "hex": "0030", "value": 48 }, "vendor": { "hex": "8086", "name": "Intel Corporation", "value": 32902 }, "sub_vendor": { "hex": "17aa", "value": 6058 }, "device": { "hex": "9ded", "value": 40429 }, "sub_device": { "hex": "2279", "value": 8825 }, "revision": { "hex": "0030", "value": 48 }, "model": "Intel USB Controller", "sysfs_id": "/devices/pci0000:00/0000:00:14.0", "sysfs_bus_id": "0000:00:14.0", "detail": { "function": 0, "command": 1030, "header_type": 0, "secondary_bus": 0, "prog_if": 48 }, "driver": "xhci_hcd", "driver_module": "xhci_pci", "drivers": ["xhci_hcd"], "driver_modules": ["xhci_pci"], "module_alias": "pci:v00008086d00009DEDsv000017AAsd00002279bc0Csc03i30" } ] }, "smbios": { "bios": { "handle": 17, "vendor": "LENOVO", "version": "N2IETA5W (1.83 )", "date": "06/20/2024", "features": [ "PCI supported", "PnP supported", "BIOS flashable", "BIOS shadowing allowed", "CD boot supported", "Selectable boot supported", "EDD spec supported", "720kB Floppy supported", "Print Screen supported", "8042 Keyboard Services supported", "Serial Services supported", "Printer Services supported", "CGA/Mono Video supported", "ACPI supported", "USB Legacy supported", "BIOS Boot Spec supported" ], "start_address": "0xe0000", "rom_size": 16777216 }, "board": { "handle": 19, "manufacturer": "LENOVO", "product": "20N2S00600", "version": "SDK0J40697 WIN", "board_type": { "hex": "000a", "name": "Motherboard", "value": 10 }, "features": ["Hosting Board", "Replaceable"], "location": "Not Available", "chassis": 0 }, "cache": [ { "handle": 12, "socket": "L1 Cache", "size_max": 256, "size_current": 256, "speed": 0, "mode": { "hex": "0001", "name": "Write Back", "value": 1 }, "enabled": true, "location": { "hex": "0000", "name": "Internal", "value": 0 }, "socketed": false, "level": 0, "ecc": { "hex": "0004", "name": "Parity", "value": 4 }, "cache_type": { "hex": "0005", "name": "Unified", "value": 5 }, "associativity": { "hex": "0007", "name": "8-way Set-Associative", "value": 7 }, "sram_type_current": ["Synchronous"], "sram_type_supported": ["Synchronous"] }, { "handle": 13, "socket": "L2 Cache", "size_max": 1024, "size_current": 1024, "speed": 0, "mode": { "hex": "0001", "name": "Write Back", "value": 1 }, "enabled": true, "location": { "hex": "0000", "name": "Internal", "value": 0 }, "socketed": false, "level": 1, "ecc": { "hex": "0005", "name": "Single-bit", "value": 5 }, "cache_type": { "hex": "0005", "name": "Unified", "value": 5 }, "associativity": { "hex": "0005", "name": "4-way Set-Associative", "value": 5 }, "sram_type_current": ["Synchronous"], "sram_type_supported": ["Synchronous"] }, { "handle": 14, "socket": "L3 Cache", "size_max": 8192, "size_current": 8192, "speed": 0, "mode": { "hex": "0001", "name": "Write Back", "value": 1 }, "enabled": true, "location": { "hex": "0000", "name": "Internal", "value": 0 }, "socketed": false, "level": 2, "ecc": { "hex": "0006", "name": "Multi-bit", "value": 6 }, "cache_type": { "hex": "0005", "name": "Unified", "value": 5 }, "associativity": { "hex": "0008", "name": "16-way Set-Associative", "value": 8 }, "sram_type_current": ["Synchronous"], "sram_type_supported": ["Synchronous"] } ], "chassis": [ { "handle": 20, "manufacturer": "LENOVO", "version": "None", "chassis_type": { "hex": "000a", "name": "Notebook", "value": 10 }, "lock_present": false, "bootup_state": { "hex": "0002", "name": "Unknown", "value": 2 }, "power_state": { "hex": "0002", "name": "Unknown", "value": 2 }, "thermal_state": { "hex": "0002", "name": "Unknown", "value": 2 }, "security_state": { "hex": "0002", "name": "Unknown", "value": 2 }, "oem": "0x0" } ], "config": { "handle": 42 }, "group_associations": [ { "handle": 1, "name": "Intel(R) Silicon View Technology", "handles": [0] }, { "handle": 51, "power": { "hex": "0000", "name": "Disabled", "value": 0 }, "keyboard": { "hex": "0002", "name": "Not Implemented", "value": 2 }, "admin": { "hex": "0000", "name": "Disabled", "value": 0 }, "reset": { "hex": "0002", "name": "Not Implemented", "value": 2 } }, { "handle": 61, "name": "$MEI", "handles": [0] } ], "language": [ { "handle": 43, "languages": ["en-US"] } ], "memory_array": [ { "handle": 2, "location": { "hex": "0003", "name": "Motherboard", "value": 3 }, "usage": { "hex": "0003", "name": "System memory", "value": 3 }, "ecc": { "hex": "0003", "name": "None", "value": 3 }, "max_size": "0x1800000", "error_handle": 65534, "slots": 2 } ], "memory_array_mapped_address": [ { "handle": 5, "array_handle": 2, "start_address": "0x0", "end_address": "0x600000000", "part_width": 2 } ], "memory_device": [ { "handle": 3, "location": "ChannelA-DIMM0", "bank_location": "BANK 0", "manufacturer": "SK Hynix", "part_number": "HMAB2GS6AMR6N-XN", "array_handle": 2, "error_handle": 65534, "width": 64, "ecc_bits": 0, "size": 16777216, "form_factor": { "hex": "000d", "name": "SODIMM", "value": 13 }, "set": 0, "memory_type": { "hex": "001a", "name": "Other", "value": 26 }, "memory_type_details": ["Synchronous"], "speed": 3200 }, { "handle": 4, "location": "ChannelB-DIMM0", "bank_location": "BANK 2", "manufacturer": "SK Hynix", "part_number": "HMA81GS6DJR8N-XN", "array_handle": 2, "error_handle": 65534, "width": 64, "ecc_bits": 0, "size": 8388608, "form_factor": { "hex": "000d", "name": "SODIMM", "value": 13 }, "set": 0, "memory_type": { "hex": "001a", "name": "Other", "value": 26 }, "memory_type_details": ["Synchronous"], "speed": 3200 } ], "memory_error": [ { "handle": 53, "error_type": { "hex": "0003", "name": "OK", "value": 3 }, "granularity": { "hex": "0002", "name": "Unknown", "value": 2 }, "operation": { "hex": "0002", "name": "Unknown", "value": 2 }, "syndrome": 0, "array_address": "0x80000000", "device_address": "0x80000000", "range": 2147483648 } ], "pointing_device": [ { "handle": 54, "mouse_type": { "hex": "0005", "name": "Track Point", "value": 5 }, "interface": { "hex": "0004", "name": "PS/2", "value": 4 }, "buttons": 3 }, { "handle": 55, "mouse_type": { "hex": "0007", "name": "Touch Pad", "value": 7 }, "interface": { "hex": "0004", "name": "PS/2", "value": 4 }, "buttons": 2 } ], "port_connector": [ { "handle": 21, "port_type": { "hex": "0010", "name": "USB", "value": 16 }, "internal_reference_designator": "Not Available", "external_connector_type": { "hex": "0012", "name": "Access Bus [USB]", "value": 18 }, "external_reference_designator": "USB 1" }, { "handle": 22, "port_type": { "hex": "0010", "name": "USB", "value": 16 }, "internal_reference_designator": "Not Available", "external_connector_type": { "hex": "0012", "name": "Access Bus [USB]", "value": 18 }, "external_reference_designator": "USB 2" }, { "handle": 23, "port_type": { "hex": "0010", "name": "USB", "value": 16 }, "internal_reference_designator": "Not Available", "external_connector_type": { "hex": "0012", "name": "Access Bus [USB]", "value": 18 }, "external_reference_designator": "USB 3" }, { "handle": 24, "port_type": { "hex": "0010", "name": "USB", "value": 16 }, "internal_reference_designator": "Not Available", "external_connector_type": { "hex": "0012", "name": "Access Bus [USB]", "value": 18 }, "external_reference_designator": "USB 4" }, { "handle": 30, "port_type": { "hex": "001f", "name": "Network Port", "value": 31 }, "internal_reference_designator": "Not Available", "external_connector_type": { "hex": "000b", "name": "RJ-45", "value": 11 }, "external_reference_designator": "Ethernet" }, { "handle": 33, "port_type": { "hex": "001c", "name": "Video Port", "value": 28 }, "internal_reference_designator": "Not Available", "external_connector_type": { "hex": "00ff", "name": "Other", "value": 255 }, "external_reference_designator": "Hdmi1" }, { "handle": 38, "port_type": { "hex": "001d", "name": "Audio Port", "value": 29 }, "internal_reference_designator": "Not Available", "external_connector_type": { "hex": "001f", "name": "Mini-jack [headphones]", "value": 31 }, "external_reference_designator": "Headphone/Microphone Combo Jack1" } ], "processor": [ { "handle": 15, "socket": "U3E1", "socket_type": { "hex": "003c", "name": "Other", "value": 60 }, "socket_populated": true, "manufacturer": "Intel(R) Corporation", "version": "Intel(R) Core(TM) i7-8565U CPU @ 1.80GHz", "part": "None", "processor_type": { "hex": "0003", "name": "CPU", "value": 3 }, "processor_family": { "hex": "00c6", "name": "Other", "value": 198 }, "processor_status": { "hex": "0001", "name": "Enabled", "value": 1 }, "clock_ext": 100, "clock_max": 2000, "cache_handle_l1": 12, "cache_handle_l2": 13, "cache_handle_l3": 14 } ], "slot": [ { "handle": 40, "designation": "Media Card Slot", "slot_type": { "hex": "0001", "name": "Other", "value": 1 }, "bus_width": { "hex": "0001", "name": "Other", "value": 1 }, "usage": { "hex": "0003", "name": "Available", "value": 3 }, "length": { "hex": "0001", "name": "Other", "value": 1 }, "id": 0, "features": ["Hot-Plug"] }, { "handle": 41, "designation": "SimCard Slot", "slot_type": { "hex": "0001", "name": "Other", "value": 1 }, "bus_width": { "hex": "0001", "name": "Other", "value": 1 }, "usage": { "hex": "0003", "name": "Available", "value": 3 }, "length": { "hex": "0001", "name": "Other", "value": 1 }, "id": 0 } ], "system": { "handle": 18, "manufacturer": "LENOVO", "product": "20N2S00600", "version": "ThinkPad T490", "wake_up": { "hex": "0006", "name": "Power Switch", "value": 6 } } } } ================================================ FILE: machines/kartoffel/configuration.nix ================================================ # Configuration for kartoffel { pkgs, lib, ... }: { imports = [ ./hardware-configuration.nix ./retiolum.nix ]; networking.hostName = "kartoffel"; pinpox.defaults.lvm-grub.enable = true; # Encrypted drive to be mounted by the bootloader. Path of the device will # have to be changed for each install. boot.initrd.luks.devices = { root = { # Get UUID from blkid /dev/sda2 device = "/dev/disk/by-uuid/608e0e77-eea4-4dc4-b88d-76cc63e4488b"; preLVM = true; allowDiscards = true; }; }; # Video driver for nvidia graphics card hardware.nvidia.open = false; boot.blacklistedKernelModules = [ "nouveau" ]; services.greetd.settings.default_session.command = lib.mkForce "${pkgs.tuigreet}/bin/tuigreet --time --cmd 'sway --unsupported-gpu'"; boot.supportedFilesystems = { btrfs = true; zfs = true; }; } ================================================ FILE: machines/kartoffel/hardware-configuration.nix ================================================ # Do not modify this file! It was generated by ‘nixos-generate-config’ # and may be overwritten by future invocations. Please make changes # to /etc/nixos/configuration.nix instead. { config, lib, pkgs, modulesPath, ... }: { imports = [ (modulesPath + "/installer/scan/not-detected.nix") ]; boot.initrd.availableKernelModules = [ "xhci_pci" "ahci" "usbhid" "sd_mod" ]; boot.initrd.kernelModules = [ "dm-snapshot" ]; boot.kernelModules = [ "kvm-amd" ]; boot.extraModulePackages = [ ]; fileSystems."/" = { device = "/dev/disk/by-uuid/8dcfb3f0-4dba-4c32-af96-84024706ff76"; fsType = "ext4"; }; fileSystems."/boot" = { device = "/dev/disk/by-uuid/5D7C-69F9"; fsType = "vfat"; }; swapDevices = [ { device = "/dev/disk/by-uuid/0f369649-cdbc-4a34-82dc-9f442c445c53"; } ]; powerManagement.cpuFreqGovernor = lib.mkDefault "performance"; } ================================================ FILE: machines/kartoffel/retiolum.nix ================================================ { config, retiolum, ... }: { imports = [ retiolum.nixosModules.retiolum ]; networking.retiolum = { ipv4 = "10.243.100.100"; ipv6 = "42:0:3c46:519d:1696:f464:9756:8727"; }; networking.retiolum.nodename = "ahorn"; clan.core.vars.generators."retiolum" = { prompts.rsa_priv.persist = true; prompts.ed25519_priv.persist = true; }; services.tinc.networks.retiolum = { rsaPrivateKeyFile = config.clan.core.vars.generators."retiolum".files."rsa_priv".path; ed25519PrivateKeyFile = config.clan.core.vars.generators."retiolum".files."ed25519_priv".path; }; } ================================================ FILE: machines/kfbox/configuration.nix ================================================ { aoe-taunt-discord-bot, config, go-karma-bot, pkgs, lib, ... }: let pinpox-utils = import ../../utils { inherit pkgs lib; }; in { # Build on machine executing the clan clan.core.networking.buildHost = "pinpox@localhost"; networking.interfaces.ens3 = { ipv6.addresses = [ { address = "2a03:4000:7:4e0::"; prefixLength = 64; } ]; }; clan.core.networking.targetHost = "46.38.242.17"; services.logind.settings.Login.RuntimeDirectorySize = "20G"; imports = [ ./retiolum.nix ./hardware-configuration.nix go-karma-bot.nixosModules.go-karma-bot aoe-taunt-discord-bot.nixosModules.aoe-taunt-discord-bot ]; systemd.services.NetworkManager-wait-online.serviceConfig.ExecStart = [ "${pkgs.networkmanager}/bin/nm-online -q" ]; # Karmabot for IRC channel clan.core.vars.generators."go-karma-bot" = pinpox-utils.mkEnvGenerator [ "IRC_BOT_SERVER" "IRC_BOT_CHANNEL" "IRC_BOT_NICK" "IRC_BOT_PASS" ]; services.go-karma-bot = { enable = true; environmentFile = config.clan.core.vars.generators."go-karma-bot".files."envfile".path; }; systemd.services.go-karma-bot.serviceConfig = { RestartSec = 5; Restart = "on-abnormal"; }; # Discord AoE2 taunt bot clan.core.vars.generators."aoe-taunt-discord-bot" = { prompts.discord_token.persist = true; }; services.aoe-taunt-discord-bot = { enable = true; discordTokenFile = config.clan.core.vars.generators."aoe-taunt-discord-bot".files."discord_token".path; }; networking.hostName = "kfbox"; pinpox = { services = { immich.enable = false; radio.enable = true; jitsi-matrix-presence.enable = true; hedgedoc.enable = false; screego.enable = true; miniflux.enable = true; kf-homepage.enable = true; calibre-web.enable = false; gitea.enable = true; owncast.enable = false; vikunja.enable = false; wastebin.enable = true; }; }; programs.ssh.startAgent = false; services.qemuGuest.enable = true; fileSystems."/" = { device = "/dev/disk/by-label/nixos"; fsType = "ext4"; autoResize = true; }; fileSystems."/tmp" = { fsType = "tmpfs"; device = "tmpfs"; options = [ "nosuid" "nodev" "relatime" "size=14G" ]; }; boot.growPartition = true; boot.kernelParams = [ "console=ttyS0" ]; boot.loader.grub.device = "/dev/sda"; boot.loader.timeout = 0; # Block anything that is not HTTP(s) or SSH. networking.firewall = { enable = true; allowPing = true; allowedTCPPorts = [ # 8989 80 443 22 7777 ]; }; # virtualisation.docker.enable = true; # services.traccar = { # enable = true; # # config.xml configuration as a Nix attribute set. This option is ignored if settingsFile is set. # # # # Nested attributes get translated to a properties entry in the traccar configuration. For instance: mail.smtp.port = "25" results in the following entry: 25 # # settings = { # h02.port = "8989"; # filter = { # # This filters out positions within 5 meters of the previous position. # enable = "true"; # duplicate = "true"; # distance = "5"; # }; # }; # # }; services.caddy = { enable = true; virtualHosts = { # "track.0cx.de".extraConfig = "reverse_proxy 127.0.0.1:8082"; # "track.0cx.de".extraConfig = " # reverse_proxy 127.0.0.1:8080 { # header_up Upgrade {http.request.header.upgrade} # header_up Connection {http.request.header.connection} # } # "; # "transfer.0cx.de".extraConfig = "reverse_proxy 127.0.0.1:6767"; # "pads.0cx.de".extraConfig = "reverse_proxy 127.0.0.1:3000"; # "photos-api.0cx.de".extraConfig = "reverse_proxy 127.0.0.1:8080"; # "matrixpresence.0cx.de".extraConfig = "reverse_proxy 127.0.0.1:8227"; "paste.0cx.de".extraConfig = "reverse_proxy ${config.services.wastebin.settings.WASTEBIN_ADDRESS_PORT}"; }; }; } ================================================ FILE: machines/kfbox/hardware-configuration.nix ================================================ { modulesPath, ... }: { imports = [ (modulesPath + "/profiles/qemu-guest.nix") ]; } ================================================ FILE: machines/kfbox/retiolum.nix ================================================ { config, retiolum, ... }: { imports = [ retiolum.nixosModules.retiolum ]; networking.retiolum = { ipv4 = "10.243.100.102"; ipv6 = "42:0:3c46:3ae6:90a8:b220:e772:8a5c"; }; clan.core.vars.generators."retiolum" = { prompts.rsa_priv.persist = true; prompts.ed25519_priv.persist = true; }; services.tinc.networks.retiolum = { rsaPrivateKeyFile = config.clan.core.vars.generators."retiolum".files."rsa_priv".path; ed25519PrivateKeyFile = config.clan.core.vars.generators."retiolum".files."ed25519_priv".path; }; } ================================================ FILE: machines/kiwi/configuration.nix ================================================ { nixos-hardware, lib, ... }: { imports = [ ./ollama-local.nix ./opencrow-geninf.nix ./disko-config-btrfs.nix # ./framework.nix nixos-hardware.nixosModules.framework-amd-ai-300-series ]; # `boltctl`, to authorize Thunderbolt docs (e.g. lenovo dock) services.hardware.bolt.enable = true; # Trust all thunderbolt devices # boot.kernelParams = [ "thunderbolt.host_reset=0" ]; # services.udev.extraRules = '' # ACTION=="add", SUBSYSTEM=="thunderbolt", ATTR{authorized}=="0", ATTR{authorized}="1" # ''; hardware = { fw-fanctrl.enable = true; rtl-sdr.enable = true; amdgpu.opencl.enable = true; xone.enable = true; }; networking.hostName = "kiwi"; # Games programs.steam.enable = true; programs.gamemode.enable = true; # For dual-boot boot.loader.efi.canTouchEfiVariables = true; boot.loader.grub.efiInstallAsRemovable = lib.mkForce false; # Enable aarch64 emulation for cross-building ARM images boot.binfmt.emulatedSystems = [ "aarch64-linux" ]; # Remap Caps Lock to Esc and vice versa services.udev.extraHwdb = '' evdev:atkbd:dmi:* KEYBOARD_KEY_3a=esc # Caps Lock -> Esc KEYBOARD_KEY_01=capslock # Esc -> Caps Lock ''; } ================================================ FILE: machines/kiwi/disko-config-btrfs.nix ================================================ { boot.growPartition = true; boot.supportedFilesystems.btrfs = true; services.btrfs.autoScrub = { enable = true; interval = "weekly"; # Defaults to all # fileSystems = [ "/" ]; }; disko.devices = { disk = { main = { type = "disk"; device = "/dev/nvme0n1"; content = { type = "gpt"; partitions = { ESP = { size = "512M"; type = "EF00"; content = { type = "filesystem"; format = "vfat"; mountpoint = "/boot"; mountOptions = [ "umask=0077" ]; }; }; windows = { size = "512G"; type = "EF00"; content = { type = "filesystem"; format = "vfat"; }; }; luks = { size = "100%"; content = { type = "luks"; name = "crypted"; # disable settings.keyFile if you want to use interactive password entry #passwordFile = "/tmp/secret.key"; # Interactive settings = { allowDiscards = true; # keyFile = "/tmp/secret.key"; }; # additionalKeyFiles = [ "/tmp/additionalSecret.key" ]; content = { type = "btrfs"; extraArgs = [ "-f" ]; subvolumes = { "/root" = { mountpoint = "/"; mountOptions = [ "compress=zstd" "noatime" ]; }; "/home" = { mountpoint = "/home"; mountOptions = [ "compress=zstd" "noatime" ]; }; "/nix" = { mountpoint = "/nix"; mountOptions = [ "compress=zstd" "noatime" ]; }; "/swap" = { mountpoint = "/.swapvol"; swap.swapfile.size = "20M"; }; }; }; }; }; }; }; }; }; }; } ================================================ FILE: machines/kiwi/framework.nix ================================================ config, pkgs, ... }: { # For fingerprint support # To enroll prints: `sudo fprint-enroll ` services.fprintd.enable = true; boot.extraModulePackages = with config.boot.kernelPackages; [ framework-laptop-kmod ]; # Module is not used for Framework EC but causes boot time error log. # boot.blacklistedKernelModules = [ "cros-usbpd-charger" ]; # https://github.com/DHowett/framework-laptop-kmod?tab=readme-ov-file#usage # boot.kernelModules = [ # "cros_ec" # "cros_ec_lpcs" # ]; # boot.kernelParams = [ # For Power consumption # https://community.frame.work/t/linux-battery-life-tuning/6665/156 # "nvme.noacpi=1" # ]; # Custom udev rules # services.udev.extraRules = '' # # Fix headphone noise when on powersave # # https://community.frame.work/t/headphone-jack-intermittent-noise/5246/55 # SUBSYSTEM=="pci", ATTR{vendor}=="0x8086", ATTR{device}=="0xa0e0", ATTR{power/control}="on" # ''; # Ethernet expansion card support services.udev.extraRules = '' ACTION=="add", SUBSYSTEM=="usb", ATTR{idVendor}=="0bda", ATTR{idProduct}=="8156", ATTR{power/autosuspend}="20" ''; environment.systemPackages = [ # This adds a patched ectool, to interact with the Embedded Controller # Can be used to interact with leds from userspace, etc. # Not part of a nixos release yet, so package only gets added if it exists. pkgs.fw-ectool pkgs.framework-tool ]; # AMD has better battery life with PPD over TLP: # https://community.frame.work/t/responded-amd-7040-sleep-states/38101/13 services.power-profiles-daemon.enable = true; services.tlp.enable = false; # Needed for desktop environments to detect/manage display brightness hardware.sensor.iio.enable = true; # TODO not sure if needed # Deactivates light sensor? # https://github.com/NixOS/nixpkgs/issues/171093 # https://wiki.archlinux.org/title/Framework_Laptop#Changing_the_brightness_of_the_monitor_does_not_work hardware.acpilight.enable = true; } ================================================ FILE: machines/kiwi/ollama-local.nix ================================================ { ... }: let vllmProvider = host: model: { baseUrl = "http://${host}:8000/v1"; api = "openai-completions"; apiKey = "dummy"; compat = { supportsDeveloperRole = false; supportsReasoningEffort = false; }; models = [ { id = model; reasoning = true; } ]; }; in { services.tailscale.enable = true; # Pi providers via home-manager module home-manager.users.pinpox.pinpox.programs.pi.providers = { llama-cpp = { baseUrl = "http://127.0.0.1:8080/v1"; api = "openai-completions"; apiKey = "dummy"; models = [ { id = "glm-4.7-flash"; name = "GLM-4.7 Flash (Local)"; reasoning = true; input = [ "text" ]; contextWindow = 32768; maxTokens = 8192; } ]; }; vllm_1 = vllmProvider "100.96.100.100" "openai/gpt-oss-120b"; vllm_2 = vllmProvider "100.96.100.101" "Qwen/Qwen3.6-35B-A3B"; vllm_3 = vllmProvider "100.96.100.102" "Qwen/Qwen3-Coder-30B-A3B-Instruct-FP8"; # Pull models with: OLLAMA_HOST="100.96.100.103:11434" ollama pull ollama = { baseUrl = "http://100.96.100.103:11434/v1"; api = "openai-completions"; apiKey = "dummy"; compat = { supportsDeveloperRole = false; supportsReasoningEffort = false; }; models = [ { id = "codegemma:latest"; contextWindow = 128000; maxTokens = 32000; } { id = "deepseek-r1:671b"; reasoning = true; contextWindow = 128000; maxTokens = 32000; } { id = "deepseek-r1:671b-0528-q4_K_M"; reasoning = true; contextWindow = 128000; maxTokens = 32000; } { id = "deepseek-r1:671b-0528-q4_K_M_131072"; reasoning = true; contextWindow = 128000; maxTokens = 32000; } { id = "deepseek-r1:671b-0528-q4_K_M_16384"; reasoning = true; contextWindow = 128000; maxTokens = 32000; } { id = "deepseek-r1:671b-0528-q4_K_M_32768"; reasoning = true; contextWindow = 128000; maxTokens = 32000; } { id = "deepseek-r1:671b-0528-q4_K_M_65536"; reasoning = true; contextWindow = 128000; maxTokens = 32000; } { id = "deepseek-r1:671b-0528-q4_K_M_8192"; reasoning = true; contextWindow = 128000; maxTokens = 32000; } { id = "deepseek-r1:671b-q4_K_M"; reasoning = true; contextWindow = 128000; maxTokens = 32000; } { id = "deepseek-r1:70b"; reasoning = true; contextWindow = 128000; maxTokens = 32000; } { id = "deepseek-r1:latest"; reasoning = true; contextWindow = 128000; maxTokens = 32000; } { id = "deepseek-v3.1:671b"; contextWindow = 128000; maxTokens = 32000; } { id = "deepseek-v3.1:671b_131072"; contextWindow = 128000; maxTokens = 32000; } { id = "deepseek-v3.1:671b_163840"; contextWindow = 128000; maxTokens = 32000; } { id = "devstral:24b-small-2505-q8_0"; contextWindow = 128000; maxTokens = 32000; } { id = "gemma3:27b-it-q8_0"; contextWindow = 128000; maxTokens = 32000; } { id = "glm-4.7-flash:latest"; contextWindow = 128000; maxTokens = 32000; } { id = "glm-5:latest"; contextWindow = 128000; maxTokens = 32000; } { id = "gpt-oss:120b"; contextWindow = 128000; maxTokens = 32000; } { id = "gpt-oss:120b_131072"; contextWindow = 128000; maxTokens = 32000; } { id = "gpt-oss:120b-cloud"; contextWindow = 128000; maxTokens = 32000; } { id = "gpt-oss:20b"; contextWindow = 128000; maxTokens = 32000; } { id = "hf.co/mradermacher/Qwen3.5-397B-A17B-GGUF:Qwen3.5-397B-A17B.Q3_K_M.gguf"; contextWindow = 128000; maxTokens = 32000; } { id = "hf.co/sebsigma/SemanticCite-Checker-Qwen3-4B:latest"; contextWindow = 128000; maxTokens = 32000; } { id = "hf.co/sebsigma/SemanticCite-Refiner-Qwen3-1B:latest"; contextWindow = 128000; maxTokens = 32000; } { id = "hf.co/unsloth/GLM-5-GGUF:latest"; contextWindow = 128000; maxTokens = 32000; } { id = "hf.co/unsloth/MiniMax-M2.5-GGUF:latest"; contextWindow = 128000; maxTokens = 32000; } { id = "jmorgan/bespoke-minicheck:7b-fp16"; contextWindow = 128000; maxTokens = 32000; } { id = "jmorgan/bespoke-minicheck:latest"; contextWindow = 128000; maxTokens = 32000; } { id = "llama3.2:1b"; contextWindow = 128000; maxTokens = 32000; } { id = "llama3.2:3b"; contextWindow = 128000; maxTokens = 32000; } { id = "llama3.3:latest"; contextWindow = 128000; maxTokens = 32000; } { id = "llama4:128x17b"; contextWindow = 128000; maxTokens = 32000; } { id = "llama4:17b-maverick-128e-instruct-q4_K_M"; contextWindow = 128000; maxTokens = 32000; } { id = "llama4:17b-maverick-128e-instruct-q8_0"; contextWindow = 128000; maxTokens = 32000; } { id = "llama4:latest"; contextWindow = 128000; maxTokens = 32000; } { id = "llama4:maverick"; contextWindow = 128000; maxTokens = 32000; } { id = "llama4:maverick_131072"; contextWindow = 128000; maxTokens = 32000; } { id = "llama4:maverick_262144"; contextWindow = 128000; maxTokens = 32000; } { id = "llama4:maverick_32768"; contextWindow = 128000; maxTokens = 32000; } { id = "llama4:scout"; contextWindow = 128000; maxTokens = 32000; } { id = "ministral-3:14b"; contextWindow = 128000; maxTokens = 32000; } { id = "mistral:7b"; contextWindow = 128000; maxTokens = 32000; } { id = "mistral:7b_32768"; contextWindow = 128000; maxTokens = 32000; } { id = "mistral-nemo:12b"; contextWindow = 128000; maxTokens = 32000; } { id = "mistral-nemo:12b_131072"; contextWindow = 128000; maxTokens = 32000; } { id = "mistral-small3.2:24b"; contextWindow = 128000; maxTokens = 32000; } { id = "mixtral:8x22b"; contextWindow = 128000; maxTokens = 32000; } { id = "mixtral:8x22b_65536"; contextWindow = 128000; maxTokens = 32000; } { id = "my-model:latest"; contextWindow = 128000; maxTokens = 32000; } { id = "phi4:14b-q8_0"; contextWindow = 128000; maxTokens = 32000; } { id = "qwen2.5-coder:32b"; contextWindow = 128000; maxTokens = 32000; } { id = "qwen2.5-coder:32b-instruct-q8_0"; contextWindow = 128000; maxTokens = 32000; } { id = "qwen3:235b"; contextWindow = 128000; maxTokens = 32000; } { id = "qwen3:32b"; contextWindow = 128000; maxTokens = 32000; } { id = "qwen3:32b_131072"; contextWindow = 128000; maxTokens = 32000; } { id = "qwen3:32b-q8_0"; contextWindow = 128000; maxTokens = 32000; } { id = "qwen3.5:397b-q3_k_m"; contextWindow = 128000; maxTokens = 32000; } { id = "qwen3-coder:30b"; contextWindow = 128000; maxTokens = 32000; } { id = "qwen3-coder:30b-a3b-fp16"; contextWindow = 128000; maxTokens = 32000; } { id = "qwen3-coder:30b-a3b-q4_K_M"; contextWindow = 128000; maxTokens = 32000; } { id = "qwen3-coder:30b-a3b-q4_K_M_131072"; contextWindow = 128000; maxTokens = 32000; } { id = "qwen3-coder:30b-a3b-q4_K_M_65536"; contextWindow = 128000; maxTokens = 32000; } { id = "qwen3-coder:30b-a3b-q8_0"; contextWindow = 128000; maxTokens = 32000; } { id = "qwen3-coder:480b"; contextWindow = 128000; maxTokens = 32000; } { id = "qwen3-coder:480b_131072"; contextWindow = 128000; maxTokens = 32000; } { id = "SemanticCite-Checker-Qwen3-4B:latest"; contextWindow = 128000; maxTokens = 32000; } { id = "SemanticCite-Refiner-Qwen3-1B:latest"; contextWindow = 128000; maxTokens = 32000; } { id = "tinyllama:1.1b-chat-v1-q2_K"; contextWindow = 128000; maxTokens = 32000; } { id = "qwen3.5:122b"; contextWindow = 256000; maxTokens = 32000; } { id = "qwen3-coder-next:q8_0"; contextWindow = 256000; maxTokens = 32000; } { id = "qwen3-next:80b"; contextWindow = 256000; maxTokens = 32000; } { id = "sinhang/QWen35-27b-q4_K_M-Claude"; reasoning = true; contextWindow = 128000; maxTokens = 32000; } { id = "frob/qwen3.5:397b-a17b-q4_K_XL"; contextWindow = 128000; maxTokens = 32000; } { id = "gemma4:31b"; contextWindow = 128000; maxTokens = 32000; } { id = "gemma4:31b-cloud"; contextWindow = 128000; maxTokens = 32000; } { id = "kwangsuklee/Qwen3.5-27B-Claude-4.6-Opus-Reasoning-Distilled-GGUF:latest"; reasoning = true; contextWindow = 128000; maxTokens = 32000; } { id = "qwen3:235b-a22b-instruct-2507-q8_0"; contextWindow = 128000; maxTokens = 32000; } { id = "qwen3.5:0.8b"; contextWindow = 128000; maxTokens = 32000; } { id = "qwen3.5:35b"; contextWindow = 128000; maxTokens = 32000; } { id = "qwen3.6:latest"; contextWindow = 128000; maxTokens = 32000; } { id = "qwen3-coder-next:q4_K_M"; contextWindow = 128000; maxTokens = 32000; } { id = "qwen3-vl:235b"; contextWindow = 128000; maxTokens = 32000; } { id = "qwen3-vl:32b"; contextWindow = 128000; maxTokens = 32000; } ]; }; }; } ================================================ FILE: machines/kiwi/opencrow-geninf.nix ================================================ { distro, opencrow, pkgs, ... }: { imports = [ opencrow.nixosModules.default distro.nixosModules.opencrow distro.nixosModules.llama-swap ]; # Local LLM server services.llama-swap.enable = true; services.opencrow-local = { enable = true; instanceName = "geninf"; piPackage = pkgs.pi; llmUrl = "http://127.0.0.1:8012"; model = "qwen2.5:0.5b"; socketName = "GenInf Crow"; noctaliaPlugin = true; noctaliaPluginUsers = [ "pinpox" ]; extraPackages = [ pkgs.pi pkgs.curl pkgs.jq ]; }; } ================================================ FILE: machines/limette/configuration.nix ================================================ { pkgs, nixos-hardware, ... }: { networking.hostName = "limette"; pinpox.defaults.lvm-grub.enable = true; boot.growPartition = true; hardware.enableRedistributableFirmware = true; imports = [ nixos-hardware.nixosModules.lenovo-thinkpad-x230 ./disko-config-btrfs.nix ]; disko.imageBuilder.extraDependencies = [ pkgs.kmod ]; hardware.graphics.extraPackages = [ pkgs.intel-media-driver # LIBVA_DRIVER_NAME=iHD ]; boot.loader.efi.canTouchEfiVariables = false; } ================================================ FILE: machines/limette/disko-config-btrfs.nix ================================================ { boot.growPartition = true; boot.supportedFilesystems.btrfs = true; disko.devices = { disk = { main = { type = "disk"; device = "/dev/sda"; content = { type = "gpt"; partitions = { ESP = { size = "512M"; type = "EF00"; content = { type = "filesystem"; format = "vfat"; mountpoint = "/boot"; mountOptions = [ "umask=0077" ]; }; }; luks = { size = "100%"; content = { type = "luks"; name = "crypted"; # disable settings.keyFile if you want to use interactive password entry #passwordFile = "/tmp/secret.key"; # Interactive settings = { allowDiscards = true; # keyFile = "/tmp/secret.key"; }; # additionalKeyFiles = [ "/tmp/additionalSecret.key" ]; content = { type = "btrfs"; extraArgs = [ "-f" ]; subvolumes = { "/root" = { mountpoint = "/"; mountOptions = [ "compress=zstd" "noatime" ]; }; "/home" = { mountpoint = "/home"; mountOptions = [ "compress=zstd" "noatime" ]; }; "/nix" = { mountpoint = "/nix"; mountOptions = [ "compress=zstd" "noatime" ]; }; "/swap" = { mountpoint = "/.swapvol"; swap.swapfile.size = "20M"; }; }; }; }; }; }; }; }; }; }; } ================================================ FILE: machines/limette/disko-config-zfs.nix ================================================ { lib, config, ... }: { # You may also find this setting useful to automatically set the latest compatible kernel: boot.kernelPackages = lib.mkDefault config.boot.zfs.package.latestCompatibleLinuxPackages; boot.supportedFilesystems.zfs = true; services.zfs.autoSnapshot = { enable = true; frequent = 4; # 15 min hourly = 24; daily = 7; weekly = 4; monthly = 0; }; disko.devices = { disk = { root = { type = "disk"; device = "/dev/sda"; content = { type = "gpt"; partitions = { ESP = { size = "1G"; type = "EF00"; content = { type = "filesystem"; format = "vfat"; mountpoint = "/boot"; mountOptions = [ "nofail" ]; }; }; zfs = { size = "100%"; content = { type = "zfs"; pool = "zroot"; }; }; }; }; }; }; zpool = { zroot = { type = "zpool"; rootFsOptions = { mountpoint = "none"; compression = "zstd"; acltype = "posixacl"; xattr = "sa"; "com.sun:auto-snapshot" = "true"; }; options.ashift = "12"; datasets = { "root" = { type = "zfs_fs"; options = { encryption = "aes-256-gcm"; keyformat = "passphrase"; #keylocation = "file:///tmp/secret.key"; keylocation = "prompt"; }; mountpoint = "/"; }; "root/nix" = { type = "zfs_fs"; options.mountpoint = "/nix"; mountpoint = "/nix"; }; "root/home" = { type = "zfs_fs"; options.mountpoint = "/home"; mountpoint = "/home"; }; "root/var/lib" = { type = "zfs_fs"; options.mountpoint = "/var/lib"; mountpoint = "/var/lib"; }; # "root/tmp" = { # type = "zfs_fs"; # mountpoint = "/tmp"; # options = { # mountpoint = "/tmp"; # sync = "disabled"; # }; # }; # README MORE: https://wiki.archlinux.org/title/ZFS#Swap_volume # "root/swap" = { # type = "zfs_volume"; # size = "10M"; # content = { # type = "swap"; # }; # options = { # volblocksize = "4096"; # compression = "zle"; # logbias = "throughput"; # sync = "always"; # primarycache = "metadata"; # secondarycache = "none"; # "com.sun:auto-snapshot" = "false"; # }; # }; }; }; }; }; } ================================================ FILE: machines/porree/blog.nix ================================================ with import { }; stdenv.mkDerivation rec { name = "blog"; # src = ./git-repos/hugo-website; src = builtins.fetchurl { url = "https://github.com/pinpox/hugo-website/archive/main.tar.gz"; }; buildInputs = [ hugo ]; buildPhase = "hugo"; installPhase = "cp -R public/ $out"; } ================================================ FILE: machines/porree/caddy.nix ================================================ { config, pkgs, ... }: let caddy-authfiles = config.clan.core.vars.generators."caddy-basicauth".files; mkBasicAuthFiles = hosts: { files = builtins.listToAttrs ( map (h: { name = h; value = { }; }) hosts ++ (map (h: { name = "${h}.auth"; value = { owner = "caddy"; group = "caddy"; }; }) hosts) ); prompts = builtins.listToAttrs ( map (h: { name = h; value = { persist = true; description = "Username for ${h}"; }; }) hosts ); runtimeInputs = with pkgs; [ coreutils caddy xkcdpass ]; validation.script = builtins.concatStringsSep "" hosts; script = '' mkdir -p $out '' + builtins.concatStringsSep "\n" ( map (h: '' xkcdpass -d- > $out/${h} printf "%s %s" "$(cat $prompts/${h})" "$(cat $out/${h} | caddy hash-password)" \ > $out/${h}.auth '') hosts ); }; in { clan.core.vars.generators."caddy-basicauth" = mkBasicAuthFiles [ "notify.pablo.tools" ]; systemd.services.caddy.restartTriggers = map (f: caddy-authfiles.${f}.path) ( builtins.attrNames caddy-authfiles ); services.caddy = { enable = true; # Handle errors for all pages # respond "{err.status_code} {err.status_text}" extraConfig = '' :443, :80 { handle_errors { respond * "This page does not exist or is not for your eyes." { close } } } ''; # The difference between {$ and {env. is that {$ is evaluated before Caddyfile # parsing begins, and {env. is evaluated at runtime. This matters if your # config is adapted in a different environment from which it is being run. virtualHosts = { # NIP-05 identity "pinpox.com".extraConfig = '' handle /.well-known/nostr.json { header Content-Type application/json header Access-Control-Allow-Origin * root * ${./nostr} rewrite * /nostr.json file_server } respond "nothing here" 404 ''; # Homepage "pablo.tools".extraConfig = '' handle /.well-known/nostr.json { header Content-Type application/json header Access-Control-Allow-Origin * root * ${./nostr} rewrite * /nostr.json file_server } root * /var/www/pablo-tools file_server encode zstd gzip ''; # Homepage (dev) - protected by Authelia "beta.pablo.tools".extraConfig = '' forward_auth http://127.0.0.1:9091 { uri /api/authz/forward-auth copy_headers Remote-User Remote-Groups Remote-Name Remote-Email } root * /var/www/pablo-tools-beta file_server encode zstd gzip ''; # Camera (read-only) stream - protected by Authelia "3dprint.pablo.tools".extraConfig = '' forward_auth http://127.0.0.1:9091 { uri /api/authz/forward-auth copy_headers Remote-User Remote-Groups Remote-Name Remote-Email } reverse_proxy 192.168.2.121:8081 ''; # Notifications API "notify.pablo.tools".extraConfig = '' reverse_proxy 127.0.0.1:11000 basic_auth { import ${config.clan.core.vars.generators."caddy-basicauth".files."notify.pablo.tools.auth".path} } ''; # Home-assistant "home.pablo.tools".extraConfig = "reverse_proxy birne.wireguard:8123"; # Navidrome "music.pablo.tools".extraConfig = "reverse_proxy birne.wireguard:4533"; # Alertmanager "vpn.alerts.pablo.tools".extraConfig = '' @vpnonly { remote_ip 192.168.0.0/16 172.168.7.0/16 } reverse_proxy @vpnonly 127.0.0.1:9093 ''; # Prometheus "vpn.prometheus.pablo.tools".extraConfig = '' @vpnonly { remote_ip 192.168.0.0/16 172.168.7.0/16 } reverse_proxy @vpnonly 127.0.0.1:9090 ''; # ntfy "vpn.notify.pablo.tools".extraConfig = '' @vpnonly { remote_ip 192.168.0.0/16 172.168.7.0/16 } reverse_proxy @vpnonly 127.0.0.1:11000 ''; # Minio admin console "vpn.minio.pablo.tools".extraConfig = '' @vpnonly { remote_ip 192.168.0.0/16 172.168.7.0/16 } reverse_proxy @vpnonly birne.wireguard:9001 ''; # Minio s3 backend "vpn.s3.pablo.tools".extraConfig = '' @vpnonly { remote_ip 192.168.0.0/16 172.168.7.0/16 } reverse_proxy @vpnonly birne.wireguard:9000 ''; }; }; } ================================================ FILE: machines/porree/configuration.nix ================================================ { matrix-hook, config, pkgs, alertmanager-ntfy, pinpox-utils, ... }: { imports = [ ./hardware-configuration.nix matrix-hook.nixosModule alertmanager-ntfy.nixosModules.default ./caddy.nix # ./retiolum.nix ../../modules/opencrow ]; clan.core.networking.targetHost = "94.16.108.229"; networking.hostName = "porree"; networking.interfaces.ens3 = { ipv6.addresses = [ { address = "2a03:4000:51:aa3::1"; prefixLength = 64; } ]; }; clan.core.vars.generators."matrix-hook" = pinpox-utils.mkEnvGenerator [ "MX_TOKEN" ]; clan.core.vars.generators."alertmanager-ntfy" = pinpox-utils.mkEnvGenerator [ "NTFY_USER" "NTFY_PASS" ]; # Per-user authelia password generators are now auto-created by the # authelia clan service based on auth.user exports from user instances. # OIDC client secret for miniflux (generated here, shared to kfbox). # client_secret → raw value for the miniflux OIDC client side # client_secret_hash → argon2 hash for the Authelia client_secret_file clan.core.vars.generators."miniflux-oidc" = { share = true; files.client_secret.owner = "authelia-main"; files.client_secret_hash.owner = "authelia-main"; runtimeInputs = with pkgs; [ coreutils openssl authelia gnused ]; script = '' mkdir -p $out openssl rand -hex 32 > $out/client_secret authelia crypto hash generate argon2 --password "$(cat $out/client_secret)" \ | sed 's/^Digest: //' > $out/client_secret_hash ''; }; # OIDC client secret for forgejo (generated here, shared to forgejo host). # client_secret → raw value for the forgejo OIDC client side # client_secret_hash → argon2 hash for the Authelia client_secret_file clan.core.vars.generators."forgejo-oidc" = { share = true; files.client_secret.owner = "authelia-main"; files.client_secret_hash.owner = "authelia-main"; runtimeInputs = with pkgs; [ coreutils openssl authelia gnused ]; script = '' mkdir -p $out openssl rand -hex 32 > $out/client_secret authelia crypto hash generate argon2 --password "$(cat $out/client_secret)" \ | sed 's/^Digest: //' > $out/client_secret_hash ''; }; services.qemuGuest.enable = true; services.tailscale.enable = true; fileSystems."/" = { device = "/dev/disk/by-label/nixos"; fsType = "ext4"; autoResize = true; }; # Block anything that is not HTTP(s) or SSH. networking.firewall = { enable = true; allowPing = true; allowedTCPPorts = [ 80 443 22 ]; allowedUDPPorts = [ 51820 ]; interfaces.wg-clan.allowedTCPPorts = [ 2812 8086 # InfluxDB ]; }; boot.growPartition = true; boot.kernelParams = [ "console=ttyS0" ]; boot.loader.grub.device = "/dev/sda"; boot.loader.timeout = 0; programs.ssh.startAgent = false; services.alertmanager-ntfy = { enable = true; httpAddress = "localhost"; httpPort = "9099"; ntfyTopic = "https://push.pablo.tools/pinpox_alertmanager"; ntfyPriority = "default"; envFile = "${config.clan.core.vars.generators."alertmanager-ntfy".files."envfile".path}"; }; pinpox = { services = { opencrow.enable = true; # Authelia is now a clan service (clan-service-modules/authelia). # Configuration is in inventory.nix. vaultwarden.enable = true; ntfy-sh.enable = true; matrix-hook = { enable = true; httpAddress = "localhost"; matrixHomeserver = "https://matrix.org"; matrixUser = "@alertus-maximus:matrix.org"; matrixRoom = "!ilXTQgAfoBlNBuDmsz:matrix.org"; envFile = "${config.clan.core.vars.generators."matrix-hook".files."envfile".path}"; msgTemplatePath = "${matrix-hook.packages."x86_64-linux".matrix-hook}/bin/message.html.tmpl"; }; # Enable paperless-ngx document management paperless.enable = true; # Enable nextcloud configuration nextcloud.enable = true; # Enable OpenCloud (uses Authelia OIDC) opencloud.enable = true; }; }; } ================================================ FILE: machines/porree/hardware-configuration.nix ================================================ { modulesPath, ... }: { imports = [ (modulesPath + "/profiles/qemu-guest.nix") ]; } ================================================ FILE: machines/porree/nostr/nostr.json ================================================ { "names": { "pinpox": "cb1250be2faac3f301b2e9f6abc8651fbd7de5623fa7cc9b98d260ff266ce878" } } ================================================ FILE: machines/porree/retiolum.nix ================================================ { config, retiolum, ... }: { imports = [ retiolum.nixosModules.retiolum ]; networking.retiolum.ipv4 = "10.243.100.101"; networking.retiolum.ipv6 = "42:0:3c46:b51c:b34d:b7e1:3b02:8d24"; clan.core.vars.generators."retiolum" = { prompts.rsa_priv.persist = true; prompts.ed25519_priv.persist = true; }; services.tinc.networks.retiolum = { rsaPrivateKeyFile = config.clan.core.vars.generators."retiolum".files."rsa_priv".path; ed25519PrivateKeyFile = config.clan.core.vars.generators."retiolum".files."ed25519_priv".path; }; } ================================================ FILE: machines/tanne/configuration.nix ================================================ { nixos-hardware, lib, ... }: { imports = [ ./disko-config-btrfs.nix nixos-hardware.nixosModules.lenovo-thinkpad-t480s ]; networking.hostName = "tanne"; programs.steam.enable = true; programs.gamemode.enable = true; # For dual-boot boot.loader.efi.canTouchEfiVariables = true; boot.loader.grub.efiInstallAsRemovable = lib.mkForce false; } ================================================ FILE: machines/tanne/disko-config-btrfs.nix ================================================ { boot.growPartition = true; boot.supportedFilesystems.btrfs = true; services.btrfs.autoScrub = { enable = true; interval = "weekly"; # Defaults to all # fileSystems = [ "/" ]; }; disko.devices = { disk = { main = { type = "disk"; device = "/dev/nvme0n1"; content = { type = "gpt"; partitions = { ESP = { size = "512M"; type = "EF00"; content = { type = "filesystem"; format = "vfat"; mountpoint = "/boot"; mountOptions = [ "umask=0077" ]; }; }; windows = { size = "512G"; type = "EF00"; content = { type = "filesystem"; format = "vfat"; }; }; luks = { size = "100%"; content = { type = "luks"; name = "crypted"; # disable settings.keyFile if you want to use interactive password entry #passwordFile = "/tmp/secret.key"; # Interactive settings = { allowDiscards = true; # keyFile = "/tmp/secret.key"; }; # additionalKeyFiles = [ "/tmp/additionalSecret.key" ]; content = { type = "btrfs"; extraArgs = [ "-f" ]; subvolumes = { "/root" = { mountpoint = "/"; mountOptions = [ "compress=zstd" "noatime" ]; }; "/home" = { mountpoint = "/home"; mountOptions = [ "compress=zstd" "noatime" ]; }; "/nix" = { mountpoint = "/nix"; mountOptions = [ "compress=zstd" "noatime" ]; }; "/swap" = { mountpoint = "/.swapvol"; swap.swapfile.size = "8G"; }; }; }; }; }; }; }; }; }; }; } ================================================ FILE: machines/traube/configuration.nix ================================================ { pkgs, lib, ... }: { imports = [ ]; nixpkgs.hostPlatform = "aarch64-linux"; # Lock CPU governor to performance mode (avoid frequency scaling latency) powerManagement.cpuFreqGovernor = "performance"; boot.kernel.sysctl = { "vm.swappiness" = 0; }; } ================================================ FILE: machines/traube/disko.nix ================================================ # --- # schema = "btrfs-single-disk-subvolumes" # [placeholders] # mainDisk = "/dev/disk/by-id/nvme-Samsung_SSD_970_EVO_1TB_S467NX0MB03860Y" # --- # This file was automatically generated! # CHANGING this configuration requires wiping and reinstalling the machine { boot.loader.grub = { efiInstallAsRemovable = true; efiSupport = true; }; disko.devices = { disk = { "main" = { name = "main-9bea948ae69a4a528dcdabbe9fe39bfb"; device = "/dev/disk/by-id/nvme-Samsung_SSD_970_EVO_1TB_S467NX0MB03860Y"; type = "disk"; content = { type = "gpt"; partitions = { "boot" = { size = "1M"; type = "EF02"; # for grub MBR priority = 1; }; "ESP" = { type = "EF00"; size = "500M"; content = { type = "filesystem"; format = "vfat"; mountpoint = "/boot"; mountOptions = [ "umask=0077" ]; }; }; #"swap" = { # size = "8G"; # adjust # content = { # type = "swap"; # discardPolicy = "both"; # }; #}; "root" = { size = "100%"; content = { type = "btrfs"; extraArgs = [ "--force" "--label root" ]; subvolumes = { "@root" = { mountpoint = "/"; mountOptions = [ ]; }; "@nix" = { mountpoint = "/nix"; mountOptions = [ "compress=zstd" "noatime" ]; }; "@home" = { mountpoint = "/home"; mountOptions = [ "compress=zstd" ]; }; }; }; }; }; }; }; }; }; # Automatic local snapshots # https://digint.ch/btrbk/doc/readme.html #$ systemctl start btrbk- services.btrbk = { instances."nix" = { onCalendar = "*/2:00"; settings = { subvolume = "/nix"; snapshot_create = "onchange"; snapshot_dir = "/nix"; snapshot_preserve = "16h 7d 2w"; snapshot_preserve_min = "3d"; }; }; instances."home" = { onCalendar = "*/2:00"; settings = { subvolume = "/home"; snapshot_dir = "/home"; snapshot_preserve = "16h 7d 3w 2m"; snapshot_preserve_min = "3d"; }; }; }; } ================================================ FILE: machines/traube/facter.json ================================================ { "version": 1, "system": "aarch64-linux", "virtualisation": "none", "uefi": { "supported": true, "platform_size": 64 }, "hardware": { "bridge": [ { "index": 16, "attached_to": 0, "class_list": [ "pci", "bridge" ], "bus_type": { "hex": "0004", "name": "PCI", "value": 4 }, "slot": { "bus": 48, "number": 0 }, "base_class": { "hex": "0006", "name": "Bridge", "value": 6 }, "sub_class": { "hex": "0004", "name": "PCI bridge", "value": 4 }, "pci_interface": { "hex": "0000", "name": "Normal decode", "value": 0 }, "vendor": { "hex": "17cd", "value": 6093 }, "device": { "hex": "0100", "value": 256 }, "model": "PCI bridge", "sysfs_id": "/devices/pci0000:30/0000:30:00.0", "sysfs_bus_id": "0000:30:00.0", "sysfs_iommu_group_id": 1, "detail": { "function": 0, "command": 7, "header_type": 1, "secondary_bus": 49, "prog_if": 0 }, "driver": "pcieport", "driver_module": "pcieportdrv", "drivers": [ "pcieport" ], "driver_modules": [ "pcieportdrv" ], "module_alias": "pci:v000017CDd00000100sv00000000sd00000000bc06sc04i00" }, { "index": 18, "attached_to": 0, "class_list": [ "pci", "bridge" ], "bus_type": { "hex": "0004", "name": "PCI", "value": 4 }, "slot": { "bus": 0, "number": 0 }, "base_class": { "hex": "0006", "name": "Bridge", "value": 6 }, "sub_class": { "hex": "0004", "name": "PCI bridge", "value": 4 }, "pci_interface": { "hex": "0000", "name": "Normal decode", "value": 0 }, "vendor": { "hex": "17cd", "value": 6093 }, "device": { "hex": "0100", "value": 256 }, "model": "PCI bridge", "sysfs_id": "/devices/pci0000:00/0000:00:00.0", "sysfs_bus_id": "0000:00:00.0", "sysfs_iommu_group_id": 2, "detail": { "function": 0, "command": 7, "header_type": 1, "secondary_bus": 1, "prog_if": 0 }, "driver": "pcieport", "driver_module": "pcieportdrv", "drivers": [ "pcieport" ], "driver_modules": [ "pcieportdrv" ], "module_alias": "pci:v000017CDd00000100sv00000000sd00000000bc06sc04i00" }, { "index": 20, "attached_to": 0, "class_list": [ "pci", "bridge" ], "bus_type": { "hex": "0004", "name": "PCI", "value": 4 }, "slot": { "bus": 144, "number": 0 }, "base_class": { "hex": "0006", "name": "Bridge", "value": 6 }, "sub_class": { "hex": "0004", "name": "PCI bridge", "value": 4 }, "pci_interface": { "hex": "0000", "name": "Normal decode", "value": 0 }, "vendor": { "hex": "17cd", "value": 6093 }, "device": { "hex": "0100", "value": 256 }, "model": "PCI bridge", "sysfs_id": "/devices/pci0000:90/0000:90:00.0", "sysfs_bus_id": "0000:90:00.0", "sysfs_iommu_group_id": 0, "detail": { "function": 0, "command": 7, "header_type": 1, "secondary_bus": 145, "prog_if": 0 }, "driver": "pcieport", "driver_module": "pcieportdrv", "drivers": [ "pcieport" ], "driver_modules": [ "pcieportdrv" ], "module_alias": "pci:v000017CDd00000100sv00000000sd00000000bc06sc04i00" } ], "cpu": [ { "architecture": "aarch64", "vendor_name": "ARM Limited", "family": 0, "model": 1, "stepping": 0, "features": [ "fp", "asimd", "evtstrm", "aes", "pmull", "sha1", "sha2", "crc32", "atomics", "fphp", "asimdhp", "cpuid", "asimdrdm", "jscvt", "fcma", "lrcpc", "dcpop", "sha3", "sm3", "sm4", "asimddp", "sha512", "sve", "asimdfhm", "dit", "uscat", "ilrcpc", "flagm", "sb", "paca", "pacg", "dcpodp", "sve2", "sveaes", "svepmull", "svebitperm", "svesha3", "svesm4", "flagm2", "frint", "svei8mm", "svebf16", "i8mm", "bf16", "dgh", "bti", "mte", "ecv", "afp", "mte3", "wfxt" ], "bogo": 2000, "page_size": 4096, "physical_id": 0, "fpu": false, "fpu_exception": false, "write_protect": false, "address_sizes": { "physical": "0x0", "virtual": "0x0" } } ], "disk": [ { "index": 21, "attached_to": 19, "class_list": [ "disk", "block_device", "nvme" ], "bus_type": { "hex": "0096", "name": "NVME", "value": 150 }, "slot": { "bus": 0, "number": 0 }, "base_class": { "hex": "0106", "name": "Mass Storage Device", "value": 262 }, "sub_class": { "hex": "0000", "name": "Disk", "value": 0 }, "vendor": { "hex": "144d", "value": 5197 }, "sub_vendor": { "hex": "144d", "value": 5197 }, "device": { "hex": "a808", "name": "Samsung SSD 970 EVO 1TB", "value": 43016 }, "sub_device": { "hex": "a801", "value": 43009 }, "serial": "S467NX0MB03860Y", "model": "Samsung SSD 970 EVO 1TB", "sysfs_id": "/class/block/nvme0n1", "sysfs_bus_id": "nvme0", "sysfs_device_link": "/devices/pci0000:90/0000:90:00.0/0000:91:00.0/nvme/nvme0", "unix_device_names": [ "/dev/disk/by-id/nvme-Samsung_SSD_970_EVO_1TB_S467NX0MB03860Y", "/dev/disk/by-id/nvme-Samsung_SSD_970_EVO_1TB_S467NX0MB03860Y_1", "/dev/disk/by-id/nvme-eui.0025385b91b0fe46", "/dev/disk/by-path/pci-0000:91:00.0-nvme-1", "/dev/nvme0n1" ], "resources": [ { "type": "disk_geo", "cylinders": 953869, "heads": 64, "sectors": 32, "size": "0x0", "geo_type": "logical" }, { "type": "size", "unit": "sectors", "value_1": 1953525168, "value_2": 512 } ], "driver": "nvme", "driver_module": "nvme", "drivers": [ "nvme" ], "driver_modules": [ "nvme" ] }, { "index": 22, "attached_to": 0, "class_list": [ "disk", "usb", "scsi", "block_device" ], "bus_type": { "hex": "0084", "name": "SCSI", "value": 132 }, "slot": { "bus": 0, "number": 0 }, "base_class": { "hex": "0106", "name": "Mass Storage Device", "value": 262 }, "sub_class": { "hex": "0000", "name": "Disk", "value": 0 }, "vendor": { "hex": "0781", "name": "USB", "value": 1921 }, "device": { "hex": "5583", "name": "SanDisk 3.2Gen1", "value": 21891 }, "revision": { "hex": "0000", "name": "1.00", "value": 0 }, "serial": "01017a33f8c1d6bbbfe7", "model": "USB SanDisk 3.2Gen1", "sysfs_id": "/class/block/sda", "sysfs_bus_id": "0:0:0:0", "sysfs_device_link": "/devices/platform/PNP0D10:04/usb10/10-1/10-1:1.0/host0/target0:0:0/0:0:0:0", "unix_device_names": [ "/dev/disk/by-id/usb-USB_SanDisk_3.2Gen1_01017a33f8c1d6bbbfe70a7b26cd37feae47d7bdcd66539fbda070dffc8874bcaa990000000000000000000020b7a975008a4b00835581076e2f52e7-0:0", "/dev/disk/by-label/nixos-minimal-26.05-aarch64", "/dev/disk/by-path/platform-PNP0D10:04-usb-0:1:1.0-scsi-0:0:0:0", "/dev/disk/by-path/platform-PNP0D10:04-usbv3-0:1:1.0-scsi-0:0:0:0", "/dev/disk/by-uuid/1980-01-01-00-00-00-00", "/dev/root", "/dev/sda" ], "unix_device_name2": "/dev/sg0", "resources": [ { "type": "disk_geo", "cylinders": 58656, "heads": 64, "sectors": 32, "size": "0x0", "geo_type": "logical" }, { "type": "size", "unit": "sectors", "value_1": 120127488, "value_2": 512 } ], "driver": "usb-storage", "driver_module": "usb_storage", "drivers": [ "sd", "usb-storage" ], "driver_modules": [ "usb_storage" ], "module_alias": "usb:v0781p5583d0100dc00dsc00dp00ic08isc06ip50in00" } ], "hub": [ { "index": 23, "attached_to": 0, "class_list": [ "usb", "hub" ], "bus_type": { "hex": "0086", "name": "USB", "value": 134 }, "slot": { "bus": 0, "number": 0 }, "base_class": { "hex": "010a", "name": "Hub", "value": 266 }, "vendor": { "hex": "1d6b", "name": "Linux 6.18.16 xhci-hcd", "value": 7531 }, "device": { "hex": "0003", "name": "xHCI Host Controller", "value": 3 }, "revision": { "hex": "0000", "name": "6.18", "value": 0 }, "serial": "PNP0D10:02", "model": "Linux 6.18.16 xhci-hcd xHCI Host Controller", "sysfs_id": "/devices/platform/PNP0D10:02/usb6/6-0:1.0", "sysfs_bus_id": "6-0:1.0", "detail": { "device_class": { "hex": "0009", "name": "hub", "value": 9 }, "device_subclass": { "hex": "0000", "name": "per_interface", "value": 0 }, "device_protocol": 3, "interface_class": { "hex": "0009", "name": "hub", "value": 9 }, "interface_subclass": { "hex": "0000", "name": "per_interface", "value": 0 }, "interface_protocol": 0, "interface_number": 0, "interface_alternate_setting": 0 }, "hotplug": "usb", "driver": "hub", "driver_module": "usbcore", "drivers": [ "hub" ], "driver_modules": [ "usbcore" ], "module_alias": "usb:v1D6Bp0003d0618dc09dsc00dp03ic09isc00ip00in00" }, { "index": 24, "attached_to": 0, "class_list": [ "usb", "hub" ], "bus_type": { "hex": "0086", "name": "USB", "value": 134 }, "slot": { "bus": 0, "number": 0 }, "base_class": { "hex": "010a", "name": "Hub", "value": 266 }, "vendor": { "hex": "1d6b", "name": "Linux 6.18.16 xhci-hcd", "value": 7531 }, "device": { "hex": "0002", "name": "xHCI Host Controller", "value": 2 }, "revision": { "hex": "0000", "name": "6.18", "value": 0 }, "serial": "PNP0D10:09", "model": "Linux 6.18.16 xhci-hcd xHCI Host Controller", "sysfs_id": "/devices/platform/PNP0D10:09/usb16/16-0:1.0", "sysfs_bus_id": "16-0:1.0", "resources": [ { "type": "baud", "speed": 480000000, "bits": 0, "stop_bits": 0, "parity": 0, "handshake": 0 } ], "detail": { "device_class": { "hex": "0009", "name": "hub", "value": 9 }, "device_subclass": { "hex": "0000", "name": "per_interface", "value": 0 }, "device_protocol": 1, "interface_class": { "hex": "0009", "name": "hub", "value": 9 }, "interface_subclass": { "hex": "0000", "name": "per_interface", "value": 0 }, "interface_protocol": 0, "interface_number": 0, "interface_alternate_setting": 0 }, "hotplug": "usb", "driver": "hub", "driver_module": "usbcore", "drivers": [ "hub" ], "driver_modules": [ "usbcore" ], "module_alias": "usb:v1D6Bp0002d0618dc09dsc00dp01ic09isc00ip00in00" }, { "index": 25, "attached_to": 0, "class_list": [ "usb", "hub" ], "bus_type": { "hex": "0086", "name": "USB", "value": 134 }, "slot": { "bus": 0, "number": 0 }, "base_class": { "hex": "010a", "name": "Hub", "value": 266 }, "vendor": { "hex": "1d6b", "name": "Linux 6.18.16 xhci-hcd", "value": 7531 }, "device": { "hex": "0002", "name": "xHCI Host Controller", "value": 2 }, "revision": { "hex": "0000", "name": "6.18", "value": 0 }, "serial": "PNP0D10:01", "model": "Linux 6.18.16 xhci-hcd xHCI Host Controller", "sysfs_id": "/devices/platform/PNP0D10:01/usb3/3-0:1.0", "sysfs_bus_id": "3-0:1.0", "resources": [ { "type": "baud", "speed": 480000000, "bits": 0, "stop_bits": 0, "parity": 0, "handshake": 0 } ], "detail": { "device_class": { "hex": "0009", "name": "hub", "value": 9 }, "device_subclass": { "hex": "0000", "name": "per_interface", "value": 0 }, "device_protocol": 1, "interface_class": { "hex": "0009", "name": "hub", "value": 9 }, "interface_subclass": { "hex": "0000", "name": "per_interface", "value": 0 }, "interface_protocol": 0, "interface_number": 0, "interface_alternate_setting": 0 }, "hotplug": "usb", "driver": "hub", "driver_module": "usbcore", "drivers": [ "hub" ], "driver_modules": [ "usbcore" ], "module_alias": "usb:v1D6Bp0002d0618dc09dsc00dp01ic09isc00ip00in00" }, { "index": 26, "attached_to": 0, "class_list": [ "usb", "hub" ], "bus_type": { "hex": "0086", "name": "USB", "value": 134 }, "slot": { "bus": 0, "number": 0 }, "base_class": { "hex": "010a", "name": "Hub", "value": 266 }, "vendor": { "hex": "1d6b", "name": "Linux 6.18.16 xhci-hcd", "value": 7531 }, "device": { "hex": "0002", "name": "xHCI Host Controller", "value": 2 }, "revision": { "hex": "0000", "name": "6.18", "value": 0 }, "serial": "PNP0D10:06", "model": "Linux 6.18.16 xhci-hcd xHCI Host Controller", "sysfs_id": "/devices/platform/PNP0D10:06/usb13/13-0:1.0", "sysfs_bus_id": "13-0:1.0", "resources": [ { "type": "baud", "speed": 480000000, "bits": 0, "stop_bits": 0, "parity": 0, "handshake": 0 } ], "detail": { "device_class": { "hex": "0009", "name": "hub", "value": 9 }, "device_subclass": { "hex": "0000", "name": "per_interface", "value": 0 }, "device_protocol": 1, "interface_class": { "hex": "0009", "name": "hub", "value": 9 }, "interface_subclass": { "hex": "0000", "name": "per_interface", "value": 0 }, "interface_protocol": 0, "interface_number": 0, "interface_alternate_setting": 0 }, "hotplug": "usb", "driver": "hub", "driver_module": "usbcore", "drivers": [ "hub" ], "driver_modules": [ "usbcore" ], "module_alias": "usb:v1D6Bp0002d0618dc09dsc00dp01ic09isc00ip00in00" }, { "index": 27, "attached_to": 0, "class_list": [ "usb", "hub" ], "bus_type": { "hex": "0086", "name": "USB", "value": 134 }, "slot": { "bus": 0, "number": 0 }, "base_class": { "hex": "010a", "name": "Hub", "value": 266 }, "vendor": { "hex": "1d6b", "name": "Linux 6.18.16 xhci-hcd", "value": 7531 }, "device": { "hex": "0002", "name": "xHCI Host Controller", "value": 2 }, "revision": { "hex": "0000", "name": "6.18", "value": 0 }, "serial": "PNP0D10:03", "model": "Linux 6.18.16 xhci-hcd xHCI Host Controller", "sysfs_id": "/devices/platform/PNP0D10:03/usb7/7-0:1.0", "sysfs_bus_id": "7-0:1.0", "resources": [ { "type": "baud", "speed": 480000000, "bits": 0, "stop_bits": 0, "parity": 0, "handshake": 0 } ], "detail": { "device_class": { "hex": "0009", "name": "hub", "value": 9 }, "device_subclass": { "hex": "0000", "name": "per_interface", "value": 0 }, "device_protocol": 1, "interface_class": { "hex": "0009", "name": "hub", "value": 9 }, "interface_subclass": { "hex": "0000", "name": "per_interface", "value": 0 }, "interface_protocol": 0, "interface_number": 0, "interface_alternate_setting": 0 }, "hotplug": "usb", "driver": "hub", "driver_module": "usbcore", "drivers": [ "hub" ], "driver_modules": [ "usbcore" ], "module_alias": "usb:v1D6Bp0002d0618dc09dsc00dp01ic09isc00ip00in00" }, { "index": 28, "attached_to": 0, "class_list": [ "usb", "hub" ], "bus_type": { "hex": "0086", "name": "USB", "value": 134 }, "slot": { "bus": 0, "number": 0 }, "base_class": { "hex": "010a", "name": "Hub", "value": 266 }, "vendor": { "hex": "1d6b", "name": "Linux 6.18.16 xhci-hcd", "value": 7531 }, "device": { "hex": "0003", "name": "xHCI Host Controller", "value": 3 }, "revision": { "hex": "0000", "name": "6.18", "value": 0 }, "serial": "PNP0D10:04", "model": "Linux 6.18.16 xhci-hcd xHCI Host Controller", "sysfs_id": "/devices/platform/PNP0D10:04/usb10/10-0:1.0", "sysfs_bus_id": "10-0:1.0", "detail": { "device_class": { "hex": "0009", "name": "hub", "value": 9 }, "device_subclass": { "hex": "0000", "name": "per_interface", "value": 0 }, "device_protocol": 3, "interface_class": { "hex": "0009", "name": "hub", "value": 9 }, "interface_subclass": { "hex": "0000", "name": "per_interface", "value": 0 }, "interface_protocol": 0, "interface_number": 0, "interface_alternate_setting": 0 }, "hotplug": "usb", "driver": "hub", "driver_module": "usbcore", "drivers": [ "hub" ], "driver_modules": [ "usbcore" ], "module_alias": "usb:v1D6Bp0003d0618dc09dsc00dp03ic09isc00ip00in00" }, { "index": 29, "attached_to": 0, "class_list": [ "usb", "hub" ], "bus_type": { "hex": "0086", "name": "USB", "value": 134 }, "slot": { "bus": 0, "number": 0 }, "base_class": { "hex": "010a", "name": "Hub", "value": 266 }, "vendor": { "hex": "1d6b", "name": "Linux 6.18.16 xhci-hcd", "value": 7531 }, "device": { "hex": "0003", "name": "xHCI Host Controller", "value": 3 }, "revision": { "hex": "0000", "name": "6.18", "value": 0 }, "serial": "PNP0D10:01", "model": "Linux 6.18.16 xhci-hcd xHCI Host Controller", "sysfs_id": "/devices/platform/PNP0D10:01/usb4/4-0:1.0", "sysfs_bus_id": "4-0:1.0", "detail": { "device_class": { "hex": "0009", "name": "hub", "value": 9 }, "device_subclass": { "hex": "0000", "name": "per_interface", "value": 0 }, "device_protocol": 3, "interface_class": { "hex": "0009", "name": "hub", "value": 9 }, "interface_subclass": { "hex": "0000", "name": "per_interface", "value": 0 }, "interface_protocol": 0, "interface_number": 0, "interface_alternate_setting": 0 }, "hotplug": "usb", "driver": "hub", "driver_module": "usbcore", "drivers": [ "hub" ], "driver_modules": [ "usbcore" ], "module_alias": "usb:v1D6Bp0003d0618dc09dsc00dp03ic09isc00ip00in00" }, { "index": 30, "attached_to": 0, "class_list": [ "usb", "hub" ], "bus_type": { "hex": "0086", "name": "USB", "value": 134 }, "slot": { "bus": 0, "number": 0 }, "base_class": { "hex": "010a", "name": "Hub", "value": 266 }, "vendor": { "hex": "1d6b", "name": "Linux 6.18.16 xhci-hcd", "value": 7531 }, "device": { "hex": "0002", "name": "xHCI Host Controller", "value": 2 }, "revision": { "hex": "0000", "name": "6.18", "value": 0 }, "serial": "PNP0D10:07", "model": "Linux 6.18.16 xhci-hcd xHCI Host Controller", "sysfs_id": "/devices/platform/PNP0D10:07/usb14/14-0:1.0", "sysfs_bus_id": "14-0:1.0", "resources": [ { "type": "baud", "speed": 480000000, "bits": 0, "stop_bits": 0, "parity": 0, "handshake": 0 } ], "detail": { "device_class": { "hex": "0009", "name": "hub", "value": 9 }, "device_subclass": { "hex": "0000", "name": "per_interface", "value": 0 }, "device_protocol": 1, "interface_class": { "hex": "0009", "name": "hub", "value": 9 }, "interface_subclass": { "hex": "0000", "name": "per_interface", "value": 0 }, "interface_protocol": 0, "interface_number": 0, "interface_alternate_setting": 0 }, "hotplug": "usb", "driver": "hub", "driver_module": "usbcore", "drivers": [ "hub" ], "driver_modules": [ "usbcore" ], "module_alias": "usb:v1D6Bp0002d0618dc09dsc00dp01ic09isc00ip00in00" }, { "index": 31, "attached_to": 0, "class_list": [ "usb", "hub" ], "bus_type": { "hex": "0086", "name": "USB", "value": 134 }, "slot": { "bus": 0, "number": 0 }, "base_class": { "hex": "010a", "name": "Hub", "value": 266 }, "vendor": { "hex": "1d6b", "name": "Linux 6.18.16 xhci-hcd", "value": 7531 }, "device": { "hex": "0003", "name": "xHCI Host Controller", "value": 3 }, "revision": { "hex": "0000", "name": "6.18", "value": 0 }, "serial": "PNP0D10:03", "model": "Linux 6.18.16 xhci-hcd xHCI Host Controller", "sysfs_id": "/devices/platform/PNP0D10:03/usb8/8-0:1.0", "sysfs_bus_id": "8-0:1.0", "detail": { "device_class": { "hex": "0009", "name": "hub", "value": 9 }, "device_subclass": { "hex": "0000", "name": "per_interface", "value": 0 }, "device_protocol": 3, "interface_class": { "hex": "0009", "name": "hub", "value": 9 }, "interface_subclass": { "hex": "0000", "name": "per_interface", "value": 0 }, "interface_protocol": 0, "interface_number": 0, "interface_alternate_setting": 0 }, "hotplug": "usb", "driver": "hub", "driver_module": "usbcore", "drivers": [ "hub" ], "driver_modules": [ "usbcore" ], "module_alias": "usb:v1D6Bp0003d0618dc09dsc00dp03ic09isc00ip00in00" }, { "index": 32, "attached_to": 0, "class_list": [ "usb", "hub" ], "bus_type": { "hex": "0086", "name": "USB", "value": 134 }, "slot": { "bus": 0, "number": 0 }, "base_class": { "hex": "010a", "name": "Hub", "value": 266 }, "vendor": { "hex": "1d6b", "name": "Linux 6.18.16 xhci-hcd", "value": 7531 }, "device": { "hex": "0002", "name": "xHCI Host Controller", "value": 2 }, "revision": { "hex": "0000", "name": "6.18", "value": 0 }, "serial": "PNP0D10:00", "model": "Linux 6.18.16 xhci-hcd xHCI Host Controller", "sysfs_id": "/devices/platform/PNP0D10:00/usb1/1-0:1.0", "sysfs_bus_id": "1-0:1.0", "resources": [ { "type": "baud", "speed": 480000000, "bits": 0, "stop_bits": 0, "parity": 0, "handshake": 0 } ], "detail": { "device_class": { "hex": "0009", "name": "hub", "value": 9 }, "device_subclass": { "hex": "0000", "name": "per_interface", "value": 0 }, "device_protocol": 1, "interface_class": { "hex": "0009", "name": "hub", "value": 9 }, "interface_subclass": { "hex": "0000", "name": "per_interface", "value": 0 }, "interface_protocol": 0, "interface_number": 0, "interface_alternate_setting": 0 }, "hotplug": "usb", "driver": "hub", "driver_module": "usbcore", "drivers": [ "hub" ], "driver_modules": [ "usbcore" ], "module_alias": "usb:v1D6Bp0002d0618dc09dsc00dp01ic09isc00ip00in00" }, { "index": 34, "attached_to": 0, "class_list": [ "usb", "hub" ], "bus_type": { "hex": "0086", "name": "USB", "value": 134 }, "slot": { "bus": 0, "number": 0 }, "base_class": { "hex": "010a", "name": "Hub", "value": 266 }, "vendor": { "hex": "1d6b", "name": "Linux 6.18.16 xhci-hcd", "value": 7531 }, "device": { "hex": "0002", "name": "xHCI Host Controller", "value": 2 }, "revision": { "hex": "0000", "name": "6.18", "value": 0 }, "serial": "PNP0D10:05", "model": "Linux 6.18.16 xhci-hcd xHCI Host Controller", "sysfs_id": "/devices/platform/PNP0D10:05/usb11/11-0:1.0", "sysfs_bus_id": "11-0:1.0", "resources": [ { "type": "baud", "speed": 480000000, "bits": 0, "stop_bits": 0, "parity": 0, "handshake": 0 } ], "detail": { "device_class": { "hex": "0009", "name": "hub", "value": 9 }, "device_subclass": { "hex": "0000", "name": "per_interface", "value": 0 }, "device_protocol": 1, "interface_class": { "hex": "0009", "name": "hub", "value": 9 }, "interface_subclass": { "hex": "0000", "name": "per_interface", "value": 0 }, "interface_protocol": 0, "interface_number": 0, "interface_alternate_setting": 0 }, "hotplug": "usb", "driver": "hub", "driver_module": "usbcore", "drivers": [ "hub" ], "driver_modules": [ "usbcore" ], "module_alias": "usb:v1D6Bp0002d0618dc09dsc00dp01ic09isc00ip00in00" }, { "index": 35, "attached_to": 0, "class_list": [ "usb", "hub" ], "bus_type": { "hex": "0086", "name": "USB", "value": 134 }, "slot": { "bus": 0, "number": 0 }, "base_class": { "hex": "010a", "name": "Hub", "value": 266 }, "vendor": { "hex": "1d6b", "name": "Linux 6.18.16 xhci-hcd", "value": 7531 }, "device": { "hex": "0002", "name": "xHCI Host Controller", "value": 2 }, "revision": { "hex": "0000", "name": "6.18", "value": 0 }, "serial": "PNP0D10:02", "model": "Linux 6.18.16 xhci-hcd xHCI Host Controller", "sysfs_id": "/devices/platform/PNP0D10:02/usb5/5-0:1.0", "sysfs_bus_id": "5-0:1.0", "resources": [ { "type": "baud", "speed": 480000000, "bits": 0, "stop_bits": 0, "parity": 0, "handshake": 0 } ], "detail": { "device_class": { "hex": "0009", "name": "hub", "value": 9 }, "device_subclass": { "hex": "0000", "name": "per_interface", "value": 0 }, "device_protocol": 1, "interface_class": { "hex": "0009", "name": "hub", "value": 9 }, "interface_subclass": { "hex": "0000", "name": "per_interface", "value": 0 }, "interface_protocol": 0, "interface_number": 0, "interface_alternate_setting": 0 }, "hotplug": "usb", "driver": "hub", "driver_module": "usbcore", "drivers": [ "hub" ], "driver_modules": [ "usbcore" ], "module_alias": "usb:v1D6Bp0002d0618dc09dsc00dp01ic09isc00ip00in00" }, { "index": 36, "attached_to": 0, "class_list": [ "usb", "hub" ], "bus_type": { "hex": "0086", "name": "USB", "value": 134 }, "slot": { "bus": 0, "number": 0 }, "base_class": { "hex": "010a", "name": "Hub", "value": 266 }, "vendor": { "hex": "1d6b", "name": "Linux 6.18.16 xhci-hcd", "value": 7531 }, "device": { "hex": "0002", "name": "xHCI Host Controller", "value": 2 }, "revision": { "hex": "0000", "name": "6.18", "value": 0 }, "serial": "PNP0D10:08", "model": "Linux 6.18.16 xhci-hcd xHCI Host Controller", "sysfs_id": "/devices/platform/PNP0D10:08/usb15/15-0:1.0", "sysfs_bus_id": "15-0:1.0", "resources": [ { "type": "baud", "speed": 480000000, "bits": 0, "stop_bits": 0, "parity": 0, "handshake": 0 } ], "detail": { "device_class": { "hex": "0009", "name": "hub", "value": 9 }, "device_subclass": { "hex": "0000", "name": "per_interface", "value": 0 }, "device_protocol": 1, "interface_class": { "hex": "0009", "name": "hub", "value": 9 }, "interface_subclass": { "hex": "0000", "name": "per_interface", "value": 0 }, "interface_protocol": 0, "interface_number": 0, "interface_alternate_setting": 0 }, "hotplug": "usb", "driver": "hub", "driver_module": "usbcore", "drivers": [ "hub" ], "driver_modules": [ "usbcore" ], "module_alias": "usb:v1D6Bp0002d0618dc09dsc00dp01ic09isc00ip00in00" }, { "index": 37, "attached_to": 0, "class_list": [ "usb", "hub" ], "bus_type": { "hex": "0086", "name": "USB", "value": 134 }, "slot": { "bus": 0, "number": 0 }, "base_class": { "hex": "010a", "name": "Hub", "value": 266 }, "vendor": { "hex": "1d6b", "name": "Linux 6.18.16 xhci-hcd", "value": 7531 }, "device": { "hex": "0002", "name": "xHCI Host Controller", "value": 2 }, "revision": { "hex": "0000", "name": "6.18", "value": 0 }, "serial": "PNP0D10:04", "model": "Linux 6.18.16 xhci-hcd xHCI Host Controller", "sysfs_id": "/devices/platform/PNP0D10:04/usb9/9-0:1.0", "sysfs_bus_id": "9-0:1.0", "resources": [ { "type": "baud", "speed": 480000000, "bits": 0, "stop_bits": 0, "parity": 0, "handshake": 0 } ], "detail": { "device_class": { "hex": "0009", "name": "hub", "value": 9 }, "device_subclass": { "hex": "0000", "name": "per_interface", "value": 0 }, "device_protocol": 1, "interface_class": { "hex": "0009", "name": "hub", "value": 9 }, "interface_subclass": { "hex": "0000", "name": "per_interface", "value": 0 }, "interface_protocol": 0, "interface_number": 0, "interface_alternate_setting": 0 }, "hotplug": "usb", "driver": "hub", "driver_module": "usbcore", "drivers": [ "hub" ], "driver_modules": [ "usbcore" ], "module_alias": "usb:v1D6Bp0002d0618dc09dsc00dp01ic09isc00ip00in00" }, { "index": 38, "attached_to": 0, "class_list": [ "usb", "hub" ], "bus_type": { "hex": "0086", "name": "USB", "value": 134 }, "slot": { "bus": 0, "number": 0 }, "base_class": { "hex": "010a", "name": "Hub", "value": 266 }, "vendor": { "hex": "1d6b", "name": "Linux 6.18.16 xhci-hcd", "value": 7531 }, "device": { "hex": "0003", "name": "xHCI Host Controller", "value": 3 }, "revision": { "hex": "0000", "name": "6.18", "value": 0 }, "serial": "PNP0D10:00", "model": "Linux 6.18.16 xhci-hcd xHCI Host Controller", "sysfs_id": "/devices/platform/PNP0D10:00/usb2/2-0:1.0", "sysfs_bus_id": "2-0:1.0", "detail": { "device_class": { "hex": "0009", "name": "hub", "value": 9 }, "device_subclass": { "hex": "0000", "name": "per_interface", "value": 0 }, "device_protocol": 3, "interface_class": { "hex": "0009", "name": "hub", "value": 9 }, "interface_subclass": { "hex": "0000", "name": "per_interface", "value": 0 }, "interface_protocol": 0, "interface_number": 0, "interface_alternate_setting": 0 }, "hotplug": "usb", "driver": "hub", "driver_module": "usbcore", "drivers": [ "hub" ], "driver_modules": [ "usbcore" ], "module_alias": "usb:v1D6Bp0003d0618dc09dsc00dp03ic09isc00ip00in00" }, { "index": 39, "attached_to": 0, "class_list": [ "usb", "hub" ], "bus_type": { "hex": "0086", "name": "USB", "value": 134 }, "slot": { "bus": 0, "number": 0 }, "base_class": { "hex": "010a", "name": "Hub", "value": 266 }, "vendor": { "hex": "1d6b", "name": "Linux 6.18.16 xhci-hcd", "value": 7531 }, "device": { "hex": "0003", "name": "xHCI Host Controller", "value": 3 }, "revision": { "hex": "0000", "name": "6.18", "value": 0 }, "serial": "PNP0D10:05", "model": "Linux 6.18.16 xhci-hcd xHCI Host Controller", "sysfs_id": "/devices/platform/PNP0D10:05/usb12/12-0:1.0", "sysfs_bus_id": "12-0:1.0", "detail": { "device_class": { "hex": "0009", "name": "hub", "value": 9 }, "device_subclass": { "hex": "0000", "name": "per_interface", "value": 0 }, "device_protocol": 3, "interface_class": { "hex": "0009", "name": "hub", "value": 9 }, "interface_subclass": { "hex": "0000", "name": "per_interface", "value": 0 }, "interface_protocol": 0, "interface_number": 0, "interface_alternate_setting": 0 }, "hotplug": "usb", "driver": "hub", "driver_module": "usbcore", "drivers": [ "hub" ], "driver_modules": [ "usbcore" ], "module_alias": "usb:v1D6Bp0003d0618dc09dsc00dp03ic09isc00ip00in00" } ], "memory": [ { "index": 14, "attached_to": 0, "class_list": [ "memory" ], "base_class": { "hex": "0101", "name": "Internally Used Class", "value": 257 }, "sub_class": { "hex": "0002", "name": "Main Memory", "value": 2 }, "model": "Main Memory", "resources": [ { "type": "phys_mem", "range": 34359738368 } ] } ], "network_controller": [ { "index": 15, "attached_to": 16, "class_list": [ "network_controller", "pci" ], "bus_type": { "hex": "0004", "name": "PCI", "value": 4 }, "slot": { "bus": 49, "number": 0 }, "base_class": { "hex": "0002", "name": "Network controller", "value": 2 }, "sub_class": { "hex": "0000", "name": "Ethernet controller", "value": 0 }, "vendor": { "hex": "10ec", "value": 4332 }, "sub_vendor": { "hex": "10ec", "value": 4332 }, "device": { "hex": "8125", "value": 33061 }, "sub_device": { "hex": "0123", "value": 291 }, "revision": { "hex": "0005", "value": 5 }, "model": "Ethernet controller", "sysfs_id": "/devices/pci0000:30/0000:30:00.0/0000:31:00.0", "sysfs_bus_id": "0000:31:00.0", "sysfs_iommu_group_id": 1, "unix_device_names": [ "enp49s0" ], "resources": [ { "type": "hwaddr", "address": 48 }, { "type": "phwaddr", "address": 48 } ], "detail": { "function": 0, "command": 1031, "header_type": 0, "secondary_bus": 0, "prog_if": 0 }, "driver": "r8169", "driver_module": "r8169", "drivers": [ "r8169" ], "driver_modules": [ "r8169" ], "module_alias": "pci:v000010ECd00008125sv000010ECsd00000123bc02sc00i00" }, { "index": 17, "attached_to": 18, "class_list": [ "network_controller", "pci" ], "bus_type": { "hex": "0004", "name": "PCI", "value": 4 }, "slot": { "bus": 1, "number": 0 }, "base_class": { "hex": "0002", "name": "Network controller", "value": 2 }, "sub_class": { "hex": "0000", "name": "Ethernet controller", "value": 0 }, "vendor": { "hex": "10ec", "value": 4332 }, "sub_vendor": { "hex": "10ec", "value": 4332 }, "device": { "hex": "8125", "value": 33061 }, "sub_device": { "hex": "0123", "value": 291 }, "revision": { "hex": "0005", "value": 5 }, "model": "Ethernet controller", "sysfs_id": "/devices/pci0000:00/0000:00:00.0/0000:01:00.0", "sysfs_bus_id": "0000:01:00.0", "sysfs_iommu_group_id": 2, "unix_device_names": [ "enp1s0" ], "resources": [ { "type": "hwaddr", "address": 48 }, { "type": "phwaddr", "address": 48 } ], "detail": { "function": 0, "command": 1031, "header_type": 0, "secondary_bus": 0, "prog_if": 0 }, "driver": "r8169", "driver_module": "r8169", "drivers": [ "r8169" ], "driver_modules": [ "r8169" ], "module_alias": "pci:v000010ECd00008125sv000010ECsd00000123bc02sc00i00" } ], "network_interface": [ { "index": 40, "attached_to": 17, "class_list": [ "network_interface" ], "base_class": { "hex": "0107", "name": "Network Interface", "value": 263 }, "sub_class": { "hex": "0001", "name": "Ethernet", "value": 1 }, "model": "Ethernet network interface", "sysfs_id": "/class/net/enp1s0", "sysfs_device_link": "/devices/pci0000:00/0000:00:00.0/0000:01:00.0", "unix_device_names": [ "enp1s0" ], "resources": [ { "type": "hwaddr", "address": 48 }, { "type": "phwaddr", "address": 48 } ], "driver": "r8169", "driver_module": "r8169", "drivers": [ "r8169" ], "driver_modules": [ "r8169" ] }, { "index": 41, "attached_to": 0, "class_list": [ "network_interface" ], "base_class": { "hex": "0107", "name": "Network Interface", "value": 263 }, "sub_class": { "hex": "0000", "name": "Loopback", "value": 0 }, "model": "Loopback network interface", "sysfs_id": "/class/net/lo", "unix_device_names": [ "lo" ] }, { "index": 42, "attached_to": 15, "class_list": [ "network_interface" ], "base_class": { "hex": "0107", "name": "Network Interface", "value": 263 }, "sub_class": { "hex": "0001", "name": "Ethernet", "value": 1 }, "model": "Ethernet network interface", "sysfs_id": "/class/net/enp49s0", "sysfs_device_link": "/devices/pci0000:30/0000:30:00.0/0000:31:00.0", "unix_device_names": [ "enp49s0" ], "resources": [ { "type": "hwaddr", "address": 48 }, { "type": "phwaddr", "address": 48 } ], "driver": "r8169", "driver_module": "r8169", "drivers": [ "r8169" ], "driver_modules": [ "r8169" ] } ], "storage_controller": [ { "index": 19, "attached_to": 20, "class_list": [ "storage_controller", "pci" ], "bus_type": { "hex": "0004", "name": "PCI", "value": 4 }, "slot": { "bus": 145, "number": 0 }, "base_class": { "hex": "0001", "name": "Mass storage controller", "value": 1 }, "sub_class": { "hex": "0008", "value": 8 }, "pci_interface": { "hex": "0002", "value": 2 }, "vendor": { "hex": "144d", "value": 5197 }, "sub_vendor": { "hex": "144d", "value": 5197 }, "device": { "hex": "a808", "value": 43016 }, "sub_device": { "hex": "a801", "value": 43009 }, "model": "Mass storage controller", "sysfs_id": "/devices/pci0000:90/0000:90:00.0/0000:91:00.0", "sysfs_bus_id": "0000:91:00.0", "sysfs_iommu_group_id": 0, "detail": { "function": 0, "command": 1030, "header_type": 0, "secondary_bus": 0, "prog_if": 2 }, "driver": "nvme", "driver_module": "nvme", "drivers": [ "nvme" ], "driver_modules": [ "nvme" ], "module_alias": "pci:v0000144Dd0000A808sv0000144Dsd0000A801bc01sc08i02" } ], "system": {} }, "smbios": {} } ================================================ FILE: machines/uconsole/configuration.nix ================================================ { nixos-hardware, lib, pkgs, config, ... }: { imports = [ # nixos-hardware.nixosModules.clockworkpi-uconsole-cm4 ./disko-config.nix ]; # uConsole uses a Raspberry Pi CM4 (aarch64) nixpkgs.hostPlatform = "aarch64-linux"; networking.hostName = "uconsole"; # Disable grub - we use extlinux from the rpi4 module boot.loader.grub.enable = lib.mkForce false; # Additional kernel params (display params are in nixos-hardware) boot.kernelParams = [ "console=tty1" "loglevel=4" ]; # Additional modules available in initrd (not forced to load) # Display modules are now in nixos-hardware boot.initrd.availableKernelModules = [ # USB ethernet adapters (for network debugging) "usbnet" "ax88179_178a" "cdc_ether" "r8152" # LUKS/dm-crypt support "dm_crypt" "dm_mod" # Crypto modules for xchacha20,aes-adiantum cipher (fast on ARM without AES-NI) "xchacha20" "adiantum" "nhpoly1305" "chacha_generic" "chacha_neon" "aes_generic" "aes_arm64" "sha256_generic" "sha256_arm64" "algif_skcipher" ]; # 32-bit graphics not supported on aarch64 hardware.graphics.enable32Bit = lib.mkForce false; # Console font sized for the 5" 720x1280 display console = { font = "ter-v24n"; packages = with pkgs; [ terminus_font ]; }; # Filesystems are now managed by disko (see disko-config.nix) # Enable NetworkManager for easy WiFi setup networking.networkmanager.enable = true; # Enable SSH for remote access services.openssh.enable = true; services.openssh.settings.PermitRootLogin = "yes"; # Set initial root password for debugging (change after first login!) users.users.root.initialPassword = "nixos"; } ================================================ FILE: machines/uconsole/disko-config.nix ================================================ { pkgs, config, lib, ... }: let # Raspberry Pi firmware files needed for boot rpiFirmware = pkgs.raspberrypifw; # Get the kernel and initrd from the NixOS system being built toplevel = config.system.build.toplevel; # Get the kernel package for DTB overlays kernelPackage = config.boot.kernelPackages.kernel; # config.txt for Raspberry Pi CM4 / uConsole # IMPORTANT: Kernel must be at root level, not in subdirectory - GPU firmware can't load from subdirs configTxt = pkgs.writeText "config.txt" '' [all] arm_64bit=1 enable_uart=1 uart_2ndstage=1 avoid_warnings=1 disable_overscan=1 disable_splash=0 # GPU memory allocation (256MB for GPU) gpu_mem=256 # Ignore LCD detect - we use DSI display ignore_lcd=1 # Audio settings disable_audio_dither=1 pwm_sample_bits=20 # Device tree debug (use vclog -m to view) dtdebug=1 # GPIO configuration for uConsole hardware gpio=10=ip,np gpio=11=op,dh # Enable VC4 graphics driver - MUST use pi4 variant for CM4 # cma-384 allocates 384MB contiguous memory for GPU (needed for full resolution) dtoverlay=vc4-kms-v3d-pi4,cma-384 # USB controller - host mode dtoverlay=dwc2,dr_mode=host # uConsole display and hardware overlay dtoverlay=clockworkpi-uconsole # Audio routing to GPIO 12/13 (headphone jack) dtoverlay=audremap,pins_12_13 # External WiFi antenna dtparam=ant2=on dtparam=audio=on # Kernel and initrd at root level (GPU firmware can't load from subdirectories) kernel=kernel.img initramfs initrd.img followkernel # Device tree for CM4 device_tree=bcm2711-rpi-cm4.dtb [cm4] # CM4-specific settings otg_mode=0 over_voltage=6 arm_freq=2000 gpu_freq=750 force_turbo=1 dtparam=spi=on ''; # cmdline.txt for kernel parameters (LUKS version) cmdlineTxt = pkgs.writeText "cmdline.txt" '' console=tty1 root=/dev/mapper/crypted rootfstype=btrfs rootflags=subvol=/root init=${toplevel}/init loglevel=4 fbcon=rotate:1,nodefer video=DSI-1:panel_orientation=right_side_up ''; in { # Use a generic kernel for the disko VM (the RPi CM4 kernel doesn't work in QEMU) disko.imageBuilder.kernelPackages = pkgs.linuxPackages; # Increase VM memory for LUKS + nix store copy (default 1024 MiB is too low) disko.memSize = 4096; # Populate firmware partition after VM creates the base image # Note: Images are in $out after VM completes # We use mtools to write to the FAT partition without needing loop devices (sandbox-safe) disko.imageBuilder.extraPostVM = '' ${pkgs.coreutils}/bin/echo "=== Populating firmware partition ===" ${pkgs.coreutils}/bin/echo "Output directory: $out" ${pkgs.coreutils}/bin/ls -la "$out" # The firmware partition starts at 1MB (sector 2048) in GPT # mtools can access FAT filesystems with an offset export MTOOLS_SKIP_CHECK=1 # Create mtools config to access the FAT partition at offset # Partition 1 starts at sector 2048 (offset 1048576 bytes) ${pkgs.coreutils}/bin/cat > /tmp/mtoolsrc << 'MTOOLSRC' drive c: file="$out/main.raw" partition=1 MTOOLSRC export MTOOLSRC=/tmp/mtoolsrc # Actually, mtools partition support is tricky. Let's use a simpler approach: # Extract the partition offset using sfdisk and create a direct access config # Get partition info PART_INFO=$(${pkgs.util-linux}/bin/sfdisk -J "$out/main.raw") ${pkgs.coreutils}/bin/echo "Partition info: $PART_INFO" # First partition starts at sector 2048 (1MB) typically for GPT with 512M firmware # Offset = 2048 * 512 = 1048576 bytes OFFSET=1048576 # Create mtools drive config with explicit offset ${pkgs.coreutils}/bin/cat > /tmp/mtoolsrc << EOF drive c: file="$out/main.raw" offset=$OFFSET EOF export MTOOLSRC=/tmp/mtoolsrc ${pkgs.coreutils}/bin/echo "=== Copying firmware files ===" # Create directories first ${pkgs.mtools}/bin/mmd -i "$out/main.raw"@@$OFFSET ::overlays || true # Copy Raspberry Pi firmware ${pkgs.mtools}/bin/mcopy -i "$out/main.raw"@@$OFFSET ${rpiFirmware}/share/raspberrypi/boot/start4.elf :: ${pkgs.mtools}/bin/mcopy -i "$out/main.raw"@@$OFFSET ${rpiFirmware}/share/raspberrypi/boot/fixup4.dat :: ${pkgs.mtools}/bin/mcopy -i "$out/main.raw"@@$OFFSET ${rpiFirmware}/share/raspberrypi/boot/bootcode.bin :: || true # Copy kernel and initrd to ROOT level (GPU firmware can't load from subdirectories!) ${pkgs.mtools}/bin/mcopy -i "$out/main.raw"@@$OFFSET ${toplevel}/kernel ::kernel.img ${pkgs.mtools}/bin/mcopy -i "$out/main.raw"@@$OFFSET ${toplevel}/initrd ::initrd.img # Copy device tree for CM4 (from toplevel/dtbs/broadcom/) ${pkgs.mtools}/bin/mcopy -i "$out/main.raw"@@$OFFSET ${toplevel}/dtbs/broadcom/bcm2711-rpi-cm4.dtb :: # Copy device tree overlays from kernel package for overlay in ${kernelPackage}/dtbs/overlays/*.dtbo; do if [ -f "$overlay" ]; then ${pkgs.mtools}/bin/mcopy -i "$out/main.raw"@@$OFFSET "$overlay" ::overlays/ || true fi done # Copy config files ${pkgs.mtools}/bin/mcopy -i "$out/main.raw"@@$OFFSET ${configTxt} ::config.txt ${pkgs.mtools}/bin/mcopy -i "$out/main.raw"@@$OFFSET ${cmdlineTxt} ::cmdline.txt # List what we copied ${pkgs.coreutils}/bin/echo "=== Firmware partition contents ===" ${pkgs.mtools}/bin/mdir -i "$out/main.raw"@@$OFFSET :: ${pkgs.coreutils}/bin/echo "=== Firmware population complete ===" ''; # Auto-resize root partition on first boot to fill the SD card boot.growPartition = true; boot.supportedFilesystems.btrfs = true; services.btrfs.autoScrub = { enable = true; interval = "weekly"; }; disko.devices = { disk = { main = { type = "disk"; # Image size for generation (actual SD card will be larger) imageSize = "16G"; content = { type = "gpt"; partitions = { # FAT32 firmware partition for Raspberry Pi bootloader # Pi bootloader reads kernel, initrd, device tree from here # Firmware files are populated by extraPostVM after VM creates the image firmware = { size = "512M"; type = "EF00"; # EFI System Partition type for GPT content = { type = "filesystem"; format = "vfat"; mountpoint = "/boot/firmware"; mountOptions = [ "fmask=0022" "dmask=0022" ]; }; }; # LUKS-encrypted partition containing BTRFS luks = { size = "100%"; content = { type = "luks"; name = "crypted"; # Password file for image building (change after first boot!) passwordFile = toString (pkgs.writeText "luks-password" "changeme"); # Use xchacha20,aes-adiantum because BCM2711 lacks AES hardware acceleration # This provides good performance on ARM without AES-NI extraFormatArgs = [ "--type" "luks2" "--cipher" "xchacha20,aes-adiantum-plain64" "--key-size" "256" ]; settings = { allowDiscards = true; }; content = { type = "btrfs"; extraArgs = [ "-f" ]; subvolumes = { "/root" = { mountpoint = "/"; mountOptions = [ "compress=zstd" "noatime" ]; }; "/home" = { mountpoint = "/home"; mountOptions = [ "compress=zstd" "noatime" ]; }; "/nix" = { mountpoint = "/nix"; mountOptions = [ "compress=zstd" "noatime" ]; }; "/swap" = { mountpoint = "/.swapvol"; swap.swapfile.size = "1G"; }; }; }; }; }; }; }; }; }; }; } ================================================ FILE: modules/bluetooth/default.nix ================================================ { lib, pkgs, config, ... }: with lib; let cfg = config.pinpox.defaults.bluetooth; in { options.pinpox.defaults.bluetooth = { enable = mkEnableOption "default bluetooth configuration"; }; config = mkIf cfg.enable { hardware.bluetooth = { enable = true; settings = { General = { Enable = "Source,Sink,Media,Socket"; }; }; }; services.blueman.enable = true; }; } ================================================ FILE: modules/caddy-security/default.nix ================================================ { pkgs, config, lib, pinpox-utils, ... }: with lib; let cfg = config.pinpox.services.caddy-security; in { options.pinpox.services.caddy-security = { enable = mkEnableOption "Caddy security portal config"; domain = mkOption { type = types.str; description = "Domain protetected by this caddy instance"; example = "0cx.de"; }; host = mkOption { type = types.str; default = "auth.${cfg.domain}"; defaultText = literalExpression ''"auth.''${cfg.domain}"''; description = "Host serving caddy-security portal"; example = "auth.0cx.de"; }; authURL = mkOption { type = types.str; default = "https://${cfg.host}/oauth2/generic"; defaultText = literalExpression ''"https://''${cfg.host}/oauth2/generic"''; description = "Authentication URL"; example = "https://auth.mydomain.tld/oauth2/generic"; }; openID = { name = mkOption { type = types.str; default = "Dex"; description = "Name of the OpenID provider, shown in the UI"; example = "GitHub"; }; domain = mkOption { type = types.str; default = cfg.domain; defaultText = literalExpression "cfg.domain"; description = "Domain of the OpenID provider"; example = "mydomain.tld"; }; host = mkOption { type = types.str; default = "login.${cfg.openID.domain}"; defaultText = literalExpression ''"login.''${cfg.openID.domain}"''; description = "Host of the OpenID provider"; example = "login.mydomain.tld"; }; metadataUrl = mkOption { type = types.str; default = "https://${cfg.openID.host}/.well-known/openid-configuration"; defaultText = literalExpression ''"https://''${cfg.openID.host}/.well-known/openid-configuration"''; description = "Metadata URL of the OpenID Host"; example = "https://myhost.tld/.well-known/openid-configuration"; }; }; }; config = mkIf cfg.enable { clan.core.vars.generators."caddy" = pinpox-utils.mkEnvGenerator [ "JWT_SHARED_KEY" "GENERIC_CLIENT_ID" "GENERIC_CLIENT_SECRET" ]; systemd.services.caddy.serviceConfig = { AmbientCapabilities = [ "CAP_NET_BIND_SERVICE" ]; EnvironmentFile = [ config.clan.core.vars.generators."caddy".files."envfile".path ]; }; services.caddy = { enable = true; # package = caddy-patched.packages.x86_64-linux.caddy; # Custom package no longer necessary. See https://github.com/NixOS/nixpkgs/pull/358586 globalConfig = '' order authenticate before respond order authorize before basicauth security { oauth identity provider generic { icon "${cfg.openID.name}" "las la-key la-2x" "white" "black" priority 100 delay_start 5 retry_attempts 5 retry_interval 10 realm generic driver generic client_id {env.GENERIC_CLIENT_ID} client_secret {env.GENERIC_CLIENT_SECRET} scopes openid email profile groups federated:id base_auth_url https://${cfg.openID.host} metadata_url ${cfg.openID.metadataUrl} } authentication portal myportal { crypto default token lifetime 3600 crypto key sign-verify {env.JWT_SHARED_KEY} cookie domain ${cfg.domain} enable identity provider generic ui { links { "My Identity" "/whoami" icon "las la-user" } } transform user { match realm generic action add role authp/user ui link "Test site 1 (user)" https://static-site-one.${cfg.domain}/ icon "las la-star" } transform user { match realm generic match sub CgZwaW5wb3gSBmdpdGh1Yg # github ID of pinpox action add role authp/admin ui link "Test site 2 (admin)" https://static-site-two.${cfg.domain}/ icon "las la-star" } } authorization policy pol-user { set auth url ${cfg.authURL} crypto key verify {env.JWT_SHARED_KEY} allow roles authp/admin authp/user validate bearer header inject headers with claims } authorization policy pol-admin { set auth url ${cfg.authURL} crypto key verify {env.JWT_SHARED_KEY} allow roles authp/admin validate bearer header inject headers with claims } } ''; virtualHosts = let mkStaticTestSite = num: pkgs.writeTextFile { name = "index.html"; text = ''

Hello World (${num})!

This is the site number ${num}

''; executable = false; destination = "/html/index.html"; }; in { "${cfg.host}".extraConfig = "authenticate with myportal"; "static-site-one.${cfg.domain}" = { extraConfig = '' authorize with pol-user encode gzip root * ${mkStaticTestSite "one"}/html file_server ''; }; "static-site-two.${cfg.domain}" = { extraConfig = '' authorize with pol-admin encode gzip root * ${mkStaticTestSite "two"}/html file_server ''; }; }; }; }; } ================================================ FILE: modules/calibre-web/default.nix ================================================ { config, lib, ... }: with lib; let cfg = config.pinpox.services.calibre-web; in { options.pinpox.services.calibre-web = { enable = mkEnableOption "calibre-web config"; host = mkOption { type = types.str; default = "books.0cx.de"; description = "Host serving calibre"; example = "books.0cx.de"; }; }; config = mkIf cfg.enable { services.calibre-web = { enable = true; # listen.port = 8083 listen.ip = "127.0.0.1"; options.enableBookUploading = true; # options.reverseProxyAuth.header # options.reverseProxyAuth.enable # options.enableKepubify # options.enableBookConversion # options.calibreLibrary }; # Reverse proxy services.caddy.virtualHosts."${cfg.host}".extraConfig = with config.services.calibre-web.listen; "reverse_proxy ${ip}:${builtins.toString port}"; # Backups pinpox.services.restic-client.backup-paths-offsite = [ config.services.calibre-web.dataDir ]; }; } ================================================ FILE: modules/ci/default.nix ================================================ { config, pkgs, lib, flake-self, nixpkgs, ... }: with lib; { options.pinpox.defaults = { CISkip = mkOption { type = types.bool; default = false; example = true; description = "Wheter this host should be skipped by the CI pipeline"; }; }; } ================================================ FILE: modules/clan-common/default.nix ================================================ { config, clan-core, self, pkgs, lib, ... }: { # Make pinpox-utils available to all modules # _module.args.pinpox-utils = import ../../utils { inherit pkgs lib; }; nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; # Default to depolying to the hostname clan.core.networking.targetHost = lib.mkOverride 999 "${config.networking.hostName}.pin"; # passCommand must remain at machine-level (not flake-level) clan.core.vars.password-store.passCommand = "passage"; environment.systemPackages = [ pkgs.passage ]; clan.core.vars.generators."mkpasswd-generator" = { files.test-password = { }; runtimeInputs = with pkgs; [ coreutils xkcdpass ]; script = '' mkdir -p $out echo ${(self.clan.exports."clan-core/internet:internet::".networking.module)} xkcdpass > $out/test-password ''; }; environment.etc."test-password".source = config.clan.core.vars.generators."mkpasswd-generator".files."test-password".path; nix.settings.trusted-substituters = [ "https://cache.clan.lol" "https://nix-community.cachix.org" ]; nix.settings.trusted-public-keys = [ "nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs=" "cache.clan.lol-1:3KztgSAB5R1M+Dz7vzkBGzXdodizbgLXGXKXlcQLA28=" ]; } ================================================ FILE: modules/environment/default.nix ================================================ { config, pkgs, lib, ... }: with lib; let cfg = config.pinpox.defaults.environment; in { options.pinpox.defaults.environment = { enable = mkEnableOption "Environment defaults"; }; config = mkIf cfg.enable { # System-wide environment variables to be set environment = { variables = { EDITOR = "nvim"; GOPATH = "/home/pinpox/.go"; VISUAL = "nvim"; # Use librsvg's gdk-pixbuf loader cache file as it enables gdk-pixbuf to load # SVG files (important for icons) # GDK_PIXBUF_MODULE_FILE = # "$(echo ${pkgs.librsvg.out}/lib/gdk-pixbuf-2.0/*/loaders.cache)"; }; }; }; } ================================================ FILE: modules/fonts/default.nix ================================================ { config, pkgs, lib, ... }: with lib; let cfg = config.pinpox.defaults.fonts; in { options.pinpox.defaults.fonts = { enable = mkEnableOption "Fonts defaults"; }; config = mkIf cfg.enable { fonts = { fontDir.enable = true; packages = with pkgs; [ # stix-two # league-of-moveable-type # inter # source-sans-pro # source-serif-pro noto-fonts-monochrome-emoji # corefonts # recursive # iosevka-bin font-awesome line-awesome ]; fontconfig = { defaultFonts = { serif = [ "Berkeley Mono" "Inconsolata Nerd Font Mono" ]; sansSerif = [ "Berkeley Mono" "Inconsolata Nerd Font Mono" ]; monospace = [ "Berkeley Mono" "Inconsolata Nerd Font Mono" ]; emoji = [ "Noto Emoji" ]; }; }; }; }; } ================================================ FILE: modules/forgejo/default.nix ================================================ { config, lib, pkgs, ... }: with lib; let cfg = config.pinpox.services.forgejo; oidcSecretPath = config.clan.core.vars.generators."forgejo-oidc".files.client_secret.path; in { options.pinpox.services.forgejo = { enable = mkEnableOption "forgejo config"; host = mkOption { type = types.str; default = "git.pinpox.com"; description = "Host serving forgejo"; example = "git.pinpox.com"; }; autheliaHost = mkOption { type = types.str; default = "auth.pablo.tools"; description = "Authelia host for OIDC discovery"; }; }; config = mkIf cfg.enable { # Reverse proxy services.caddy.virtualHosts."${cfg.host}".extraConfig = with config.services.forgejo.settings.server; "reverse_proxy ${HTTP_ADDR}:${toString HTTP_PORT}"; # Backups pinpox.services.restic-client.backup-paths-offsite = [ config.services.forgejo.dump.backupDir # config.services.forgejo.stateDir config.services.forgejo.lfs.contentDir ]; # Shared OIDC secret (generated by the authelia host, shared via clan vars) clan.core.vars.generators."forgejo-oidc" = { share = true; files.client_secret = { }; files.client_secret_hash = { }; runtimeInputs = with pkgs; [ coreutils openssl authelia gnused ]; script = '' mkdir -p $out openssl rand -hex 32 > $out/client_secret authelia crypto hash generate argon2 --password "$(cat $out/client_secret)" \ | sed 's/^Digest: //' > $out/client_secret_hash ''; }; # Register Authelia as OAuth2 source after forgejo starts systemd.services.forgejo.postStart = let exe = lib.getExe config.services.forgejo.package; providerName = "authelia"; customConf = "--work-path ${config.services.forgejo.stateDir} --config ${config.services.forgejo.customDir}/conf/app.ini"; in '' # Only add if not already registered if ! ${exe} admin auth list ${customConf} 2>/dev/null | grep -q "${providerName}"; then ${exe} admin auth add-oauth ${customConf} \ --name "${providerName}" \ --provider openidConnect \ --key "forgejo" \ --secret "$(cat ${oidcSecretPath})" \ --auto-discover-url "https://${cfg.autheliaHost}/.well-known/openid-configuration" \ --scopes "openid email profile groups" fi ''; services.forgejo = { enable = true; database.type = "sqlite3"; dump.enable = true; lfs.enable = true; settings = { server = { HTTP_PORT = 3333; HTTP_ADDR = "127.0.0.1"; DOMAIN = cfg.host; PROTOCOL = "https"; }; openid = { ENABLE_OPENID_SIGNIN = false; ENABLE_OPENID_SIGNUP = true; WHITELISTED_URIS = cfg.autheliaHost; }; oauth2_client = { ENABLE_AUTO_REGISTRATION = true; }; service = { DISABLE_REGISTRATION = false; ALLOW_ONLY_EXTERNAL_REGISTRATION = true; SHOW_REGISTRATION_BUTTON = false; ENABLE_INTERNAL_SIGNIN = false; ENABLE_BASIC_AUTHENTICATION = false; REQUIRE_SIGNIN_VIEW = true; }; mailer = { ENABLED = true; FROM = "git@0cx.de"; PROTOCOL = "smtp"; IS_TLS_ENABLED = false; USER = "mail@0cx.de"; SMTP_ADDR = "r19.hallo.cloud:587"; }; other.SHOW_FOOTER_VERSION = false; session.COOKIE_SECURE = true; }; }; }; } ================================================ FILE: modules/gitea/default.nix ================================================ { config, lib, ... }: with lib; let cfg = config.pinpox.services.gitea; in { options.pinpox.services.gitea = { enable = mkEnableOption "gitea config"; host = mkOption { type = types.str; default = "git.0cx.de"; description = "Host serving gitea"; example = "git.0cx.de"; }; }; config = mkIf cfg.enable { # Reverse proxy services.caddy.virtualHosts."${cfg.host}".extraConfig = with config.services.gitea.settings.server; "reverse_proxy ${HTTP_ADDR}:${builtins.toString HTTP_PORT}"; # Backups pinpox.services.restic-client.backup-paths-offsite = [ "/var/lib/gitea" ]; clan.core.vars.generators."gitea" = { files.mailer-pw.owner = "gitea"; prompts.mailer-pw.persist = true; }; services.gitea = { enable = true; mailerPasswordFile = "${config.clan.core.vars.generators."gitea".files."mailer-pw".path}"; settings = { server = { ROOT_URL = "https://${cfg.host}"; HTTP_PORT = 3333; HTTP_ADDR = "127.0.0.1"; }; service = { DISABLE_REGISTRATION = true; REQUIRE_SIGNIN_VIEW = true; DOMAIN = cfg.host; }; mailer = { ENABLED = true; FROM = "git@0cx.de"; PROTOCOL = "smtp"; IS_TLS_ENABLED = false; USER = "mail@0cx.de"; SMTP_ADDR = "r19.hallo.cloud:587"; }; markdown.ENABLE_MATH = true; }; }; }; } ================================================ FILE: modules/hedgedoc/default.nix ================================================ { config, lib, pinpox-utils, ... }: with lib; let cfg = config.pinpox.services.hedgedoc; in # pinpox-utils = import ../../utils { inherit pkgs; }; { options.pinpox.services.hedgedoc = { enable = mkEnableOption "Hedgedoc server"; }; config = mkIf cfg.enable { clan.core.vars.generators."hedgedoc" = pinpox-utils.mkEnvGenerator [ "CMD_SESSION_SECRET" "CMD_OAUTH2_CLIENT_ID" "CMD_OAUTH2_CLIENT_SECRET" ]; systemd.services.hedgedoc.serviceConfig.Environment = [ # Allow creating on-the-fly by url "CMD_ALLOW_FREEURL=true" # Default permission of notes "CMD_DEFAULT_PERMISSION=limited" # Forbid anonymous usage "CMD_ALLOW_ANONYMOUS=false" # oauth2 with dex "CMD_OAUTH2_BASEURL=https://${config.pinpox.services.dex.host}" "CMD_OAUTH2_AUTHORIZATION_URL=https://${config.pinpox.services.dex.host}/auth" "CMD_OAUTH2_TOKEN_URL=https://${config.pinpox.services.dex.host}/token" "CMD_OAUTH2_USER_PROFILE_URL='https://${config.pinpox.services.dex.host}/userinfo'" "CMD_OAUTH2_PROVIDERNAME=dex" "CMD_OAUTH2_SCOPE='openid email profile'" "CMD_OAUTH2_USER_PROFILE_USERNAME_ATTR='preferred_username'" "CMD_OAUTH2_USER_PROFILE_DISPLAY_NAME_ATTR='name'" "CMD_OAUTH2_USER_PROFILE_EMAIL_ATTR='email'" ]; # Create system user and group services.hedgedoc = { enable = true; environmentFile = "${config.clan.core.vars.generators."hedgedoc".files."envfile".path}"; settings = { protocolUseSSL = true; # Use https when loading assets allowEmailRegister = false; # Disable email registration email = false; # Disable email login domain = "pads.0cx.de"; host = "127.0.0.1"; # port = 3000; # Default debug = true; db = { dialect = "sqlite"; storage = "/var/lib/hedgedoc/db.sqlite"; }; useCDN = true; }; }; # Backup SQLite databse pinpox.services.restic-client.backup-paths-offsite = [ config.services.hedgedoc.settings.db.storage ]; # systemd.services.hedgedoc-git-sync = { # serviceConfig = { # Type = "oneshot"; # Environment = [ # "GIT_SSH_COMMAND='ssh -i private_key_file'" # ]; # }; # path = with pkgs; [ bash ]; # script = '' # echo "RUNNING IN " # pwd # ''; # }; # systemd.timers.hedgedoc-git-sync = { # wantedBy = [ "timers.target" ]; # partOf = [ "hedgedoc-git-sync.service" ]; # timerConfig = { # OnCalendar = "*:0/1"; # Unit = "hedgedoc-git-sync.service"; # }; # }; }; } ================================================ FILE: modules/hello/default.nix ================================================ { lib, pkgs, config, ... }: with lib; let cfg = config.pinpox.services.hello; in { options.pinpox.services.hello = { enable = mkEnableOption "hello service"; greeter = mkOption { type = types.str; default = "world"; example = "universe"; description = "A very friendly service that greets you"; }; }; config = mkIf cfg.enable { environment.systemPackages = [ pkgs.hello ]; systemd.services.hello = { wantedBy = [ "multi-user.target" ]; serviceConfig.ExecStart = "${pkgs.hello}/bin/hello -g'Hello, ${escapeShellArg cfg.greeter}!'"; }; }; } ================================================ FILE: modules/hello/test.nix ================================================ { pkgs, system, self, ... }: with import (pkgs + "/nixos/lib/testing-python.nix") { inherit system; }; (makeTest { nodes = { client = { ... }: { imports = [ self.nixosModules.hello ]; pinpox.services.hello.enable = true; }; }; testScript = '' start_all() client.wait_for_unit("multi-user.target") print(client.succeed("uname")) print(client.succeed("hello")) ''; }).test ================================================ FILE: modules/home-assistant/default.nix ================================================ { config, lib, pkgs, ... }: with lib; let cfg = config.pinpox.services.home-assistant; in { options.pinpox.services.home-assistant = { enable = mkEnableOption "Home-assitant server"; }; config = mkIf cfg.enable { networking.firewall.trustedInterfaces = [ "wg-clan" ]; clan.core.vars.generators."home-assistant" = { prompts."secrets.yaml".persist = true; prompts."secrets.yaml".type = "multiline"; }; systemd.services.home-manager.serviceConfig.BindReadOnlyPaths = [ "${ config.clan.core.vars.generators."home-assistant".files."secrets.yaml".path }:/var/lib/hass/secrets.yaml" ]; # https://nixos.wiki/wiki/Home_Assistant#Combine_declarative_and_UI_defined_automations systemd.tmpfiles.rules = [ "f ${config.services.home-assistant.configDir}/automations.yaml 0755 hass hass" ]; # Backup configuration dir - Config done via UI stateful pinpox.services.restic-client.backup-paths-offsite = [ config.services.home-assistant.configDir ]; # Needed for some integrations users.users.hass.extraGroups = [ "dialout" "keys" ]; # Open port for mqtt networking.firewall = { allowedTCPPorts = [ 1883 ]; # For home-assistant COIT interfaces.eno1.allowedUDPPorts = [ 5683 ]; # Expose home-assitant over wireguard interfaces.wg-clan.allowedTCPPorts = [ 8123 9273 # Telegraf ]; }; # Enable mosquitto MQTT broker services.mosquitto = { enable = true; # Mosquitto is only listening on the local IP, traffic from outside is not # allowed. listeners = [ { address = "192.168.101.221"; port = 1883; users = { # No real authentication needed here, since the local network is # trusted. mosquitto = { acl = [ "readwrite #" ]; password = "mosquitto"; }; }; } ]; }; # The prometheus integration of home-assistant is incomplete (e.g. missing # GPS lon/lat data), but the influx integration is fine. To have the # home-assistant states in prometheus to be able to create alerts and # graphs, telegraf is used to listen for influx formatted data form # home-assistant and export it as prometheus metrics. services.telegraf = { enable = true; extraConfig = { agent = { interval = "60s"; ## Log at debug level. debug = true; ## Log only error level messages. quiet = false; }; inputs = { influxdb_v2_listener = { # Start influxdb V2 listener on localhost only as we are running on # the same host as home-assistant. service_address = ":8086"; }; }; outputs = { prometheus_client = let wg-clan-ip = builtins.elemAt (builtins.match "(.*)/.*" (builtins.elemAt config.networking.wireguard.interfaces.wg-clan.ips 0)) 0; in { # Listen on the wireguard VPN IP. Localhost is not enough here, as # prometheus is hosted on a different machine. listen = "${wg-clan-ip}:9273"; metric_version = 2; }; }; }; }; # Enable home-assistant service services.home-assistant = { enable = true; customComponents = with pkgs.home-assistant-custom-components; [ ntfy awtrix moonraker ]; # List extraComponents here to be installed. The names can be found here: # https://github.com/NixOS/nixpkgs/blob/master/pkgs/servers/home-assistant/component-packages.nix # Components listed here will be possible to add via the webUI if not # automatically picked up. extraComponents = [ # "piper" # "whisper" "bthome" "nextcloud" "unifi_direct" "unifi" "esphome" # "map" "openweathermap" "tasmota" "icloud" ]; # Disable the python checks, they take for ever when building the # configuration # package = (home-assistant-package.overrideAttrs (old: { # doInstallCheck = false; # doCheck = false; # })); # Configuration generated to /var/lib/hass/configuration.yaml config = { /* # M5Stack Atom Echo # Example configuration.yaml homeassistant.media_dirs = { # media ="/var/lib/hass/media"; media = "/var/lib/hass/tts"; recording = "/var/lib/hass/recordings"; }; */ sensor = [ { name = "random_joke"; platform = "rest"; json_attributes = "joke"; resource = "https://icanhazdadjoke.com/"; scan_interval = "3600"; headers.Accept = "application/json"; } ]; conversation.intents.TellJoke = [ "Witz" ]; intent_script.TellJoke = { speech.text = ''{{ state_attr("sensor.random_joke", "joke") }}''; action = { service = "homeassistant.update_entity"; entity_id = "sensor.random_joke"; }; }; assist_pipeline = { }; notify = [ { name = "ntfy"; platform = "rest"; method = "POST_JSON"; authentication = "basic"; username = "nfty"; password = "!nfty-pass"; data.topic = "homeassistant"; title_param_name = "title"; message_param_name = "message"; resource = "https://push.pablo.tools"; } ]; device_tracker = let # Unifi APs ap-ips = [ "192.168.2.110" "192.168.2.111" "192.168.2.126" ]; in map (host: { inherit host; platform = "unifi_direct"; username = "pinpox"; password = "!unifi-ap-ssh"; }) ap-ips; weather = { }; sun = { }; # icloud = { }; intent_script.FindIphone = { # speech.text = "Notified pinpox"; action = { service = "icloud.play_sound"; data_template.account = "apple@pablo.tools"; data_template.device_name = "Apfeltasche (2)"; }; }; ios = { actions = [ # Toggle RGB strip { name = "Toggle RGB"; background_color = "#24283B"; label = { text = "RGB-Kette"; color = "#E5E9F0"; }; icon = { icon = "lightbulb-on"; color = "#FF5370"; }; } { name = "Toggle Bulbbox"; background_color = "#24283B"; label = { text = "Bulbbox"; color = "#E5E9F0"; }; icon = { icon = "lightbulb-on"; color = "#E5E9F0"; }; } # Toggle Deckenlicht { name = "Toggle Deckenlicht"; background_color = "#24283B"; label = { text = "Deckenlicht"; color = "#E5E9F0"; }; icon = { icon = "lightbulb-on"; color = "#E5E9F0"; }; } ]; }; # https://home.pablo.tools/developer-tools/event # https://home.pablo.tools/config/automation/dashboard "automation ui" = "!include automations.yaml"; "automation manual" = [ { id = "rackmount_button1"; mode = "single"; alias = "Rack Button 1"; description = "Toggle Lightbulb Box"; trigger = [ { type = "turned_off"; platform = "device"; device_id = "f0a65ba3fc5a542ec83a1fc22a36d2e2"; entity_id = "42b59835bb83659d01cc2ab0da4c429e"; domain = "binary_sensor"; } ]; action = [ { type = "toggle"; device_id = "a8c96a8429ae8a7a13c058f79c886684"; entity_id = "switch.lightbulb_box"; domain = "switch"; } ]; } { id = "rackmount_button2"; mode = "single"; alias = "Rack Button 2"; description = "Toggle RGB Strip"; trigger = [ { type = "turned_off"; platform = "device"; device_id = "f0a65ba3fc5a542ec83a1fc22a36d2e2"; entity_id = "binary_sensor.button_2"; domain = "binary_sensor"; } ]; action = [ { type = "toggle"; device_id = "d97c93bff99173ae0b3b20d640050508"; entity_id = "light.rgb_strip_1"; domain = "light"; } ]; } { id = "rackmount_button3"; mode = "single"; alias = "Rack Button 3"; description = "Toggle Ceiling light"; trigger = [ { type = "turned_off"; platform = "device"; device_id = "f0a65ba3fc5a542ec83a1fc22a36d2e2"; entity_id = "binary_sensor.button_3"; domain = "binary_sensor"; } ]; action = [ { type = "toggle"; device_id = "d71e3f9c22a777149793e6b126f27550"; entity_id = "switch.deckenlicht"; domain = "switch"; } ]; } { id = "auto_deckenlicht_toggle"; alias = "Deckenlicht Toggle"; trigger = [ { platform = "event"; event_type = "ios.action_fired"; event_data.actionName = "Toggle Deckenlicht"; } ]; action = [ { type = "toggle"; device_id = "d71e3f9c22a777149793e6b126f27550"; entity_id = "switch.deckenlicht"; domain = "switch"; } ]; } { id = "auto_rgb_toggle"; alias = "RGB-Kette Toggle"; trigger = [ { platform = "event"; event_type = "state_changed"; event_data.entity_id = "switch.lichterkette"; } { platform = "event"; event_type = "ios.action_fired"; event_data.actionName = "Toggle RGB"; } ]; condition = [ { condition = "template"; value_template = "{{ trigger.event.data.old_state.state != 'unavailable' }}"; } { condition = "template"; value_template = "{{ trigger.event.data.new_state.state != 'unavailable' }}"; } ]; action = [ { type = "toggle"; device_id = "d97c93bff99173ae0b3b20d640050508"; entity_id = "light.rgb_strip_1"; domain = "light"; } ]; } ]; # Provides some sane defaults and minimal dependencies default_config = { }; shelly = { }; zeroconf = { # default_interface = true; }; # Basic settings for home-assistant homeassistant = { name = "Villa Kunterbunt"; latitude = "!secret home-latitude"; longitude = "!secret home-longitude"; elevation = 86; unit_system = "metric"; time_zone = "Europe/Berlin"; external_url = "https://home.pablo.tools"; }; http = { use_x_forwarded_for = true; trusted_proxies = [ (builtins.readFile ( config.clan.core.settings.directory + "/vars/per-machine/porree/wireguard-wg-clan-ip/ipv4/value" )) ]; }; frontend = { }; shopping_list = { }; sun = { }; config = { }; mobile_app = { }; cloud = { }; system_health = { }; # Discover some devices automatically # discovery = { }; # Show some system health data system_health = { }; # Enable support for tamota devices # tasmota = { }; # Led strip wifi controller, component needs to be listed explicitely in # extraComponents above # light = [{ # platform = "flux_led"; # automatic_add = true; # devices = { "192.168.2.106" = { name = "flux_led"; }; }; # }]; # Fritzbox network traffic stats # sensor = [{ platform = "fritzbox_netmonitor"; }]; # Metrics for prometheus prometheus = { namespace = "hass"; }; # Enable MQTT mqtt = { }; logger.default = "info"; # logger.default = "debug"; influxdb = { api_version = 2; # host = "vpn.influx.pablo.tools"; host = "localhost"; port = "8086"; max_retries = 10; ssl = false; verify_ssl = false; # Authorization is not used for telegraf, but home-assistant requires # passing these parameters token = "!secret influx-token"; organization = "pinpox"; bucket = "home_assistant"; }; # Track the sun sun = { }; # Enable mobile app mobile_app = { }; # Enable configuration UI # config = { }; # Enable support for tracking state changes over time history = { }; # Purge tracked history after 10 days recorder.purge_keep_days = 10; # View all events in o logbook logbook = { }; }; }; }; } ================================================ FILE: modules/http2irc/default.nix ================================================ { lib, pkgs, config, ... }: with lib; let cfg = config.pinpox.services.monitoring-server.http-irc; http2irc = pkgs.buildGoModule rec { pname = "http2irc"; version = "1.0"; # TODO use flake inputs src = pkgs.fetchFromGitHub { owner = "pinpox"; repo = "http2irc"; rev = "v${version}"; sha256 = "sha256-5aHQ3Y0Md0qrJlFju8Nx6S5Ul+SVZOtFrcx90oiVvWo="; }; vendorHash = "sha256-k45e6RSIl3AQdOFQysIwJP9nlYsSFeaUznVIXfbYwLA="; subPackages = [ "." ]; meta = with lib; { description = "Webhook reciever to annouce in IRC channels"; homepage = "https://github.com/pinpox/http2irc"; license = licenses.gpl3; maintainers = with maintainers; [ pinpox ]; platforms = platforms.linux; }; }; templateFile = pkgs.writeTextFile { name = "template.mustache"; text = concatStrings [ "{{#plain}}{{plain}}{{/plain}}" ]; }; in # port-loki = 3100; { options.pinpox.services.monitoring-server.http-irc = { enable = mkEnableOption "http2irc webhook relay"; }; config = mkIf cfg.enable { # User and group users.users.http2irc = { isSystemUser = true; home = "/var/lib/http2irc"; description = "http2irc system user"; group = "http2irc"; createHome = true; }; users.groups.http2irc = { name = "http2irc"; }; clan.core.vars.generators."http2irc" = pinpox-utils.mkEnvGenerator [ "IRC_SASL_PASS" "IRC_SASL_USER" "IRC_NICK" "IRC_BOT_TOKEN" ]; # Service systemd.services.http2irc = { wantedBy = [ "multi-user.target" ]; after = [ "network.target" ]; description = "Start http2irc"; serviceConfig = { EnvironmentFile = [ config.clan.core.vars.generators.http2irc.files."envfile".path ]; Environment = [ "IRC_TEMPLATE='${templateFile}'" "IRC_CHANNEL='#lounge-rocks'" "IRC_DEBUG='false'" "IRC_LISTEN=localhost:8989" "IRC_NOTICE='true'" "IRC_SERVER='irc.freenode.net:7000'" ]; WorkingDirectory = "/var/lib/http2irc"; User = "http2irc"; ExecStart = "${http2irc}/bin/http2irc"; Restart = "on-failure"; RestartSec = "5s"; }; }; # Reverse proxy }; } ================================================ FILE: modules/immich/default.nix ================================================ { config, lib, ... }: with lib; let cfg = config.pinpox.services.immich; in { options.pinpox.services.immich = { enable = mkEnableOption "immich photo gallery"; host = mkOption { type = types.str; default = "photos.0cx.de"; description = "Host serving immich"; example = "pics.0cx.de"; }; }; config = mkIf cfg.enable { # services.immich-public-proxy.enable # services.immich-public-proxy.immichUrl # services.immich-public-proxy.openFirewall # services.immich-public-proxy.package # services.immich-public-proxy.port # services.immich-public-proxy.settings services.immich = { enable = true; host = "127.0.0.1"; # environment # openFirewall # secretsFile mediaLocation = "/mnt/storagebox/photos"; # settings # Configuration for Immich. See https://immich.app/docs/install/config-file/ or # navigate to https://my.immich.app/admin/system-settings for options and # defaults. Setting it to null allows configuring Immich in the web interface. # You can load secret values from a file in this configuration by setting # somevalue._secret = "/path/to/file" instead of setting somevalue directly. settings = { server.externalDomain = "https://${cfg.host}"; storageTemplate = { enabled = true; hashVerificationEnabled = true; template = "{{y}}/{{y}}-{{MM}}-{{dd}}/{{filename}}"; }; }; # storageTemplate = { # enabled = true; # hashVerificationEnabled = true; # # template = "{{y}}/{{y}}-{{MM}}-{{dd}}/{{filename}}"; # }; # # passwordLogin.enabled = false; # # oauth = { }; # }; # "oauth": { # "autoLaunch": false, # "autoRegister": true, # "buttonText": "Login with OAuth", # "clientId": "", # "clientSecret": "", # "defaultStorageQuota": null, # "enabled": false, # "issuerUrl": "", # "mobileOverrideEnabled": false, # "mobileRedirectUri": "", # "profileSigningAlgorithm": "none", # "roleClaim": "immich_role", # "scope": "openid email profile", # "signingAlgorithm": "RS256", # "storageLabelClaim": "preferred_username", # "storageQuotaClaim": "immich_quota", # "timeout": 30000, # "tokenEndpointAuthMethod": "client_secret_post" # }, # }, }; # Reverse proxy services.caddy = { enable = true; virtualHosts."${cfg.host}".extraConfig = "reverse_proxy 127.0.0.1:${toString config.services.immich.port}"; }; # Mount storagebox pinpox.defaults.storagebox = { enable = true; mountOnAccess = false; }; # Add immich user to storage-users group for access to storagebox users.users.${config.services.immich.user}.extraGroups = [ "storage-users" ]; # Ensure storagebox is mounted before immich starts systemd.services.immich-server = { requires = [ "mnt-storagebox.mount" ]; after = [ "mnt-storagebox.mount" ]; }; }; } ================================================ FILE: modules/jitsi-matrix-presence/default.nix ================================================ { config, lib, pkgs, jitsi-matrix-presence, ... }: with lib; let cfg = config.pinpox.services.jitsi-matrix-presence; pinpox-utils = import ../../utils { inherit pkgs; }; mkPres = JITSI_ROOMS: JITSI_SERVER: ROOM_ID: port: { wantedBy = [ "multi-user.target" ]; environment = { inherit JITSI_ROOMS JITSI_SERVER ROOM_ID; HOMESERVER_URL = "https://matrix.org"; USER_ID = "@alertus-maximus:matrix.org"; LISTEN_ADDRESS = "0.0.0.0:${port}"; }; serviceConfig = { EnvironmentFile = [ config.clan.core.vars.generators."jitsi-presence".files."envfile".path ]; DynamicUser = true; ExecStart = "${jitsi-matrix-presence.packages.x86_64-linux.default}/bin/jitsi-presence"; Restart = "on-failure"; RestartSec = "5s"; }; }; in { options.pinpox.services.jitsi-matrix-presence = { enable = mkEnableOption "Jitsi presence notification service"; }; config = mkIf cfg.enable { networking.firewall.allowedTCPPorts = [ 8226 8227 8228 ]; clan.core.vars.generators."jitsi-presence" = pinpox-utils.mkEnvGenerator [ "ACCESS_TOKEN" ]; systemd.services.jitsi-matrix-presence-krebs = mkPres "krebs,nixos" "https://jitsi.lassul.us" "!bohcSYPVoePqBDWlvE:hackint.org" "8226"; }; } ================================================ FILE: modules/kf-homepage/default.nix ================================================ { lib, config, ... }: with lib; let cfg = config.pinpox.services.kf-homepage; in { options.pinpox.services.kf-homepage.enable = mkEnableOption "Krosse Flagge Homepage"; config = mkIf cfg.enable { services.caddy = { enable = true; virtualHosts = { "0cx.de".extraConfig = '' root * ${./page} encode zstd gzip file_server ''; }; }; }; } ================================================ FILE: modules/kf-homepage/page/index.html ================================================ Krosse Flagge ================================================ FILE: modules/locale/default.nix ================================================ { config, lib, ... }: with lib; let cfg = config.pinpox.defaults.locale; in { options.pinpox.defaults.locale = { enable = mkEnableOption "Locale defaults"; automatic-timezone = mkEnableOption "Automatic timezone based on location (for mobile machines)"; }; config = mkIf cfg.enable (mkMerge [ { # Set localization and tty options i18n.defaultLocale = "en_DK.UTF-8"; i18n.supportedLocales = [ "en_US.UTF-8/UTF-8" "en_DK.UTF-8/UTF-8" ]; console = { keyMap = "colemak"; }; time.timeZone = mkDefault "Europe/Berlin"; } (mkIf cfg.automatic-timezone { time.timeZone = null; services.automatic-timezoned.enable = true; services.geoclue2.geoProviderUrl = "https://api.beacondb.net/v1/geolocate"; }) ]); } ================================================ FILE: modules/lvm-grub/default.nix ================================================ { config, lib, ... }: with lib; let cfg = config.pinpox.defaults.lvm-grub; in { options.pinpox.defaults.lvm-grub = { enable = mkEnableOption "LVM/Grub defaults"; }; config = mkIf cfg.enable { # Use the grub2 boot loader. boot = { loader = { grub.enable = true; # Required for LVM grub.device = "nodev"; # Use UEFI support grub.efiSupport = true; grub.efiInstallAsRemovable = true; # efi.canTouchEfiVariables = true; # useOSProber = true; }; # /tmp is cleaned after each reboot tmp.cleanOnBoot = true; }; }; } ================================================ FILE: modules/miniflux/default.nix ================================================ { config, lib, pkgs, ... }: with lib; let cfg = config.pinpox.services.miniflux; oidcSecretPath = config.clan.core.vars.generators."miniflux-oidc".files.client_secret.path; in { options.pinpox.services.miniflux = { enable = mkEnableOption "miniflux RSS reader"; }; config = mkIf cfg.enable { clan.core.vars.generators."miniflux" = { files.credentials = { }; runtimeInputs = with pkgs; [ coreutils xkcdpass ]; script = '' mkdir -p $out printf "ADMIN_USERNAME=admin\nADMIN_PASSWORD='%s'" "$(xkcdpass -d-)" > $out/credentials ''; }; clan.core.vars.generators."miniflux-oidc" = { share = true; files.client_secret = { }; files.client_secret_hash = { }; runtimeInputs = with pkgs; [ coreutils openssl authelia gnused ]; script = '' mkdir -p $out openssl rand -hex 32 > $out/client_secret authelia crypto hash generate argon2 --password "$(cat $out/client_secret)" \ | sed 's/^Digest: //' > $out/client_secret_hash ''; }; services.caddy = { enable = true; virtualHosts."news.0cx.de".extraConfig = "reverse_proxy ${config.services.miniflux.config.LISTEN_ADDR}"; }; systemd.services.miniflux.serviceConfig.LoadCredential = [ "oauth2_client_secret_file:${oidcSecretPath}" ]; services.miniflux = { enable = true; config = { OAUTH2_USER_CREATION = "1"; DISABLE_LOCAL_AUTH = "1"; CLEANUP_FREQUENCY = "48"; LISTEN_ADDR = "127.0.0.1:8787"; OAUTH2_PROVIDER = "oidc"; OAUTH2_CLIENT_ID = "miniflux"; OAUTH2_CLIENT_SECRET_FILE = "/run/credentials/miniflux.service/oauth2_client_secret_file"; OAUTH2_REDIRECT_URL = "https://news.0cx.de/oauth2/oidc/callback"; OAUTH2_OIDC_DISCOVERY_ENDPOINT = "https://auth.pablo.tools"; OAUTH2_OIDC_PROVIDER_NAME = "pablo.tools"; }; adminCredentialsFile = config.clan.core.vars.generators."miniflux".files."credentials".path; }; }; } ================================================ FILE: modules/minio/default.nix ================================================ { lib, config, pkgs, ... }: with lib; let cfg = config.pinpox.services.minio; in { options.pinpox.services.minio = { enable = mkEnableOption "minio s3 config"; }; config = mkIf cfg.enable { clan.core.vars.generators."minio" = rec { files.root-credentials = { }; validation.script = script; runtimeInputs = with pkgs; [ coreutils xkcdpass ]; script = # sh '' mkdir -p $out printf "MINIO_ROOT_USER=admin\nMINIO_ROOT_PASSWORD='%s'" "$(xkcdpass -d-)" > $out/root-credentials ''; }; networking.firewall.interfaces.wg-clan.allowedTCPPorts = [ 9000 9001 ]; services.minio = let wg-clan-ip = builtins.elemAt (builtins.match "(.*)/.*" (builtins.elemAt config.networking.wireguard.interfaces.wg-clan.ips 0)) 0; in { enable = true; listenAddress = "${wg-clan-ip}:9000"; consoleAddress = "${wg-clan-ip}:9001"; region = "eu-central-1"; rootCredentialsFile = "${config.clan.core.vars.generators."minio".files."root-credentials".path}"; dataDir = [ "/mnt/data/minio/data" ]; configDir = "/mnt/data/minio/config"; }; systemd.services.minio = { environment = { MINIO_SERVER_URL = "https://vpn.s3.pablo.tools"; MINIO_BROWSER_REDIRECT_URL = "https://vpn.minio.pablo.tools"; }; }; }; } ================================================ FILE: modules/minio/policies/nextcloud-external.json ================================================ { "ID": "NextcloudExternalRepoPolicy", "Version": "2012-10-17", "Statement": [ { "Sid": "AllowObjects", "Effect": "Allow", "Action": ["s3:DeleteObject", "s3:GetObject", "s3:PutObject"], "Resource": ["arn:aws:s3:::nextcloud-external/*"] }, { "Sid": "AllowRepo", "Effect": "Allow", "Action": ["s3:GetBucketLocation", "s3:ListBucket"], "Resource": ["arn:aws:s3:::nextcloud-external"] } ] } ================================================ FILE: modules/minio/policies/restic.json ================================================ { "ID": "ResticRepoPolicy", "Version": "2012-10-17", "Statement": [ { "Sid": "AllowObjects", "Effect": "Allow", "Action": ["s3:DeleteObject", "s3:GetObject", "s3:PutObject"], "Resource": ["arn:aws:s3:::restic/*"] }, { "Sid": "AllowRepo", "Effect": "Allow", "Action": ["s3:GetBucketLocation", "s3:ListBucket"], "Resource": ["arn:aws:s3:::restic"] } ] } ================================================ FILE: modules/networking/default.nix ================================================ { config, lib, ... }: with lib; let cfg = config.pinpox.defaults.networking; in { options.pinpox.defaults.networking = { enable = mkEnableOption "Network defaults"; }; config = mkIf cfg.enable { networking = { # Additional hosts to put in /etc/hosts extraHosts = let wgIp = host: builtins.readFile ( config.clan.core.settings.directory + "/vars/per-machine/${host}/wireguard-wg-clan-ip/ipv4/value" ); mkWgEntry = host: "${wgIp host} ${host}.wireguard"; porreeWgIp = wgIp "porree"; in '' # Wireguard ${mkWgEntry "porree"} ${mkWgEntry "kartoffel"} ${mkWgEntry "birne"} ${mkWgEntry "kfbox"} # Public 94.16.114.42 porree-old.public 94.16.108.229 porree.public 46.38.242.17 kfbox.public 93.177.66.52 kfbox-old 5.181.48.121 mega.public # VPN protected services (porree via wireguard) ${porreeWgIp} vpn.motion.pablo.tools ${porreeWgIp} vpn.alerts.pablo.tools ${porreeWgIp} vpn.prometheus.pablo.tools ${porreeWgIp} vpn.notify.pablo.tools ${porreeWgIp} vpn.s3.pablo.tools ${porreeWgIp} vpn.minio.pablo.tools ''; }; }; } ================================================ FILE: modules/nextcloud/default.nix ================================================ { lib, pkgs, config, ... }: with lib; let cfg = config.pinpox.services.nextcloud; # Pin Nextcloud major version. # Refer to upstream docs for updating major versions package = pkgs.nextcloud33; in { options.pinpox.services.nextcloud = { enable = mkEnableOption "Nextcloud"; }; config = mkIf cfg.enable { # Backup pinpox.services.restic-client.backup-paths-onsite = [ "/var/lib/nextcloud" ]; pinpox.services.restic-client.backup-paths-offsite = [ # TODO Plan on how to backup nextcloud data # "${config.services.nextcloud.home}/data" "${config.services.nextcloud.home}/config" # "${config.services.nextcloud.home}/store-apps" ]; services.postgresql.package = pkgs.postgresql_17; clan.core.vars.generators."nextcloud" = { files.admin-pass-file = { owner = "nextcloud"; # path = "/var/lib/nextcloud/admin-pass"; }; runtimeInputs = with pkgs; [ coreutils xkcdpass ]; script = '' mkdir -p $out xkcdpass > $out/admin-pass-file ''; }; clan.core.vars.generators."nextcloud-smtp" = { files.smtp-secret.owner = "nextcloud"; prompts.smtp-password.persist = true; runtimeInputs = with pkgs; [ jq ]; script = '' mkdir -p $out jq -n --arg pw "$(cat "$prompts/smtp-password")" \ '{"mail_smtppassword": $pw}' > $out/smtp-secret ''; }; services.phpfpm.pools.nextcloud.settings = { "listen.owner" = config.services.caddy.user; "listen.group" = config.services.caddy.group; }; services.nextcloud = { caching.apcu = true; caching.redis = true; configureRedis = true; phpOptions."opcache.interned_strings_buffer" = "64"; # opcache.memory_consumption=256 # opcache.interned_strings_buffer=64 # opcache.max_accelerated_files=100000 secretFile = config.clan.core.vars.generators."nextcloud-smtp".files."smtp-secret".path; settings = { mail_smtpmode = "smtp"; mail_smtphost = "r19.hallo.cloud"; mail_smtpport = 587; mail_smtpsecure = ""; mail_smtpauth = true; mail_smtpauthtype = "LOGIN"; mail_smtpname = "mail@0cx.de"; mail_from_address = "files"; mail_domain = "pablo.tools"; maintenance_window_start = "4"; trusted_proxies = [ (builtins.readFile ( config.clan.core.settings.directory + "/vars/per-machine/porree/wireguard-wg-clan-ip/ipv4/value" )) "94.16.108.229" ]; trusted_domains = [ "birne.wireguard" ]; default_phone_region = "DE"; enabledPreviewProviders = [ "OC\\Preview\\BMP" "OC\\Preview\\GIF" "OC\\Preview\\JPEG" "OC\\Preview\\Krita" "OC\\Preview\\MarkDown" "OC\\Preview\\MP3" "OC\\Preview\\OpenDocument" "OC\\Preview\\PNG" "OC\\Preview\\TXT" "OC\\Preview\\XBitmap" "OC\\Preview\\HEIC" "OC\\Preview\\Movie" ]; }; enable = true; inherit package; # Use HTTPS for links https = true; # overwriteProtocol = "https"; hostName = "files.pablo.tools"; # Disable adding apps from the app store, apps are only configured # declaratively via nix appstoreEnable = false; extraApps = { inherit (package.packages.apps) mail calendar contacts # TODO https://github.com/pulsejet/memories/issues/1625 memories previewgenerator # maps twofactor_webauthn # TODO re-enable after https://github.com/NixOS/nixpkgs/pull/400158 # recognize music # phonetrack ; }; # phpExtraExtensions = []; home = "/var/lib/nextcloud"; poolSettings = { pm = "dynamic"; "pm.max_children" = "160"; "pm.max_requests" = "700"; "pm.max_spare_servers" = "120"; "pm.min_spare_servers" = "40"; "pm.start_servers" = "40"; }; config = { # Database dbtype = "pgsql"; dbuser = "nextcloud"; dbhost = "/run/postgresql"; dbname = "nextcloud"; # Admin user adminuser = "pinpox"; adminpassFile = "${config.clan.core.vars.generators."nextcloud".files."admin-pass-file".path}"; }; }; environment.systemPackages = with pkgs; [ exiftool ffmpeg ]; # To run nginx alongside caddy for nextcloud only services.nginx.enable = false; # services.nginx.virtualHosts."files.pablo.tools".listen = [{ addr = "0.0.0.0"; port = 8080; }]; # reverse_proxy http://127.0.0.1:8080 services.caddy.virtualHosts = { "files.pablo.tools".extraConfig = '' encode zstd gzip root * ${config.services.nginx.virtualHosts."files.pablo.tools".root} root /nix-apps/* ${config.services.nginx.virtualHosts."files.pablo.tools".root} redir /.well-known/carddav /remote.php/dav 301 redir /.well-known/caldav /remote.php/dav 301 redir /.well-known/* /index.php{uri} 301 redir /remote/* /remote.php{uri} 301 header { Strict-Transport-Security max-age=31536000 Permissions-Policy interest-cohort=() X-Content-Type-Options nosniff X-Frame-Options SAMEORIGIN Referrer-Policy strict-origin-when-cross-origin X-XSS-Protection "1; mode=block" X-Permitted-Cross-Domain-Policies none X-Robots-Tag "noindex, nofollow" -X-Powered-By } php_fastcgi unix//run/phpfpm/nextcloud.sock { root ${config.services.nginx.virtualHosts."files.pablo.tools".root} env front_controller_active true env modHeadersAvailable true } @forbidden { path /build/* /tests/* /config/* /lib/* /3rdparty/* /templates/* /data/* path /.* /autotest* /occ* /issue* /indie* /db_* /console* not path /.well-known/* } error @forbidden 404 @immutable { path *.css *.js *.mjs *.svg *.gif *.png *.jpg *.ico *.wasm *.tflite query v=* } header @immutable Cache-Control "max-age=15778463, immutable" @static { path *.css *.js *.mjs *.svg *.gif *.png *.jpg *.ico *.wasm *.tflite not query v=* } header @static Cache-Control "max-age=15778463" @woff2 path *.woff2 header @woff2 Cache-Control "max-age=604800" file_server ''; }; # Fix for memories # https://memories.gallery/troubleshooting/#trigger-compatibility-mode systemd.services.nextcloud-cron = { path = [ pkgs.perl ]; }; # Database configuration services.postgresql.enable = true; # Ensure that postgres is running *before* running the setup systemd.services."nextcloud-setup" = { requires = [ "postgresql.service" ]; after = [ "postgresql.service" ]; }; }; } ================================================ FILE: modules/nix-common/default.nix ================================================ { config, pkgs, lib, flake-self, nixpkgs, nix-index-database, ... }: with lib; let cfg = config.pinpox.defaults.nix; in { options.pinpox.defaults.nix = { enable = mkEnableOption "Nix defaults"; }; imports = [ nix-index-database.nixosModules.nix-index ]; config = mkIf cfg.enable { boot.loader.grub.configurationLimit = 5; _module.args.pinpox-utils = import ../../utils { inherit pkgs; }; # Use nix-index-database for comma programs.nix-index-database.comma.enable = true; # Generates a .prom file that can be scraped with prometheus to monitor the # current nixpkgs version environment.etc."nix/flake_inputs.prom" = { mode = "0555"; text = '' # HELP flake_registry_last_modified Last modification date of flake input in unixtime # TYPE flake_input_last_modified gauge ${concatStringsSep "\n" ( map ( i: ''flake_input_last_modified{input="${i}",${ concatStringsSep "," ( mapAttrsToList (n: v: ''${n}="${v}"'') ( filterAttrs ( n: v: (builtins.tryEval (builtins.typeOf v == "string")).value or false ) flake-self.inputs."${i}" ) ) }} ${toString flake-self.inputs."${i}".lastModified or 0}'' ) (attrNames flake-self.inputs) )} ''; }; # Set the $NIX_PATH entry for nixpkgs. This is necessary in # this setup with flakes, otherwise commands like `nix-shell # -p pkgs.htop` will keep using an old version of nixpkgs. # With this entry in $NIX_PATH it is possible (and # recommended) to remove the `nixos` channel for both users # and root e.g. `nix-channel --remove nixos`. `nix-channel # --list` should be empty for all users afterwards nix.nixPath = [ "nixpkgs=flake:nixpkgs" ]; nixpkgs.overlays = [ flake-self.overlays.default ]; # Let 'nixos-version --json' know the Git revision of this flake. system.configurationRevision = nixpkgs.lib.mkIf (flake-self ? rev) flake-self.rev; nix.registry.nixpkgs.flake = nixpkgs; nix.registry.pinpox.flake = flake-self; # Allow unfree licenced packages nixpkgs.config.allowUnfree = true; clan.core.vars.generators."nix" = { prompts.nix-access-tokens.persist = true; share = true; files."nix-access-tokens" = { group = "wheel"; mode = "0440"; }; }; # Enable flakes nix = { # Enable flakes package = pkgs.nixVersions.stable; extraOptions = '' fallback = true connect-timeout = 100 stalled-download-timeout = 100 !include ${config.clan.core.vars.generators."nix".files."nix-access-tokens".path} ''; settings = { auto-allocate-uids = true; system-features = [ "uid-range" ]; experimental-features = [ "nix-command" "flakes" "auto-allocate-uids" "cgroups" ]; trusted-users = [ "@wheel" ]; trusted-public-keys = [ "nix-cache:4FILs79Adxn/798F8qk2PC1U8HaTlaPqptwNJrXNA1g=" "nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs=" ]; # nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs= substituters = [ "https://cache.nixos.org" "https://nix-community.cachix.org" "https://cache.lounge.rocks/nix-cache" ]; trusted-substituters = [ "https://cache.nixos.org" "https://cache.lounge.rocks" ]; # Save space by hardlinking store files auto-optimise-store = true; # Users allowed to run nix allowed-users = [ "root" ]; }; # Clean up old generations after 30 days gc = { automatic = true; dates = "weekly"; options = "--delete-older-than 30d"; }; }; }; } ================================================ FILE: modules/ntfy-sh/default.nix ================================================ { lib, config, ... }: with lib; let cfg = config.pinpox.services.ntfy-sh; ntfy-port = "8090"; ntfy-host = "push.pablo.tools"; in { options.pinpox.services.ntfy-sh = { enable = mkEnableOption "ntfy-sh notification server"; }; config = mkIf cfg.enable { services.ntfy-sh = { enable = true; settings = { behind-proxy = true; listen-http = "127.0.0.1:${ntfy-port}"; base-url = "https://${ntfy-host}"; auth-file = "/var/lib/ntfy-sh/user.db"; auth-default-access = "deny-all"; upstream-base-url = "https://ntfy.sh"; # https://github.com/binwiederhier/ntfy/issues/459 web-root = "disable"; # Set to "app" to enable web UI }; }; users.users.ntfy-sh = { home = "/var/lib/ntfy-sh"; createHome = true; }; services.caddy.virtualHosts."${ntfy-host}".extraConfig = '' reverse_proxy 127.0.0.1:${ntfy-port} ''; }; } ================================================ FILE: modules/opencloud/default.nix ================================================ { lib, pkgs, config, ... }: with lib; let cfg = config.pinpox.services.opencloud; in { options.pinpox.services.opencloud = { enable = mkEnableOption "OpenCloud file sync and sharing"; host = mkOption { type = types.str; default = "cloud.pablo.tools"; description = "Host serving OpenCloud"; }; port = mkOption { type = types.port; default = 9200; description = "Port OpenCloud listens on"; }; oidcIssuer = mkOption { type = types.str; default = "https://auth.pablo.tools"; description = "OIDC issuer URL (Authelia)"; }; oidcClientId = mkOption { type = types.str; default = "opencloud"; description = "OIDC client ID"; }; }; config = mkIf cfg.enable { # Backup pinpox.services.restic-client.backup-paths-onsite = [ "/var/lib/opencloud" ]; pinpox.services.restic-client.backup-paths-offsite = [ "/var/lib/opencloud" ]; # Generate required secrets clan.core.vars.generators."opencloud" = { files.envfile = { owner = "opencloud"; group = "opencloud"; }; runtimeInputs = with pkgs; [ coreutils openssl util-linux ]; script = '' mkdir -p $out # Generate required secrets (matching opencloud init output) SERVICE_ACCOUNT_ID=$(uuidgen) STORAGE_UUID=$(uuidgen) # Generate LDAP passwords (same password used by multiple services) REVA_PASSWORD=$(openssl rand -base64 24) IDM_PASSWORD=$(openssl rand -base64 24) IDP_PASSWORD=$(openssl rand -base64 24) ADMIN_PASSWORD=$(openssl rand -base64 24) { echo "OC_JWT_SECRET=$(openssl rand -base64 24)" echo "OC_TRANSFER_SECRET=$(openssl rand -base64 24)" echo "OC_MACHINE_AUTH_API_KEY=$(openssl rand -base64 24)" echo "OC_SYSTEM_USER_ID=$(uuidgen)" echo "OC_SYSTEM_USER_API_KEY=$(openssl rand -base64 24)" echo "OC_ADMIN_USER_ID=$(uuidgen)" echo "GRAPH_APPLICATION_ID=$(uuidgen)" echo "OC_SERVICE_ACCOUNT_ID=$SERVICE_ACCOUNT_ID" echo "OC_SERVICE_ACCOUNT_SECRET=$(openssl rand -base64 24)" echo "STORAGE_USERS_MOUNT_ID=$STORAGE_UUID" echo "GATEWAY_STORAGE_USERS_MOUNT_ID=$STORAGE_UUID" echo "THUMBNAILS_TRANSFER_SECRET=$(openssl rand -base64 24)" echo "COLLABORATION_WOPI_SECRET=$(openssl rand -base64 24)" echo "OC_URL_SIGNING_SECRET=$(openssl rand -base64 24)" # LDAP bind passwords for internal services echo "IDM_ADMIN_PASSWORD=$ADMIN_PASSWORD" echo "IDM_SVC_PASSWORD=$IDM_PASSWORD" echo "IDM_REVASVC_PASSWORD=$REVA_PASSWORD" echo "IDM_IDPSVC_PASSWORD=$IDP_PASSWORD" echo "GRAPH_LDAP_BIND_PASSWORD=$IDM_PASSWORD" echo "OC_LDAP_BIND_PASSWORD=$REVA_PASSWORD" echo "LDAP_BIND_PASSWORD=$REVA_PASSWORD" echo "AUTH_BASIC_LDAP_BIND_PASSWORD=$REVA_PASSWORD" echo "GROUPS_LDAP_BIND_PASSWORD=$REVA_PASSWORD" echo "USERS_LDAP_BIND_PASSWORD=$REVA_PASSWORD" echo "IDP_LDAP_BIND_PASSWORD=$IDP_PASSWORD" } > $out/envfile ''; }; services.opencloud = { enable = true; url = "https://${cfg.host}"; address = "127.0.0.1"; port = cfg.port; environmentFile = config.clan.core.vars.generators."opencloud".files."envfile".path; environment = { OC_INSECURE = "true"; OC_LOG_LEVEL = "warn"; # Disable TLS on proxy - Caddy handles TLS termination PROXY_TLS = "false"; # Disable built-in IDP since we use external OIDC (Authelia) OC_EXCLUDE_RUN_SERVICES = "idp"; # External OIDC configuration OC_OIDC_ISSUER = cfg.oidcIssuer; PROXY_OIDC_ISSUER = cfg.oidcIssuer; PROXY_OIDC_REWRITE_WELLKNOWN = "false"; PROXY_OIDC_ACCESS_TOKEN_VERIFY_METHOD = "none"; PROXY_OIDC_SKIP_USER_INFO = "false"; # OIDC client ID for web WEB_OIDC_CLIENT_ID = cfg.oidcClientId; # Auto-provision accounts from OIDC PROXY_AUTOPROVISION_ACCOUNTS = "true"; PROXY_AUTOPROVISION_CLAIM_USERNAME = "preferred_username"; PROXY_AUTOPROVISION_CLAIM_EMAIL = "email"; PROXY_AUTOPROVISION_CLAIM_DISPLAYNAME = "name"; PROXY_AUTOPROVISION_CLAIM_GROUPS = "groups"; PROXY_USER_OIDC_CLAIM = "preferred_username"; PROXY_USER_CS3_CLAIM = "username"; GRAPH_USERNAME_MATCH = "none"; # Avoid port conflicts with prometheus exporters (9115 = blackbox_exporter) WEB_HTTP_ADDR = "127.0.0.1:9105"; WEBDAV_HTTP_ADDR = "127.0.0.1:9116"; COLLABORATION_HTTP_ADDR = "127.0.0.1:9300"; COLLABORATION_GRPC_ADDR = "127.0.0.1:9301"; }; # OpenCloud configuration (prevents init service from running) settings = { # Main opencloud config - non-empty value prevents opencloud init from running opencloud = { graph.spaces.insecure = true; proxy.insecure_backends = true; }; # Proxy config proxy.csp_config_file_location = "/etc/opencloud/csp.yaml"; # CSP - allow connecting to external OIDC provider csp.directives = { "connect-src" = ["https://${cfg.host}/" cfg.oidcIssuer]; "frame-src" = ["https://${cfg.host}/" cfg.oidcIssuer]; "script-src" = ["'self'" "'unsafe-inline'" "'unsafe-eval'"]; }; # Web UI OIDC configuration web.web.config = { server = "https://${cfg.host}"; oidc = { metadata_url = "${cfg.oidcIssuer}/.well-known/openid-configuration"; authority = cfg.oidcIssuer; client_id = cfg.oidcClientId; response_type = "code"; scope = "openid offline_access profile email groups"; }; }; }; }; # Caddy reverse proxy services.caddy.virtualHosts = { "${cfg.host}".extraConfig = '' reverse_proxy 127.0.0.1:${toString cfg.port} ''; }; }; } ================================================ FILE: modules/opencrow/default.nix ================================================ { opencrow, mics-skills, config, lib, pkgs, pinpox-utils, ... }: with lib; let cfg = config.pinpox.services.opencrow; stateDir = "/var/lib/opencrow-claude"; localStateDir = "/var/lib/opencrow-local"; # models.json for the local instance to discover ollama on spark1 localPiModelsJson = pkgs.writeText "opencrow-local-models.json" ( builtins.toJSON { providers.ollama = { baseUrl = "http://100.96.100.103:11434/v1"; api = "openai-completions"; apiKey = "dummy"; compat = { supportsDeveloperRole = false; supportsReasoningEffort = false; }; models = [ { id = "gemma4:26b"; reasoning = true; } ]; }; } ); himalayaVars = config.clan.core.vars.generators."opencrow-himalaya"; # Himalaya config for the mail fetcher (IMAP only, no SMTP) himalayaFetcherConfig = pkgs.writeText "himalaya-fetcher-config.toml" '' [accounts."mailbox.org"] default = true display-name = "opencrow-fetcher" downloads-dir = "/tmp" backend.type = "imap" backend.host = "imap.mailbox.org" backend.port = 993 backend.encryption.type = "tls" backend.auth.type = "password" backend.auth.command = "printenv EMAIL_PASSWORD" message.send.backend.type = "none" ''; # Script that fetches starred/flagged emails to a directory # NOTE: goimapnotify does not pass its environment to child scripts, # so we source the envfile explicitly to get EMAIL_PASSWORD/EMAIL_LOGIN. onChangedMailScript = pkgs.writeShellScript "opencrow-fetch-starred" '' set -euo pipefail set -a source ${himalayaVars.files."envfile".path} set +a HIMALAYA="${lib.getExe pkgs.himalaya} -c ${himalayaFetcherConfig} -c ${himalayaVars.files."config".path}" MAIL_DIR="${stateDir}/mail-inbox" PIPE="${stateDir}/sessions/trigger.pipe" mkdir -p "$MAIL_DIR" # List flagged messages, get their IDs ids=$($HIMALAYA envelope list --folder INBOX --output json flag flagged | ${lib.getExe pkgs.jq} -r '.[].id') [ -z "$ids" ] && exit 0 for id in $ids; do $HIMALAYA message read "$id" > "$MAIL_DIR/$(date +%s)-''${id}.txt" $HIMALAYA flag remove "$id" flagged $HIMALAYA flag add "$id" crow-processed $HIMALAYA message move Archive "$id" done # Notify the bot [ -p "$PIPE" ] && echo "New starred emails arrived. Read them from $MAIL_DIR" > "$PIPE" ''; # goimapnotify configuration goimapnotifyConfig = pkgs.writeText "goimapnotify-config.yaml" (builtins.toJSON { configurations = [ { host = "imap.mailbox.org"; port = 993; tls = true; tlsOptions = { rejectUnauthorized = true; }; usernameCMD = "${lib.getExe' pkgs.coreutils "printenv"} EMAIL_LOGIN"; passwordCMD = "${lib.getExe' pkgs.coreutils "printenv"} EMAIL_PASSWORD"; boxes = [ { mailbox = "INBOX"; onChangedMail = toString onChangedMailScript; } ]; } ]; }); in { imports = [ opencrow.nixosModules.default ]; options.pinpox.services.opencrow.enable = mkEnableOption "opencrow Matrix bot"; config = mkIf cfg.enable { # OpenCrow Matrix bot clan.core.vars.generators."opencrow" = pinpox-utils.mkEnvGenerator [ "OPENCROW_MATRIX_ACCESS_TOKEN" "OPENCROW_MATRIX_USER_ID" ]; # Nextcloud (personal) clan.core.vars.generators."opencrow-nextcloud" = pinpox-utils.mkEnvGenerator [ "NEXTCLOUD_PASSWORD" ]; # Nextcloud (work) clan.core.vars.generators."opencrow-nextcloud-work" = pinpox-utils.mkEnvGenerator [ "WORK_NEXTCLOUD_PASSWORD" ]; # Eversports clan.core.vars.generators."opencrow-eversports" = pinpox-utils.mkEnvGenerator [ "EVERSPORTS_EMAIL" "EVERSPORTS_PASSWORD" ]; # Himalaya email secrets (TOML config + env file for EMAIL_PASSWORD) # Used by the goimapnotify fetcher service for IMAP credentials clan.core.vars.generators."opencrow-himalaya" = { files.config = { }; files.envfile = { }; runtimeInputs = [ pkgs.coreutils ]; prompts.EMAIL_ADDRESS.persist = false; prompts.EMAIL_LOGIN.persist = false; prompts.EMAIL_PASSWORD.persist = false; script = '' mkdir -p $out cat > $out/config << TOML [accounts."mailbox.org"] email = "$(cat $prompts/EMAIL_ADDRESS)" backend.login = "$(cat $prompts/EMAIL_LOGIN)" TOML cat > $out/envfile << ENV EMAIL_LOGIN='$(cat $prompts/EMAIL_LOGIN)' EMAIL_PASSWORD='$(cat $prompts/EMAIL_PASSWORD)' ENV ''; }; # Local instance matrix credentials (migrated from traube) clan.core.vars.generators."opencrow-local" = pinpox-utils.mkEnvGenerator [ "OPENCROW_MATRIX_ACCESS_TOKEN" ]; # Mail inbox directory for fetched emails # Deutschebahn skill symlinked from mics-skills flake systemd.tmpfiles.rules = [ "d ${stateDir}/mail-inbox 0777 root root -" "L+ ${stateDir}/skills/deutschebahn - - - - ${mics-skills}/skills/db-cli" "L+ ${localStateDir}/skills/deutschebahn - - - - ${mics-skills}/skills/db-cli" "L+ ${localStateDir}/pi-agent/models.json - - - - ${localPiModelsJson}" ]; # goimapnotify service: watches for starred emails and fetches them systemd.services.opencrow-goimapnotify = { description = "Watch IMAP for starred emails and fetch them for opencrow"; after = [ "network-online.target" ]; wants = [ "network-online.target" ]; wantedBy = [ "multi-user.target" ]; path = with pkgs; [ coreutils bash himalaya jq ]; serviceConfig = { ExecStart = "${lib.getExe pkgs.goimapnotify} -conf ${goimapnotifyConfig}"; EnvironmentFile = himalayaVars.files."envfile".path; Restart = "on-failure"; RestartSec = "10s"; }; }; services.opencrow.instances.claude = { enable = true; piPackage = pkgs.pi; environmentFiles = [ config.clan.core.vars.generators."opencrow".files."envfile".path config.clan.core.vars.generators."opencrow-nextcloud".files."envfile".path config.clan.core.vars.generators."opencrow-nextcloud-work".files."envfile".path config.clan.core.vars.generators."opencrow-eversports".files."envfile".path ]; extraPackages = [ pkgs.pi pkgs.curl pkgs.jq mics-skills.packages.${pkgs.system}.db-cli ]; environment = { NEXTCLOUD_URL = "https://files.pablo.tools"; NEXTCLOUD_USER = "pinpox"; NEXTCLOUD_CALENDAR = "personal"; WORK_NEXTCLOUD_URL = "https://nextcloud.clan.lol"; WORK_NEXTCLOUD_USER = "pinpox"; WORK_NEXTCLOUD_CALENDAR = "personal"; OPENCROW_MATRIX_HOMESERVER = "https://matrix.org"; OPENCROW_ALLOWED_USERS = "@pinpox:matrix.org"; OPENCROW_HEARTBEAT_INTERVAL = "30m"; OPENCROW_PI_SKILLS_DIR = "${stateDir}/skills"; }; }; services.opencrow.instances.local = { enable = true; piPackage = pkgs.pi; environmentFiles = [ config.clan.core.vars.generators."opencrow-local".files."envfile".path config.clan.core.vars.generators."opencrow-nextcloud".files."envfile".path config.clan.core.vars.generators."opencrow-nextcloud-work".files."envfile".path config.clan.core.vars.generators."opencrow-eversports".files."envfile".path ]; extraPackages = [ pkgs.pi pkgs.curl pkgs.jq mics-skills.packages.${pkgs.system}.db-cli ]; environment = { NEXTCLOUD_URL = "https://files.pablo.tools"; NEXTCLOUD_USER = "pinpox"; NEXTCLOUD_CALENDAR = "personal"; WORK_NEXTCLOUD_URL = "https://nextcloud.clan.lol"; WORK_NEXTCLOUD_USER = "pinpox"; WORK_NEXTCLOUD_CALENDAR = "personal"; OPENCROW_MATRIX_HOMESERVER = "https://matrix.org"; OPENCROW_MATRIX_USER_ID = "@c.h.i.m.p.:matrix.org"; OPENCROW_ALLOWED_USERS = "@pinpox:matrix.org"; OPENCROW_PI_PROVIDER = "ollama"; OPENCROW_PI_MODEL = "gemma4:26b"; OPENCROW_PI_SKILLS_DIR = "${localStateDir}/skills"; OPENCROW_HEARTBEAT_INTERVAL = "30m"; OPENCROW_PI_IDLE_TIMEOUT = "12h"; OPENCROW_LOG_LEVEL = "debug"; }; }; }; } ================================================ FILE: modules/openssh/ca.pub ================================================ ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAPcS8NMzwYLvKFOXeTZwX/W6ua0zIzs4zA0PW0xz62i user-ca ================================================ FILE: modules/openssh/default.nix ================================================ { config, pkgs, lib, pinpox-keys, ... }: with lib; let cfg = config.pinpox.services.openssh; in { options.pinpox.services.openssh = { enable = mkEnableOption "OpenSSH server"; }; config = mkIf cfg.enable { # Enable the OpenSSH daemon. services.openssh = { enable = true; startWhenNeeded = true; settings = { PasswordAuthentication = false; KbdInteractiveAuthentication = false; }; }; # Block anything that is not HTTP(s) or SSH. networking.firewall = { enable = true; allowPing = true; allowedTCPPorts = [ 22 ]; }; users.users.root.openssh.authorizedKeys.keyFiles = [ pinpox-keys ]; services.openssh.extraConfig = "TrustedUserCAKeys ${./ca.pub}"; }; } ================================================ FILE: modules/owncast/default.nix ================================================ { config, lib, ... }: with lib; let cfg = config.pinpox.services.owncast; in { options.pinpox.services.owncast = { enable = mkEnableOption "owncast server"; host = mkOption { type = types.str; default = "stream.0cx.de"; description = "Host serving owncast"; example = "stream.0cx.de"; }; }; config = mkIf cfg.enable { services.owncast = { enable = true; port = 9768; rtmp-port = 1935; }; networking.firewall.allowedTCPPorts = [ config.services.owncast.rtmp-port ]; services.caddy = { enable = true; virtualHosts."${cfg.host}".extraConfig = "reverse_proxy 127.0.0.1:${builtins.toString config.services.owncast.port}"; }; }; } ================================================ FILE: modules/paperless/default.nix ================================================ { config, lib, pkgs, ... }: with lib; let cfg = config.pinpox.services.paperless; paperlessPermsApp = ./django-apps; paperlessCfg = config.services.paperless; paperlessPythonPath = "${paperlessCfg.package.python.pkgs.makePythonPath paperlessCfg.package.propagatedBuildInputs}:${paperlessCfg.package}/lib/paperless-ngx/src:${paperlessPermsApp}"; in { options.pinpox.services.paperless = { enable = mkEnableOption "paperless-ngx document management"; host = mkOption { type = types.str; default = "paper.pablo.tools"; description = "Host serving paperless-ngx"; example = "paper.pablo.tools"; }; }; config = mkIf cfg.enable { clan.core.vars.generators."paperless" = { files.password = { }; runtimeInputs = with pkgs; [ coreutils xkcdpass ]; script = '' mkdir -p $out xkcdpass -d- > $out/password ''; }; services.paperless = { enable = true; address = "127.0.0.1"; port = 28981; passwordFile = config.clan.core.vars.generators."paperless".files."password".path; settings = { PAPERLESS_URL = "https://${cfg.host}"; PAPERLESS_OCR_LANGUAGE = "deu+eng"; PAPERLESS_CONSUMER_RECURSIVE = true; PAPERLESS_CONSUMER_SUBDIRS_AS_TAGS = true; PAPERLESS_ADMIN_USER = "pinpox"; PAPERLESS_ENABLE_HTTP_REMOTE_USER = true; PAPERLESS_HTTP_REMOTE_USER_HEADER_NAME = "HTTP_REMOTE_USER"; PAPERLESS_LOGOUT_REDIRECT_URL = "/"; PAPERLESS_APPS = "paperless_perms.apps.PaperlessPermsConfig"; }; }; # Add the perms Django app to PYTHONPATH for all paperless services systemd.services.paperless-web.environment.PYTHONPATH = lib.mkForce paperlessPythonPath; systemd.services.paperless-task-queue.environment.PYTHONPATH = paperlessPythonPath; systemd.services.paperless-consumer.environment.PYTHONPATH = paperlessPythonPath; systemd.services.paperless-scheduler.environment.PYTHONPATH = paperlessPythonPath; services.caddy = { enable = true; virtualHosts."${cfg.host}".extraConfig = '' forward_auth http://127.0.0.1:9091 { uri /api/authz/forward-auth copy_headers Remote-User Remote-Groups Remote-Name Remote-Email } reverse_proxy ${config.services.paperless.address}:${toString config.services.paperless.port} ''; }; pinpox.services.restic-client.backup-paths-offsite = [ "${config.services.paperless.dataDir}" ]; }; } ================================================ FILE: modules/paperless/django-apps/paperless_perms/__init__.py ================================================ ================================================ FILE: modules/paperless/django-apps/paperless_perms/apps.py ================================================ """Auto-manage permissions for Remote-User accounts in Paperless-ngx. Loaded as a Django app via PAPERLESS_APPS. On startup it: 1. Creates an "editors" group with full CRUD permissions on all models. 2. Adds all non-service Remote-User accounts to the editors group. 3. Installs a post_save signal so new accounts are handled automatically. """ import logging from django.apps import AppConfig from django.db.models.signals import post_save logger = logging.getLogger(__name__) SERVICE_ACCOUNTS = {"consumer", "AnonymousUser"} def _setup_editors_group(): from django.contrib.auth.models import Group, Permission from django.contrib.contenttypes.models import ContentType editors_group, _ = Group.objects.get_or_create(name="editors") permissions = [] for ct in ContentType.objects.all(): permissions.extend( Permission.objects.filter( content_type=ct, codename__regex=r"^(view|change|add|delete)_", ) ) editors_group.permissions.set(permissions) return editors_group def _promote_user(user, editors_group): if not user.groups.filter(name="editors").exists(): user.groups.add(editors_group) logger.info("Added user %s to editors group", user.username) def _on_user_created(sender, instance, created, **kwargs): if not created or instance.username in SERVICE_ACCOUNTS: return from django.contrib.auth.models import Group try: editors = Group.objects.get(name="editors") except Group.DoesNotExist: return _promote_user(instance, editors) class PaperlessPermsConfig(AppConfig): name = "paperless_perms" verbose_name = "Paperless permission management" def ready(self): from django.contrib.auth.models import User post_save.connect(_on_user_created, sender=User) try: editors_group = _setup_editors_group() for user in User.objects.exclude(username__in=SERVICE_ACCOUNTS): _promote_user(user, editors_group) except Exception: logger.exception("Failed initial permission setup") ================================================ FILE: modules/radio/default.nix ================================================ { config, lib, radio, pkgs, ... }: with lib; let cfg = config.pinpox.services.radio; in { options.pinpox.services.radio = { enable = mkEnableOption "web radio streamer"; host = mkOption { type = types.str; default = "radio.0cx.de"; description = "Host serving the radio"; example = "radio.0cx.de"; }; }; config = mkIf cfg.enable { services.caddy = { enable = true; virtualHosts."${cfg.host}".extraConfig = "reverse_proxy 127.0.0.1:7000"; }; systemd.services.radio = let stationsfile = pkgs.writeTextFile { name = "stations.ini"; text = '' [Hirschmilch Psytrance] url = "https://hirschmilch.de:7000/psytrance.mp3" [Hirschmilch Progressive] url = "https://hirschmilch.de:7000/progressive.mp3" [Lassulus Radio] url = "https://radio.lassul.us/radio.mp3" [Hirschmilch chillout] url = "https://hirschmilch.de:7000/chillout.mp3" [Nightride FM] url = https://stream.rekt.network/nightride.ogg [Rekt Network] url = https://stream.rekt.network/rekt.ogg ''; }; in { wantedBy = [ "multi-user.target" ]; environment = { RADIO_ADDRESS = "127.0.0.1:7000"; RADIO_STATIONFILE = stationsfile; GIN_MODE = "release"; }; serviceConfig = { DynamicUser = true; ExecStart = "${radio.packages.x86_64-linux.default}/bin/radio"; Restart = "on-failure"; RestartSec = "5s"; }; }; }; } ================================================ FILE: modules/restic/default.nix ================================================ { lib, pkgs, config, pinpox-utils, ... }: with lib; let cfg = config.pinpox.services.restic-client; in { options.pinpox.services.restic-client = { enable = mkEnableOption "restic backups"; backup-paths-onsite = mkOption { type = types.listOf types.str; default = [ ]; example = [ "/home/pinpox/Notes" ]; description = "Paths to backup to onsite storage"; }; backup-paths-offsite = mkOption { type = types.listOf types.str; default = [ ]; example = [ "/home/pinpox/Notes" ]; description = "Paths to backup to offsite storage"; }; backup-paths-exclude = mkOption { type = types.listOf types.str; default = [ "*.pyc" "*/.BurpSuite" "*/.arduino15/packages" "*/.cache" "*/.cargo" "*/.coc" "*/.config/Nextcloud/logs" "*/.config/Signal" "*/.config/chromium" "*/.config/discord" "*/.config/retroarch" "*/.container-diff" "*/.go/pkg" "*/.gvfs/" "*/.local/share/Steam" "*/.local/share/Trash" "*/.local/share/tor-browser" "*/.local/share/typeracer" "*/.local/share/virtualenv" "*/.local/state/NvChad/" "*/.mozilla/firefox" "*/.nextcloud" "*/.npm" "*/.npm/_cacache" "*/.platformio" "*/.rustup" "*/.thumbnails" "*/.ts3client" "*/.vagrant.d" "*/.vim" "*/.vimtemp" "*/Cache" "*/Downloads" "*/Seafile" "*/VirtualBox VMs" "*/cache2" "*/code" "/var/lib/docker" "discord/Cache" "tags" ]; example = [ "/home/pinpox/cache" ]; description = "Paths to exclude from backup"; }; }; config = let s3-generator = (pinpox-utils.mkEnvGenerator [ "AWS_ACCESS_KEY_ID" "AWS_SECRET_ACCESS_KEY" "NTFY_USER" "NTFY_PASS" ]) // { share = true; }; in mkIf cfg.enable { clan.core.vars.generators."restic-credentials" = s3-generator; clan.core.vars.generators."restic-credentials-backblaze" = s3-generator; clan.core.vars.generators."restic" = { files.repo-pw = { }; runtimeInputs = with pkgs; [ coreutils xkcdpass ]; script = '' mkdir -p $out xkcdpass -d- > $out/repo-pw ''; }; clan.core.vars.generators."restic-offsite" = { prompts.repo-pw.persist = true; share = true; }; services.restic.backups = let script-post = host: site: '' if [ $EXIT_STATUS -ne 0 ]; then ${pkgs.curl}/bin/curl -u $NTFY_USER:$NTFY_PASS \ -H 'Title: Backup (${site}) on ${host} failed!' \ -H 'Tags: backup,restic,${host},${site}' \ -d "Restic (${site}) backup error on ${host}!" 'https://push.pablo.tools/pinpox_backups' else ${pkgs.curl}/bin/curl -u $NTFY_USER:$NTFY_PASS \ -H 'Title: Backup (${site}) on ${host} successful!' \ -H 'Tags: backup,restic,${host},${site}' \ -d "Restic (${site}) backup success on ${host}!" 'https://push.pablo.tools/pinpox_backups' fi ''; restic-ignore-file = pkgs.writeTextFile { name = "restic-ignore-file"; text = builtins.concatStringsSep "\n" cfg.backup-paths-exclude; }; pruneOpts = [ "--keep-daily 7" "--keep-weekly 5" "--keep-monthly 12" "--keep-yearly 75" ]; extraBackupArgs = [ "--exclude-file=${restic-ignore-file}" "--one-file-system" "-vv" ]; in { s3-offsite = { paths = cfg.backup-paths-offsite; repository = "s3:https://s3.us-east-005.backblazeb2.com/pinpox-restic"; environmentFile = "${config.clan.core.vars.generators."restic-credentials-backblaze".files."envfile".path }"; passwordFile = "${config.clan.core.vars.generators."restic-offsite".files."repo-pw".path}"; backupCleanupCommand = script-post config.networking.hostName "backblaze"; inherit pruneOpts extraBackupArgs; }; s3-onsite = { paths = cfg.backup-paths-onsite; repository = "s3:https://vpn.s3.pablo.tools/restic"; environmentFile = "${config.clan.core.vars.generators."restic-credentials".files."envfile".path}"; passwordFile = "${config.clan.core.vars.generators."restic".files."repo-pw".path}"; backupCleanupCommand = script-post config.networking.hostName "NAS"; inherit pruneOpts extraBackupArgs; }; }; }; } ================================================ FILE: modules/screego/default.nix ================================================ { config, lib, pkgs, ... }: let cfg = config.pinpox.services.screego; in { options.pinpox.services.screego = { enable = lib.mkEnableOption "screego server"; domain = lib.mkOption { type = lib.types.str; default = "0cx.de"; description = "Domain to create the sudomains unders"; }; }; config = lib.mkIf cfg.enable { services.caddy = { enable = true; virtualHosts = { "screen.${cfg.domain}".extraConfig = "reverse_proxy 127.0.0.1:5050"; "turn.${cfg.domain}".extraConfig = "reverse_proxy 127.0.0.1:5050"; }; }; clan.core.vars.generators."screego" = { files.envfile = { }; files.users = { }; files.prometheus-pass = { }; runtimeInputs = with pkgs; [ coreutils screego xkcdpass ]; script = '' echo "SCREEGO_SECRET=$(tr -dc A-Za-z0-9 < /dev/urandom | head -c 40)" > $out/envfile xkcdpass -n 4 -d - > $out/prometheus-pass cat $out/prometheus-pass | screego hash --name "prometheus" --pass - > $out/users ''; }; systemd.services.screego.serviceConfig.LoadCredential = [ "users:${config.clan.core.vars.generators.screego.files."users".path}" ]; services.screego = { enable = true; openFirewall = true; environmentFile = "${config.clan.core.vars.generators.screego.files."envfile".path}"; settings = { # SCREEGO_EXTERNAL_IP = "46.38.242.17"; SCREEGO_EXTERNAL_IP = "dns:screen.${cfg.domain}"; SCREEGO_SERVER_TLS = "false"; SCREEGO_CORS_ALLOWED_ORIGINS = "https://screen.${cfg.domain}"; SCREEGO_USERS_FILE = "%d/users"; SCREEGO_PROMETHEUS = "true"; }; }; }; } ================================================ FILE: modules/sound/default.nix ================================================ { config, pkgs, lib, ... }: with lib; let cfg = config.pinpox.defaults.sound; in { options.pinpox.defaults.sound = { enable = mkEnableOption "sound defaults"; }; config = mkIf cfg.enable { environment.systemPackages = [ pkgs.qjackctl ]; services.pipewire = { enable = true; # Use pipeware to emulate jack and pulseaudio jack.enable = true; pulse.enable = true; alsa.enable = true; configPackages = let rnnoiseFilter = { nodes = [ { type = "ladspa"; name = "rnnoise"; plugin = "${pkgs.rnnoise-plugin}/lib/ladspa/librnnoise_ladspa.so"; label = "noise_suppressor_mono"; control = { "VAD Threshold (%)" = 75.0; "VAD Grace Period (ms)" = 200; "Retroactive VAD Grace (ms)" = 100; }; } ]; }; mkFilterChain = { name, capture, playback, }: { name = "libpipewire-module-filter-chain"; args = { "node.description" = name; "media.name" = name; "filter.graph" = rnnoiseFilter; "capture.props" = { "audio.rate" = 48000; } // capture; "playback.props" = { "audio.rate" = 48000; } // playback; }; }; inputFilter = mkFilterChain { name = "Noise Cancelling Source"; capture = { # > indicate that a link is passive and does not cause the graph to be runnable. # https://docs.pipewire.org/group__pw__keys.html#gafcd3d133168b9353c89c1c5f2de6954e "node.passive" = true; "node.name" = "capture.rnnoise_source"; # Explicitly capture from Scarlett Input 1 (left XLR) "node.target" = "alsa_input.usb-Focusrite_Scarlett_2i2_USB_Y87MV6G157830B-00.HiFi__Mic1__source"; }; playback = { "node.name" = "rnnoise_source"; "media.class" = "Audio/Source"; }; }; outputFilter = mkFilterChain { name = "Noise Cancelling Sink"; capture = { "node.name" = "capture.rnnoise_sink"; "media.class" = "Audio/Sink"; }; playback = { "node.passive" = true; "node.name" = "rnnoise_sink"; "media.class" = "Stream/Output/Audio"; }; }; config = { "context.modules" = [ inputFilter outputFilter ]; }; in [ (pkgs.writeTextDir "share/pipewire/pipewire.conf.d/99-input-denoising.conf" ( builtins.toJSON config )) ]; }; # Set Scarlett 2i2 Input 1 to Inst mode when the device appears services.udev.extraRules = '' ACTION=="add", SUBSYSTEM=="sound", ATTR{id}=="USB", RUN+="${pkgs.alsa-utils}/bin/amixer -c USB cset name='Line In 1 Level Capture Enum' 'Inst'" ''; # Hide "Monitor of ..." sources from applications like pavucontrol services.pipewire.wireplumber.extraConfig."50-hide-monitors" = { "wireplumber.settings" = { "node.features.audio.monitor-ports" = false; }; }; # Force Scarlett 2i2 to use the "Direct" profile services.pipewire.wireplumber.extraConfig."50-scarlett-profile" = { "monitor.alsa.rules" = [ { matches = [ { "device.name" = "alsa_card.usb-Focusrite_Scarlett_2i2_USB_Y87MV6G157830B-00"; } ]; actions = { update-props = { "device.profile" = "Direct"; }; }; } ]; }; # Use noisetorch (RNnoise) to create a virtual source with noise removal programs.noisetorch.enable = true; # services.pipewire.wireplumber.enable = true; # environment.etc."wireplumber/main.lua.d/90-suspend-timeout.lua" = { # text = '' # session.suspend-timeout-seconds = 0 # ''; # }; }; } ================================================ FILE: modules/storagebox/default.nix ================================================ { config, pkgs, lib, ... }: with lib; let cfg = config.pinpox.defaults.storagebox; in { options.pinpox.defaults.storagebox = { enable = mkEnableOption "storagebox access"; mountOnAccess = mkOption { type = types.bool; default = false; description = "Whether to mount on access, instead of permanently"; example = true; }; mountPoint = mkOption { type = types.str; default = "/mnt/storagebox"; description = "Where to mount the storage"; example = "/mnt/music"; }; }; config = mkIf cfg.enable { # Hard-code an unsused gid for the group users.groups.storage-users.gid = 982; # SSH keypair generator for Hetzner Storage Box clan.core.vars.generators."storagebox-ssh" = { share = true; files.ssh-private-key = { }; files.ssh-public-key.secret = false; runtimeInputs = with pkgs; [ openssh ]; script = '' mkdir -p $out ssh-keygen -t ed25519 -f $out/ssh-private-key -N "" -C "kiwi-storagebox" mv $out/ssh-private-key.pub $out/ssh-public-key ''; }; # Add rclone to system packages for mount helper support environment.systemPackages = [ pkgs.rclone ]; # Hetzner Storage Box mount with rclone - using proper mount helper # Create cache directory for rclone systemd.tmpfiles.rules = [ "d /var/cache/rclone-storagebox 0750 root storage-users -" ]; fileSystems."${cfg.mountPoint}" = { device = ":sftp:"; fsType = "rclone"; options = [ "rw" "nofail" "_netdev" "x-systemd.mount-timeout=120s" "args2env" "config=/dev/null" "vfs_cache_mode=full" "cache_dir=/var/cache/rclone-storagebox" "checkers=8" "gid=${toString config.users.groups.storage-users.gid}" "umask=007" "allow_other" "allow_non_empty" "links" "sftp_host=u515095.your-storagebox.de" "sftp_user=u515095" "sftp_port=23" "sftp_key_file=${config.clan.core.vars.generators."storagebox-ssh".files."ssh-private-key".path}" "vfs_cache_max_size=2G" "vfs_cache_max_age=5m" "vfs_read_ahead=128M" "buffer_size=64M" "dir_cache_time=30s" "log_level=INFO" "log_systemd=true" ] ++ optionals cfg.mountOnAccess [ "noauto" "x-systemd.automount" "x-systemd.idle-timeout=600" ]; }; }; } ================================================ FILE: modules/twitch-first/default.nix ================================================ { config, lib, pkgs, pinpox-utils, twitch-first, ... }: with lib; let cfg = config.pinpox.services.twitch-first; in { options.pinpox.services.twitch-first = { enable = mkEnableOption "Twitch 'first' channel point redemption tracker"; }; config = mkIf cfg.enable { clan.core.vars.generators."twitch-first" = pinpox-utils.mkEnvGenerator [ "TWITCH_CLIENT_ID" "TWITCH_CLIENT_SECRET" "TWITCH_CHANNEL" "TWITCH_REWARD_ID" ]; systemd.services.twitch-first = { description = "Twitch first channel point tracker"; wantedBy = [ "multi-user.target" ]; after = [ "network-online.target" ]; wants = [ "network-online.target" ]; serviceConfig = { ExecStart = "${twitch-first.packages.${pkgs.system}.twitch-first}/bin/twitch-first"; EnvironmentFile = config.clan.core.vars.generators."twitch-first".files."envfile".path; DynamicUser = true; StateDirectory = "twitch-first"; WorkingDirectory = "/var/lib/twitch-first"; Restart = "on-failure"; RestartSec = 10; }; environment = { TWITCH_FIRST_DB = "/var/lib/twitch-first/firsts.db"; TWITCH_TOKEN_FILE = "/var/lib/twitch-first/token.json"; }; }; }; } ================================================ FILE: modules/unbound-desktop/default.nix ================================================ { lib, config, ... }: with lib; let cfg = config.pinpox.services.unbound-desktop; in { options.pinpox.services.unbound-desktop = { enable = mkEnableOption "local unbound for desktops"; }; config = mkIf cfg.enable { services.avahi = { enable = true; nssmdns4 = true; nssmdns6 = true; openFirewall = true; publish = { enable = true; addresses = true; workstation = true; userServices = true; domain = true; }; }; networking.networkmanager.insertNameservers = config.services.unbound.settings.server.interface; # networking.networkmanager.dns = "unbound"; # services.resolved.enable = false; networking.search = [ "fritz.box" ]; services.unbound = { enable = true; settings = { server = { interface = [ "127.0.0.1" ]; # include = [ # "\"${dns-overwrites-config}\"" # "\"${flake-self.inputs.adblock-unbound.packages.${pkgs.system}.unbound-adblockStevenBlack}\"" # ]; access-control = [ "127.0.0.0/8 allow" ]; }; domain-insecure = [ "fritz.box" ]; stub-zone = [ { name = "fritz.box"; stub-addr = "192.168.101.1"; } ]; forward-zone = [ { name = "google.*."; forward-addr = [ "8.8.8.8@853#dns.google" "8.8.8.4@853#dns.google" ]; forward-tls-upstream = "yes"; } { name = "."; forward-addr = [ "1.1.1.1@853#cloudflare-dns.com" "1.0.0.1@853#cloudflare-dns.com" "192.168.101.1" ]; forward-tls-upstream = "yes"; } ]; # remote-control.control-enable = true; }; }; }; } ================================================ FILE: modules/unifi-controller/default.nix ================================================ { lib, config, ... }: with lib; let cfg = config.pinpox.services.unifi-controller; in { options.pinpox.services.unifi-controller.enable = mkEnableOption "unifi controller (docker)"; config = mkIf cfg.enable { users.users.unifi = { isSystemUser = true; description = "unifi user"; extraGroups = [ "unifi" ]; group = "unifi"; createHome = true; home = "/var/lib/unifi"; }; users.groups.unifi.name = "unifi"; # Access locally via: # https://birne:8443/manage/ # Set inform via ssh: # set-inform http://birne:8080/inform virtualisation.oci-containers.containers = { unifi-contoller = { autoStart = true; image = "linuxserver/unifi-controller:version-5.6.42"; environment = { "PUID" = toString config.users.users.unifi.uid; "PGID" = toString config.users.groups.unifi.gid; # "TZ" = "Etc/UTC"; # "MEM_LIMIT" = "4096"; # "MEM_STARTUP" = "4096"; }; ports = [ "3478:3478/udp" "8080:8080" "8081:8081" "8443:8443" "8843:8843" "8880:8880" ]; volumes = [ "${config.users.users.unifi.home}/config:/config" ]; }; }; networking.firewall = { allowedUDPPorts = [ 3478 # Unifi UDP port used for STUN. 10001 # Unifi UDP port used for device discovery. ]; allowedTCPPorts = [ 8080 # Unifi port for UAP to inform controller. 8880 # Unifi port for HTTP portal redirect, if guest portal is enabled. 8843 # Unifi port for HTTPS portal redirect, ditto. 6789 # Unifi port for UniFi mobile speed test. ]; }; }; } ================================================ FILE: modules/vaultwarden/default.nix ================================================ { config, lib, pinpox-utils, ... }: with lib; let cfg = config.pinpox.services.vaultwarden; in { options.pinpox.services.vaultwarden = { enable = mkEnableOption "vaultwarden password manager"; host = mkOption { type = types.str; default = "pass.pablo.tools"; description = "Host serving vaultwarden"; example = "pass.pablo.tools"; }; }; config = mkIf cfg.enable { services.caddy = { enable = true; virtualHosts."${cfg.host}".extraConfig = '' reverse_proxy 127.0.0.1:${builtins.toString config.services.vaultwarden.config.ROCKET_PORT} ''; }; systemd.services.backup-vaultwarden.serviceConfig.StateDirectory = "vaultwarden-backups"; services.vaultwarden = { enable = true; dbBackend = "sqlite"; # Still in /var/lib/bitwarde_rs backupDir = "/var/lib/vaultwarden-backups"; # backup its persistent data config = { DOMAIN = "https://${cfg.host}"; SIGNUPS_ALLOWED = false; INVITATIONS_ALLOWED = "true"; ROCKET_PORT = 8222; EXPERIMENTAL_CLIENT_FEATURE_FLAGS = "ssh-key-vault-item,ssh-agent"; }; environmentFile = "${config.clan.core.vars.generators."vaultwarden".files."envfile".path}"; }; clan.core.vars.generators."vaultwarden" = pinpox-utils.mkEnvGenerator [ "YUBICO_CLIENT_ID" "YUBICO_SECRET_KEY" "ADMIN_TOKEN" ]; # Backup DB and persistent data (e.g. attachments) pinpox.services.restic-client.backup-paths-offsite = [ "${config.services.vaultwarden.backupDir}" "/var/lib/bitwarden_rs" ]; }; } ================================================ FILE: modules/vikunja/default.nix ================================================ { config, lib, pinpox-utils, ... }: with lib; let cfg = config.pinpox.services.vikunja; in { options.pinpox.services.vikunja = { enable = mkEnableOption "vikunja config"; host = mkOption { type = types.str; default = "todo.0cx.de"; description = "Host serving vikunja"; example = "tasks.0cx.de"; }; }; config = mkIf cfg.enable { services.caddy.virtualHosts."${cfg.host}".extraConfig = "reverse_proxy localhost:${toString config.services.vikunja.port}"; clan.core.vars.generators."vikunja" = pinpox-utils.mkEnvGenerator [ "VIKUNJA_AUTH_OPENID_PROVIDERS_DEX_CLIENTID" "VIKUNJA_AUTH_OPENID_PROVIDERS_DEX_CLIENTSECRET" "VIKUNJA_METRIC_PASSWORD" "VIKUNJA_MAILER_PASSWORD" ]; services.vikunja = { enable = true; port = 3456; environmentFiles = [ config.clan.core.vars.generators."vikunja".files."envfile".path ]; frontendScheme = "https"; frontendHostname = cfg.host; settings = { service.timezone = "Europe/Berlin"; files.basepath = "/var/lib/vikunja/files"; defaultsettings = { discoverable_by_name = true; discoverable_by_email = true; email_reminders_enabled = true; overdue_tasks_reminders_enabled = true; overdue_tasks_reminders_time = "10:00"; week_start = "1"; }; mailer = { enabled = true; host = "smtp.sendgrid.net"; username = "apikey"; frommail = "todo@0cx.de"; port = "587"; authtype = "plain"; skiptlsverify = "false"; forcessl = true; }; metrics = { enabled = true; username = "prometheus"; }; auth = { local.enabled = false; openid = { enabled = true; redirect_url = "https://todo.0cx.de/auth/openid/"; providers.dex = { authurl = "https://login.0cx.de"; name = "dex"; }; }; }; }; }; }; } ================================================ FILE: modules/virtualisation/default.nix ================================================ { config, lib, ... }: with lib; let cfg = config.pinpox.virtualisation; in { options.pinpox.virtualisation = { docker.enable = mkEnableOption "Docker virtualisation"; virtualbox.enable = mkEnableOption "VirtualBox virtualisation"; virt-manager.enable = mkEnableOption "Virt-Manager virtualisation"; }; config = mkMerge [ (mkIf cfg.docker.enable { users.users.pinpox.extraGroups = [ "docker" ]; virtualisation.docker.enable = true; }) (mkIf cfg.virt-manager.enable { boot.kernelModules = [ "kvm-amd" ]; virtualisation.libvirtd.enable = true; programs.virt-manager.enable = true; }) (mkIf cfg.virtualbox.enable { users.extraGroups.vboxusers.members = [ "pinpox" ]; virtualisation.virtualbox.host.enable = true; # virtualisation.virtualbox.host.enableKvm = true; # virtualisation.virtualbox.host.addNetworkInterface = false; # virtualisation.virtualbox.host.enableExtensionPack = true; }) ]; } ================================================ FILE: modules/wastebin/default.nix ================================================ { config, lib, pkgs, ... }: with lib; let cfg = config.pinpox.services.wastebin; in { options.pinpox.services.wastebin.enable = mkEnableOption "wastebin server"; config = mkIf cfg.enable { clan.core.vars.generators."wastebin" = { files.envfile = { }; runtimeInputs = [ pkgs.coreutils ]; script = '' echo "WASTEBIN_PASSWORD_SALT=$(tr -dc A-Za-z0-9 < /dev/urandom | head -c 80)" >> $out/envfile echo "WASTEBIN_SIGNING_KEY=$(tr -dc A-Za-z0-9 < /dev/urandom | head -c 80)" >> $out/envfile ''; }; # Create system user and group services.wastebin = { enable = true; secretFile = config.clan.core.vars.generators."wastebin".files."envfile".path; settings = { WASTEBIN_ADDRESS_PORT = "127.0.0.1:8088"; WASTEBIN_BASE_URL = "https://paste.0cx.de"; WASTEBIN_HTTP_TIMEOUT = 7; WASTEBIN_MAX_BODY_SIZE = 16384; WASTEBIN_TITLE = "wastebin"; RUST_LOG = "warning"; }; }; # Reverse proxy services.caddy.virtualHosts."paste.0cx.de".extraConfig = "reverse_proxy ${config.services.wastebin.settings.WASTEBIN_ADDRESS_PORT}"; }; } ================================================ FILE: modules/wayland/default.nix ================================================ { config, pkgs, lib, ... }: with lib; let cfg = config.pinpox.services.wayland; in { options.pinpox.services.wayland = { enable = mkEnableOption "wayland configuration"; }; config = mkIf cfg.enable { # Wayland/sway programs.sway.enable = true; # Turn on wayland support for some electron apps environment.sessionVariables = { LIBVA_DRIVER_NAME = "iHD"; NIXOS_OZONE_WL = "1"; }; # Extra portals (screensharing) xdg.portal = { enable = true; config.common.default = [ "wlr" "gtk" ]; wlr.enable = true; extraPortals = [ pkgs.xdg-desktop-portal-gtk ]; }; environment.systemPackages = with pkgs; [ xdg-desktop-portal wdisplays # Configure screen placement ]; }; } ================================================ FILE: modules/web-vm/default.nix ================================================ { lib, config, ... }: with lib; let cfg = config.pinpox.services.web-vm; in { options.pinpox.services.web-vm.enable = mkEnableOption "Web VM"; config = mkIf cfg.enable { services.caddy = { enable = true; virtualHosts."vm.0cx.de".extraConfig = '' root * /var/www/vm-test encode zstd gzip header Cross-Origin-Embedder-Policy require-corp header Cross-Origin-Opener-Policy same-origin file_server ''; }; }; } ================================================ FILE: modules/yubikey/default.nix ================================================ { config, pkgs, lib, age-plugin-picohsm, passage-secret-service, ... }: with lib; let cfg = config.pinpox.defaults.yubikey; in { options.pinpox.defaults.yubikey.enable = mkEnableOption "yubikey defaults"; config = mkIf cfg.enable { # OpenSC PIN caching (per process) # environment.etc."opensc.conf".text = '' # app default { # framework pkcs15 { # use_pin_caching = true; # pin_cache_counter = 10; # pin_cache_ignore_user_consent = true; # } # } # ''; services.pcscd.enable = true; # Allow pcscd access for SSH sessions (not just graphical) security.polkit.extraConfig = '' polkit.addRule(function(action, subject) { if (action.id == "org.debian.pcsc-lite.access_pcsc" || action.id == "org.debian.pcsc-lite.access_card") { return polkit.Result.YES; } }); ''; environment.systemPackages = [ age-plugin-picohsm.packages.${pkgs.system}.default pkgs.age pkgs.opensc pkgs.keyutils ]; environment.sessionVariables = { PICOHSM_ASKPASS = "${age-plugin-picohsm.packages.${pkgs.system}.default}/bin/picohsm-askpass"; PICOHSM_ASKPASS_BACKEND = lib.getExe pkgs.noctalia-askpass; }; security.tpm2.enable = true; security.tpm2.pkcs11.enable = true; # expose /run/current-system/sw/lib/libtpm2_pkcs11.so security.tpm2.pkcs11.package = pkgs.tpm2-pkcs11-esapi; security.tpm2.tctiEnvironment.enable = true; # TPM2TOOLS_TCTI and TPM2_PKCS11_TCTI env variables users.users.pinpox.extraGroups = [ config.security.tpm2.tssGroup ]; # tss group has access to TPM devices programs.ssh.startAgent = true; # OpenSSH 10.2+ uses comma-separated list (not colon) for -P whitelist programs.ssh.agentPKCS11Whitelist = "${config.security.tpm2.pkcs11.package}/lib/libtpm2_pkcs11.so,${pkgs.opensc}/lib/opensc-pkcs11.so"; # services.yubikey-agent.enable = false; services.udev.packages = [ pkgs.yubikey-personalization ]; systemd.user.services.passage-secret-service = { description = "passage-backed D-Bus Secret Service"; partOf = [ "graphical-session.target" ]; after = [ "graphical-session.target" ]; wantedBy = [ "graphical-session.target" ]; serviceConfig = { Type = "simple"; ExecStart = lib.getExe passage-secret-service.packages.${pkgs.system}.passage-secret-service; Restart = "on-failure"; RestartSec = 3; }; }; }; } ================================================ FILE: modules/zsh/default.nix ================================================ { config, pkgs, lib, ... }: with lib; let cfg = config.pinpox.defaults.zsh; in { options.pinpox.defaults.zsh = { enable = mkEnableOption "ZSH defaults"; }; config = mkIf cfg.enable { environment.systemPackages = with pkgs; [ zsh ]; # Needed for yubikey to work environment.shellInit = '' export ZDOTDIR=$HOME/.config/zsh ''; programs.zsh = { enable = true; shellAliases = { vim = "nvim"; }; enableCompletion = true; autosuggestions.enable = true; }; # Needed for zsh completion of system packages, e.g. systemd environment.pathsToLink = [ "/share/zsh" ]; }; } ================================================ FILE: overlays/default.nix ================================================ inputs: flake-self: pinpox-utils: let # Pass flake inputs to overlay so we can use the sources pinned in flake.lock # instead of having to keep sha256 hashes in each package for src inherit inputs; # Pass flake itself, so we can build woodpecker-pipeline and manual inherit flake-self; in self: super: { manual = super.callPackage ../packages/manual { inherit inputs; inherit pinpox-utils; inherit flake-self; }; woodpecker-pipeline = super.callPackage ../packages/woodpecker-pipeline { inherit inputs; inherit flake-self; }; # Override unfree src with flake input # ndi = super.ndi.overrideAttrs (old: { # src = inputs.ndi-linux; # unpackPhase = '' # echo y | $src; # sourceRoot="NDI SDK for Linux"; # ''; # }); # TODO remove when fixed upsteam zynaddsubfx = super.zynaddsubfx.overrideAttrs (old: { CXXFLAGS = [ # GCC 13: error: 'uint8_t' does not name a type "-include cstdint" ]; }); # To override packages from master input do: # pamixer = inputs.nixpkgs-master.legacyPackages."${super.system}".pamixer; # intel-graphics-compiler = # inputs.nixpkgs-master.legacyPackages."${super.system}".intel-graphics-compiler; # Override tpm2-pytss from master for all python versions # https://github.com/NixOS/nixpkgs/issues/417992 # python3 = super.python3.override { # packageOverrides = python-self: python-super: { # tpm2-pytss = inputs.nixpkgs-master.legacyPackages."${super.system}".python3Packages.tpm2-pytss; # }; # }; # inherit (inputs.llm-agents.packages.${super.stdenv.hostPlatform.system}) pi openspec claude-code but gitbutler ; rio = inputs.rio.packages.${super.stdenv.hostPlatform.system}.default; # TODO: Remove once merged upstream in nixpkgs groups-relay = super.callPackage ../packages/groups-relay { }; # Example package, used only for tests hello-custom = super.callPackage ../packages/hello-custom { }; # river-luatile = super.callPackage ../packages/river-luatile { }; fritzbox_exporter = super.callPackage ../packages/fritzbox_exporter { }; mqtt2prometheus = super.callPackage ../packages/mqtt2prometheus { }; # Custom packages. Will be made available on all machines and used where # needed. smartmon-script = super.callPackage ../packages/smartmon-script { }; noctalia-askpass = super.callPackage ../packages/noctalia-askpass { }; machine-report = super.callPackage ../packages/machine-report { }; # Use custom neovim in standalone flake neovim = inputs.pinpox-neovim.packages.${super.stdenv.hostPlatform.system}.pinpox-neovim; # ZSH plugins zsh-abbrev-alias = super.callPackage ../packages/zsh-abbrev-alias { inputs = inputs; }; zsh-colored-man-pages = super.callPackage ../packages/zsh-colored-man-pages { inputs = inputs; }; zsh-async = super.callPackage ../packages/zsh-async { inputs = inputs; }; } ================================================ FILE: overlays/nextcloud.patch ================================================ From 3c3e45f0ad4b4f10161197f70daf46c8888ba91e Mon Sep 17 00:00:00 2001 From: Josh Date: Fri, 23 Feb 2024 12:55:58 -0500 Subject: [PATCH] fix(Files): Change how scanner diffs for changed metadata Fixes #43408 Signed-off-by: Josh --- lib/private/Files/Cache/Scanner.php | 49 +++++++++++++++++++++++++++-- 1 file changed, 47 insertions(+), 2 deletions(-) diff --git a/lib/private/Files/Cache/Scanner.php b/lib/private/Files/Cache/Scanner.php index 1c66f3af8d2b0..4aef73b9b2522 100644 --- a/lib/private/Files/Cache/Scanner.php +++ b/lib/private/Files/Cache/Scanner.php @@ -221,8 +221,9 @@ public function scanFile($file, $reuseExisting = 0, $parentId = -1, $cacheData = } // Only update metadata that has changed - $newData = array_diff_assoc($data, $cacheData->getData()); - + // i.e. get all the values in $data that are not present in the cache already + $newData = $this->array_diff_assoc_multi($data, $cacheData->getData()); + // make it known to the caller that etag has been changed and needs propagation if (isset($newData['etag'])) { $data['etag_changed'] = true; @@ -369,6 +370,50 @@ public function scan($path, $recursive = self::SCAN_RECURSIVE, $reuse = -1, $loc return $data; } + /** + * Compares $array1 against $array2 and returns all the values in $array1 that are not in $array2 + * Note this is a one-way check - i.e. we don't care about things that are in $array2 that aren't in $array1 + * + * Supports multi-dimensional arrays + * Also checks keys/indexes + * Comparisons are strict just like array_diff_assoc + * Order of keys/values does not matter + * + * @param array $array1 + * @param array $array2 + * @return array with the differences between $array1 and $array1 + * @throws \InvalidArgumentException if $array1 isn't an actual array + * + */ + protected function array_diff_assoc_multi(array $array1, array $array2) { + + $result = []; + + foreach ($array1 as $key => $value) { + + // if $array2 doesn't have the same key, that's a result + if (!array_key_exists($key, $array2)) { + $result[$key] = $value; + continue; + } + + // if $array2's value for the same key is different, that's a result + if ($array2[$key] !== $value && !is_array($value)) { + $result[$key] = $value; + continue; + } + + if (is_array($value)) { + $nestedDiff = $this->array_diff_assoc_multi($value, $array2[$key]); + if (!empty($nestedDiff)) { + $result[$key] = $nestedDiff; + continue; + } + } + } + return $result; + } + /** * Get the children currently in the cache * ================================================ FILE: packages/fritzbox_exporter/default.nix ================================================ { lib, fetchFromGitHub, buildGoModule, pkgs, }: buildGoModule rec { pname = "fritzbox_exporter"; version = "latest"; # vendorHash = null; vendorHash = "sha256-jcHJNTdiYRQcjJr9VcABY5Ark4bmzqsJcn1iMW09Xl0="; nativeBuildInputs = with pkgs; [ pkg-config ]; # Updated 2022-01-11 src = fetchFromGitHub { owner = "sberk42"; repo = "fritzbox_exporter"; rev = "baa6961be43256af0d904642492e016a35f2a135"; sha256 = "sha256-ANK8sIHn2vx5+XJ0c6U2uQQiDBYhTfQ65RASdXPtF7w="; }; meta = with lib; { maintainers = with maintainers; [ pinpox ]; license = licenses.asl20; description = "Fritzbox exporter for prometheus"; }; } ================================================ FILE: packages/groups-relay/default.nix ================================================ { lib, buildGoModule, fetchFromGitHub, }: buildGoModule { pname = "groups-relay"; version = "0-unstable-2025-05-10"; src = fetchFromGitHub { owner = "max21dev"; repo = "groups-relay"; rev = "f7f81d4daf9b2d5fb12e04e9081cf2e307763508"; hash = "sha256-2my17kgpL+5dROB3iQN2SAe9jTPDRHPvW0QIwFun0vg="; }; vendorHash = "sha256-oZgku/7KA/V3IiSH7LnJPSk2mBy3Y3RmypuGDo1d7VA="; meta = { description = "NIP-29 group chat relay for Nostr, built on khatru and relay29"; homepage = "https://github.com/max21dev/groups-relay"; license = lib.licenses.mit; mainProgram = "groups-relay"; }; } ================================================ FILE: packages/hello-custom/default.nix ================================================ { lib, stdenv, fetchurl, }: stdenv.mkDerivation rec { pname = "hello"; version = "2.10"; src = fetchurl { url = "mirror://gnu/hello/${pname}-${version}.tar.gz"; sha256 = "0ssi1wpaf7plaswqqjwigppsg5fyh99vdlb9kzl7c9lng89ndq1i"; }; doCheck = true; meta = with lib; { description = "A program that produces a familiar, friendly greeting"; longDescription = '' GNU Hello is a program that prints "Hello, world!" when you run it. It is fully customizable. ''; homepage = "https://www.gnu.org/software/hello/manual/"; changelog = "https://git.savannah.gnu.org/cgit/hello.git/plain/NEWS?h=v${version}"; license = licenses.gpl3Plus; maintainers = [ maintainers.eelco ]; platforms = platforms.all; }; } ================================================ FILE: packages/machine-report/default.nix ================================================ { stdenv, lib, makeWrapper, procps, coreutils, gawk, gnused, gnugrep, util-linux, iproute2, net-tools, hostname, acpi, ... }: stdenv.mkDerivation { pname = "machine-report"; version = "0.1.0"; src = ./.; nativeBuildInputs = [ makeWrapper ]; installPhase = '' mkdir -p $out/bin cp machine_report.sh $out/bin/machine-report chmod +x $out/bin/machine-report wrapProgram $out/bin/machine-report \ --set LC_NUMERIC C \ --prefix PATH : ${ lib.makeBinPath [ procps coreutils gawk gnused gnugrep util-linux iproute2 net-tools hostname acpi ] } ''; } ================================================ FILE: packages/machine-report/machine_report.sh ================================================ #!/usr/bin/env bash # TR-100 Machine Report # Copyright © 2024, U.S. Graphics, LLC. BSD-3-Clause License. # Global variables MIN_NAME_LEN=5 MAX_NAME_LEN=13 MIN_DATA_LEN=20 MAX_DATA_LEN=32 BORDERS_AND_PADDING=7 # Basic configuration, change as needed report_title="PINPOX INFORMATION SYSTEMS" last_login_ip_present=0 zfs_present=0 zfs_filesystem="zroot/ROOT/os" # Utilities max_length() { local max_len=0 local len for str in "$@"; do len=${#str} if (( len > max_len )); then max_len=$len fi done if [ $max_len -lt $MAX_DATA_LEN ]; then printf '%s' "$max_len" else printf '%s' "$MAX_DATA_LEN" fi } # All data strings must go here set_current_len() { CURRENT_LEN=$(max_length \ "$report_title" \ "$os_name" \ "$os_kernel" \ "$net_hostname" \ "$net_machine_ip" \ "$net_client_ip" \ "$net_current_user" \ "$cpu_model" \ "$cpu_cores_per_socket vCPU(s) / $cpu_sockets Socket(s)" \ "$cpu_hypervisor" \ "$cpu_freq GHz" \ "$cpu_1min_bar_graph" \ "$cpu_5min_bar_graph" \ "$cpu_15min_bar_graph" \ "$zfs_used_gb/$zfs_available_gb GB [$disk_percent%]" \ "$disk_bar_graph" \ "$zfs_health" \ "$root_used_gb/$root_total_gb GB [$disk_percent%]" \ "${mem_used_gb}/${mem_total_gb} GiB [${mem_percent}%]" \ "${mem_bar_graph}" \ "$last_login_time" \ "$last_login_ip" \ "$last_login_ip" \ "$sys_uptime" \ "$nixos_gen_info" \ "$bat_info" \ ) } PRINT_HEADER() { local length=$((CURRENT_LEN+MAX_NAME_LEN+BORDERS_AND_PADDING)) local top="┌" local bottom="├" for (( i = 0; i < length - 2; i++ )); do top+="┬" bottom+="┴" done top+="┐" bottom+="┤" printf '%s\n' "$top" printf '%s\n' "$bottom" } PRINT_CENTERED_DATA() { local max_len=$((CURRENT_LEN+MAX_NAME_LEN-BORDERS_AND_PADDING)) local text="$1" local total_width=$((max_len + 12)) local text_len=${#text} local padding_left=$(( (total_width - text_len) / 2 )) local padding_right=$(( total_width - text_len - padding_left )) printf "│%${padding_left}s%s%${padding_right}s│\n" "" "$text" "" } PRINT_DIVIDER() { # either "top" or "bottom", no argument means middle divider local side="$1" case "$side" in "top") local left_symbol="├" local middle_symbol="┬" local right_symbol="┤" ;; "bottom") local left_symbol="└" local middle_symbol="┴" local right_symbol="┘" ;; *) local left_symbol="├" local middle_symbol="┼" local right_symbol="┤" esac local length=$((CURRENT_LEN+MAX_NAME_LEN+BORDERS_AND_PADDING)) local divider="$left_symbol" for (( i = 0; i < length - 3; i++ )); do divider+="─" if [ "$i" -eq 14 ]; then divider+="$middle_symbol" fi done divider+="$right_symbol" printf '%s\n' "$divider" } PRINT_DATA() { local name="$1" local data="$2" local max_data_len=$CURRENT_LEN # Pad name local name_len=${#name} if (( name_len < MIN_NAME_LEN )); then name=$(printf "%-${MIN_NAME_LEN}s" "$name") elif (( name_len > MAX_NAME_LEN )); then name=$(echo "$name" | cut -c 1-$((MAX_NAME_LEN-3)))... else name=$(printf "%-${MAX_NAME_LEN}s" "$name") fi # Truncate or pad data local data_len=${#data} if (( data_len >= MAX_DATA_LEN || data_len == MAX_DATA_LEN-1 )); then data=$(echo "$data" | cut -c 1-$((MAX_DATA_LEN-3-2)))... else data=$(printf "%-${max_data_len}s" "$data") fi printf "│ %-${MAX_NAME_LEN}s │ %s │\n" "$name" "$data" } PRINT_FOOTER() { local length=$((CURRENT_LEN+MAX_NAME_LEN+BORDERS_AND_PADDING)) local footer="└" for (( i = 0; i < length - 3; i++ )); do footer+="─" if [ "$i" -eq 14 ]; then footer+="┴" fi done footer+="┘" printf '%s\n' "$footer" } bar_graph() { local percent local num_blocks local width=$CURRENT_LEN local graph="" local used=$1 local total=$2 if (( total == 0 )); then percent=0 else percent=$(awk -v used="$used" -v total="$total" 'BEGIN { printf "%.2f", (used / total) * 100 }') fi num_blocks=$(awk -v percent="$percent" -v width="$width" 'BEGIN { printf "%d", (percent / 100) * width }') for (( i = 0; i < num_blocks; i++ )); do graph+="█" done for (( i = num_blocks; i < width; i++ )); do graph+="░" done printf "%s" "${graph}" } get_ip_addr() { # Initialize variables ipv4_address="" ipv6_address="" # Check if ifconfig command exists if command -v ifconfig &> /dev/null; then # Try to get IPv4 address using ifconfig ipv4_address=$(ifconfig | awk ' /^[a-z]/ {iface=$1} iface != "lo:" && iface !~ /^docker/ && /inet / && !found_ipv4 {found_ipv4=1; print $2}') # If IPv4 address not available, try IPv6 using ifconfig if [ -z "$ipv4_address" ]; then ipv6_address=$(ifconfig | awk ' /^[a-z]/ {iface=$1} iface != "lo:" && iface !~ /^docker/ && /inet6 / && !found_ipv6 {found_ipv6=1; print $2}') fi elif command -v ip &> /dev/null; then # Try to get IPv4 address using ip addr ipv4_address=$(ip -o -4 addr show | awk ' $2 != "lo" && $2 !~ /^docker/ {split($4, a, "/"); if (!found_ipv4++) print a[1]}') # If IPv4 address not available, try IPv6 using ip addr if [ -z "$ipv4_address" ]; then ipv6_address=$(ip -o -6 addr show | awk ' $2 != "lo" && $2 !~ /^docker/ {split($4, a, "/"); if (!found_ipv6++) print a[1]}') fi fi # If neither IPv4 nor IPv6 address is available, assign "No IP found" if [ -z "$ipv4_address" ] && [ -z "$ipv6_address" ]; then ip_address="No IP found" else # Prioritize IPv4 if available, otherwise use IPv6 ip_address="${ipv4_address:-$ipv6_address}" fi printf '%s' "$ip_address" } # Operating System Information source /etc/os-release os_name="${ID^} ${VERSION} ${VERSION_CODENAME^}" os_kernel=$({ uname; uname -r; } | tr '\n' ' ') # NixOS Generation (conditional) nixos_gen_present=0 if [ -L /nix/var/nix/profiles/system ]; then nixos_generation=$(readlink /nix/var/nix/profiles/system | sed 's/system-\([0-9]*\)-link/\1/') nixos_gen_date=$(stat -c '%y' /nix/var/nix/profiles/system | awk '{print $1, $2}' | cut -d. -f1) nixos_gen_info="Gen ${nixos_generation} / ${nixos_gen_date}" nixos_gen_present=1 fi # Battery Information (conditional) bat_present=0 if command -v acpi &> /dev/null; then bat_line=$(acpi -b 2>/dev/null | head -n 1) if [ -n "$bat_line" ] && ! echo "$bat_line" | grep -q "No support"; then bat_status=$(echo "$bat_line" | awk -F': ' '{print $2}' | cut -d',' -f1) bat_percent=$(echo "$bat_line" | grep -oP '[0-9]+(?=%)') bat_detail=$(echo "$bat_line" | awk -F', ' '{if (NF>=3) print $3}') # Compute wattage from /sys if possible bat_watts="" for bat_dir in /sys/class/power_supply/BAT*; do if [ -d "$bat_dir" ]; then if [ -f "$bat_dir/voltage_now" ] && [ -f "$bat_dir/current_now" ]; then bat_watts=$(awk -v v="$(cat "$bat_dir/voltage_now")" -v c="$(cat "$bat_dir/current_now")" \ 'BEGIN { printf "%.1f", (v * c) / 1e12 }') elif [ -f "$bat_dir/power_now" ]; then bat_watts=$(awk -v p="$(cat "$bat_dir/power_now")" 'BEGIN { printf "%.1f", p / 1e6 }') fi break fi done if [ -n "$bat_watts" ] && [ "$bat_watts" != "0.0" ]; then bat_info="${bat_percent}% ${bat_status} [${bat_watts}W]" else bat_info="${bat_percent}% ${bat_status}" fi bat_present=1 fi fi # Network Information net_current_user=$(whoami) if ! [ "$(command -v hostname)" ]; then net_hostname=$(grep -w "$(uname -n)" /etc/hosts | awk '{print $2}' | head -n 1) else net_hostname=$(hostname -f) fi if [ -z "$net_hostname" ]; then net_hostname="Not Defined"; fi net_machine_ip=$(get_ip_addr) net_client_ip=$(who am i | awk '{print $5}' | tr -d '()') if [ -z "$net_client_ip" ]; then net_client_ip="Not connected" fi net_dns_ip=($(grep '^nameserver [0-9.]' /etc/resolv.conf | awk '{print $2}')) # CPU Information cpu_model="$(lscpu | grep 'Model name' | grep -v 'BIOS' | cut -f 2 -d ':' | awk '{print $1 " " $2 " " $3 " " $4}')" cpu_hypervisor="$(lscpu | grep 'Hypervisor vendor' | cut -f 2 -d ':' | awk '{$1=$1}1')" if [ -z "$cpu_hypervisor" ]; then cpu_hypervisor="Bare Metal" fi cpu_cores="$(nproc --all)" cpu_cores_per_socket="$(lscpu | grep 'Core(s) per socket' | cut -f 2 -d ':'| awk '{$1=$1}1')" cpu_sockets="$(lscpu | grep 'Socket(s)' | cut -f 2 -d ':' | awk '{$1=$1}1')" cpu_freq="$(grep 'cpu MHz' /proc/cpuinfo | cut -f 2 -d ':' | awk 'NR==1 { printf "%.2f", $1 / 1000 }')" # Convert from M to G units load_avg_1min=$(uptime | awk -F'load average: ' '{print $2}' | cut -d ',' -f1 | tr -d ' ') load_avg_5min=$(uptime | awk -F'load average: ' '{print $2}' | cut -d ',' -f2 | tr -d ' ') load_avg_15min=$(uptime| awk -F'load average: ' '{print $2}' | cut -d ',' -f3 | tr -d ' ') # Memory Information mem_total=$(grep 'MemTotal' /proc/meminfo | awk '{print $2}') mem_available=$(grep 'MemAvailable' /proc/meminfo | awk '{print $2}') mem_used=$((mem_total - mem_available)) mem_percent=$(awk -v used="$mem_used" -v total="$mem_total" 'BEGIN { printf "%.2f", (used / total) * 100 }') mem_percent=$(printf "%.2f" "$mem_percent") mem_total_gb=$(echo "$mem_total" | awk '{ printf "%.2f", $1 / (1024 * 1024) }') # (From Ki to Gi units) mem_available_gb=$(echo "$mem_available" | awk '{ printf "%.2f", $1 / (1024 * 1024) }') # (From Ki to Gi units) Not used currently mem_used_gb=$(echo "$mem_used" | awk '{ printf "%.2f", $1 / (1024 * 1024) }') # Disk Information if [ "$(command -v zfs)" ] && [ "$(grep -q "zfs" /proc/mounts)" ]; then zfs_present=1 zfs_health=$(zpool status -x zroot | grep -q "is healthy" && echo "HEALTH O.K.") zfs_available=$(zfs get -o value -Hp available "$zfs_filesystem") zfs_used=$(zfs get -o value -Hp used "$zfs_filesystem") zfs_available_gb=$(echo "$zfs_available" | awk '{ printf "%.2f", $1 / (1024 * 1024 * 1024) }') # (To G units) zfs_used_gb=$(echo "$zfs_used" | awk '{ printf "%.2f", $1 / (1024 * 1024 * 1024) }') # (To G units) disk_percent=$(awk -v used="$zfs_used" -v available="$zfs_available" 'BEGIN { printf "%.2f", (used / available) * 100 }') else # Thanks https://github.com/AnarchistHoneybun root_partition="/" root_used=$(df -m "$root_partition" | awk 'NR==2 {print $3}') root_total=$(df -m "$root_partition" | awk 'NR==2 {print $2}') root_total_gb=$(awk -v total="$root_total" 'BEGIN { printf "%.2f", total / 1024 }') root_used_gb=$(awk -v used="$root_used" 'BEGIN { printf "%.2f", used / 1024 }') disk_percent=$(awk -v used="$root_used" -v total="$root_total" 'BEGIN { printf "%.2f", (used / total) * 100 }') fi # Last login and Uptime if command -v lastlog &> /dev/null; then last_login=$(lastlog -u "$USER") last_login_ip=$(echo "$last_login" | awk 'NR==2 {print $3}') # Check if last_login_ip is an IP address if [[ "$last_login_ip" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then last_login_ip_present=1 last_login_time=$(echo "$last_login" | awk 'NR==2 {print $6, $7, $10, $8}') else last_login_time=$(echo "$last_login" | awk 'NR==2 {print $4, $5, $8, $6}') # Check for **Never logged in** edge case if [ "$last_login_time" = "in**" ]; then last_login_time="Never logged in" fi fi else # Fallback to `last` for systems without lastlog (e.g. NixOS) last_entry=$(last -n 1 "$USER" 2>/dev/null | head -n 1) if [ -n "$last_entry" ] && ! [[ "$last_entry" =~ ^[[:space:]]*$ ]]; then last_login_ip=$(echo "$last_entry" | awk '{print $3}') if [[ "$last_login_ip" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then last_login_ip_present=1 fi last_login_time=$(echo "$last_entry" | awk '{print $4, $5, $6, $7}') else last_login_time="Never logged in" fi fi if uptime -p &> /dev/null; then sys_uptime=$(uptime -p | sed 's/up\s*//; s/\s*day\(s*\)/d/; s/\s*hour\(s*\)/h/; s/\s*minute\(s*\)/m/') else # Fallback: parse uptime output for systems without -p (e.g. coreutils uptime) sys_uptime=$(uptime | sed 's/.*up\s*//; s/,\s*[0-9]* user.*//; s/\s\+/ /g') fi # Set current length before graphs get calculated set_current_len # Create graphs cpu_1min_bar_graph=$(bar_graph "$load_avg_1min" "$cpu_cores") cpu_5min_bar_graph=$(bar_graph "$load_avg_5min" "$cpu_cores") cpu_15min_bar_graph=$(bar_graph "$load_avg_15min" "$cpu_cores") mem_bar_graph=$(bar_graph "$mem_used" "$mem_total") if [ $zfs_present -eq 1 ]; then disk_bar_graph=$(bar_graph "$zfs_used" "$zfs_available") else disk_bar_graph=$(bar_graph "$root_used" "$root_total") fi if [ $bat_present -eq 1 ]; then bat_bar_graph=$(bar_graph "$bat_percent" "100") fi # Machine Report PRINT_HEADER PRINT_CENTERED_DATA "$report_title" PRINT_CENTERED_DATA "$(echo "${net_hostname%%.*}" | tr '[:lower:]' '[:upper:]') MACHINE REPORT" PRINT_DIVIDER "top" PRINT_DATA "OS" "$os_name" PRINT_DATA "KERNEL" "$os_kernel" if [ $nixos_gen_present -eq 1 ]; then PRINT_DATA "GENERATION" "$nixos_gen_info" fi PRINT_DIVIDER PRINT_DATA "HOSTNAME" "$net_hostname" PRINT_DATA "MACHINE IP" "$net_machine_ip" PRINT_DATA "CLIENT IP" "$net_client_ip" for dns_num in "${!net_dns_ip[@]}"; do PRINT_DATA "DNS IP $(($dns_num + 1))" "${net_dns_ip[dns_num]}" done PRINT_DATA "USER" "$net_current_user" PRINT_DIVIDER PRINT_DATA "PROCESSOR" "$cpu_model" PRINT_DATA "CORES" "$cpu_cores_per_socket vCPU(s) / $cpu_sockets Socket(s)" PRINT_DATA "HYPERVISOR" "$cpu_hypervisor" PRINT_DATA "CPU FREQ" "$cpu_freq GHz" PRINT_DATA "LOAD 1m" "$cpu_1min_bar_graph" PRINT_DATA "LOAD 5m" "$cpu_5min_bar_graph" PRINT_DATA "LOAD 15m" "$cpu_15min_bar_graph" if [ $zfs_present -eq 1 ]; then PRINT_DIVIDER PRINT_DATA "VOLUME" "$zfs_used_gb/$zfs_available_gb GB [$disk_percent%]" PRINT_DATA "DISK USAGE" "$disk_bar_graph" PRINT_DATA "ZFS HEALTH" "$zfs_health" else PRINT_DIVIDER PRINT_DATA "VOLUME" "$root_used_gb/$root_total_gb GB [$disk_percent%]" PRINT_DATA "DISK USAGE" "$disk_bar_graph" fi PRINT_DIVIDER PRINT_DATA "MEMORY" "${mem_used_gb}/${mem_total_gb} GiB [${mem_percent}%]" PRINT_DATA "USAGE" "${mem_bar_graph}" if [ $bat_present -eq 1 ]; then PRINT_DIVIDER PRINT_DATA "BATTERY" "$bat_info" PRINT_DATA "USAGE" "$bat_bar_graph" fi PRINT_DIVIDER PRINT_DATA "LAST LOGIN" "$last_login_time" if [ $last_login_ip_present -eq 1 ]; then PRINT_DATA "" "$last_login_ip" fi PRINT_DATA "UPTIME" "$sys_uptime" PRINT_DIVIDER "bottom" ================================================ FILE: packages/manual/default.nix ================================================ { stdenvNoCC, pkgs, flake-self, inputs, pinpox-utils, }: stdenvNoCC.mkDerivation rec { pname = "flake-manual"; version = "latest"; src = ./.; dontConfigure = true; dontUnpack = true; buildPhase = let options-json = let isValidOpt = a: (builtins.hasAttr "_type" a) && (a._type == "option"); getOptionValues = opt: path: if builtins.typeOf opt == "set" then if isValidOpt opt then { inherit path; name = builtins.concatStringsSep "." path; example = if builtins.hasAttr "example" opt then opt.example else ""; description = if builtins.hasAttr "description" opt then opt.description else ""; default = if builtins.hasAttr "defaultText" opt then opt.defaultText else let defaultEval = builtins.tryEval (if builtins.hasAttr "default" opt then opt.default else ""); in if defaultEval.success then defaultEval.value else ""; type = opt.type.description; documentedOption = true; } else # it is a set, but has no "default", recurse builtins.mapAttrs (name: value: getOptionValues value (path ++ [ "${name}" ])) opt else { }; # it is no set in pkgs.writeTextFile { name = "options.json"; text = builtins.toJSON { options = pkgs.lib.attrsets.collect (o: o ? "documentedOption") ( pkgs.lib.attrsets.mapAttrs ( name: value: let allopts = getOptionValues (value ( { inherit (inputs) flake-self; inherit pkgs; inherit pinpox-utils; lib = pkgs.lib; config = { }; } // inputs )) [ ]; in if # Filter out everything that has no ".options.pinpox" builtins.hasAttr "options" allopts then if builtins.hasAttr "pinpox" allopts.options then allopts.options.pinpox else null else null ) flake-self.nixosModules ); }; }; in '' cat ${options-json} | ${pkgs.mustache-go}/bin/mustache --allow-missing-variables=false ${src}/template.html > index.html ''; installPhase = '' mkdir -p "$out" cp index.html "$out" ''; meta = { description = "Manual for this flake as package"; homepage = "https://github.com/pinpox/nixos"; }; } ================================================ FILE: packages/manual/template.html ================================================ Module Options

Module options

Options provided by modules in github.com/pinpox/nixos

{{#options}}

# {{name}}

{{type}}

{{description}}

Default: {{default}}

Example: {{example}}

{{/options}}
================================================ FILE: packages/mqtt2prometheus/default.nix ================================================ { lib, fetchFromGitHub, buildGoModule, pkgs, }: # https://github.com/hikhvar/mqtt2prometheus buildGoModule rec { pname = "mqtt2prometheus"; version = "latest"; # vendorHash = null; vendorHash = "sha256-5DIU1NUEVI7Fz6UHhC6trva9qd47DwdFNw1OxY6M37s="; nativeBuildInputs = with pkgs; [ pkg-config ]; # Updated 2022-01-11 src = fetchFromGitHub { owner = "hikhvar"; repo = "mqtt2prometheus"; rev = "v0.1.6"; sha256 = "sha256-55WAuu6n2h0IPIjt8iTJzNSF1Fe7roxiIS8MUXmu5Tc="; }; meta = with lib; { maintainers = with maintainers; [ pinpox ]; license = licenses.mit; description = "TODO"; }; } ================================================ FILE: packages/noctalia-askpass/default.nix ================================================ { writeShellApplication, noctalia-shell, procps, coreutils, }: writeShellApplication { name = "noctalia-askpass"; runtimeInputs = [ noctalia-shell procps coreutils ]; text = '' # noctalia-askpass — askpass backend that uses noctalia to show # a password/PIN entry dialog. Implements the standard askpass # contract: prompt as $1, password printed to stdout, exit 1 on cancel. # # Usage: # SSH_ASKPASS=/path/to/noctalia-askpass # SUDO_ASKPASS=/path/to/noctalia-askpass # PICOHSM_ASKPASS_BACKEND=/path/to/noctalia-askpass PROMPT="''${1:-Enter password}" # Figure out what program is requesting the password by walking the process tree CALLER="" if [ -n "''${PPID:-}" ]; then PID="$PPID" for _ in 1 2 3 4 5; do if [ "$PID" -le 1 ] 2>/dev/null; then break; fi CMD=$(ps -o comm= "$PID" 2>/dev/null || true) if [ -n "$CMD" ] && [ "$CMD" != "bash" ] && [ "$CMD" != "sh" ] && [ "$CMD" != "picohsm-askpass" ] && [ "$CMD" != "noctalia-askpass" ]; then if [ -n "$CALLER" ]; then CALLER="$CMD → $CALLER" else CALLER="$CMD" fi fi PID=$(ps -o ppid= "$PID" 2>/dev/null | tr -d ' ' || true) if [ -z "$PID" ]; then break; fi done fi # Create a named pipe for the response FIFO=$(mktemp -u /tmp/noctalia-askpass-XXXXXX) mkfifo -m 600 "$FIFO" trap 'rm -f "$FIFO"' EXIT # Ask noctalia to show the password dialog noctalia-shell ipc call plugin:noctalia-askpass prompt "$PROMPT" "$CALLER" "$FIFO" & # Block until the plugin writes the password (or empty on cancel) PASSWORD=$(cat "$FIFO") if [ -z "$PASSWORD" ]; then exit 1 fi printf '%s' "$PASSWORD" ''; } ================================================ FILE: packages/raspi-image ================================================ # nix build '.#base-image' raspi-image = let system = "aarch64-linux"; in import "${nixpkgs}/nixos/lib/make-disk-image.nix" { pkgs = nixpkgs.legacyPackages."${system}"; lib = nixpkgs.lib; config = (nixpkgs.lib.nixosSystem { inherit system; modules = [ ./images/raspi.nix ]; }).config; format = "qcow2"; diskSize = 4096; name = "raspi-image"; }; ================================================ FILE: packages/river-luatile/default.nix ================================================ { lib, fetchFromGitHub, openssl, luajit, pkg-config, rustPlatform, }: rustPlatform.buildRustPackage rec { pname = "river-luatile"; version = "0.1.0"; src = fetchFromGitHub { owner = "MaxVerevkin"; repo = pname; fetchSubmodules = true; rev = "v${version}"; sha256 = "sha256-A8vx8jN4XUUI970ZsWLKBCd5lO9p3w63b9EiGwk/rCU="; }; cargoSha256 = "sha256-udfsd1iONlDSQ/7mzzRNNhoJHmXJsxWdhqeKK/onx+4="; buildInputs = [ luajit ]; nativeBuildInputs = [ pkg-config ]; PKG_CONFIG_PATH = "${openssl.dev}/lib/pkgconfig"; meta = with lib; { homepage = "https://github.com/MaxVerevkin/river-luatile"; description = "Write your own river layout generator in lua"; license = licenses.gpl3; platforms = platforms.linux; maintainers = with maintainers; [ pinpox ]; }; } ================================================ FILE: packages/smartmon-script/default.nix ================================================ { stdenv, smartmontools, python3, ... }: stdenv.mkDerivation { name = "smartmon-script"; buildInputs = [ python3 smartmontools ]; unpackPhase = "true"; installPhase = '' mkdir -p $out/bin cp ${./smartmon.py} $out/bin/smartmon-script chmod +x $out/bin/smartmon-script ''; } ================================================ FILE: packages/smartmon-script/smartmon.py ================================================ #!/usr/bin/env python3 import argparse import collections import csv import datetime import decimal import re import shlex import subprocess import sys device_info_re = re.compile(r'^(?P[^:]+?)(?:(?:\sis|):)\s*(?P.*)$') ata_error_count_re = re.compile( r'^Error (\d+) \[\d+\] occurred', re.MULTILINE) self_test_re = re.compile(r'^SMART.*(PASSED|OK)$', re.MULTILINE) device_info_map = { 'Vendor': 'vendor', 'Product': 'product', 'Revision': 'revision', 'Logical Unit id': 'lun_id', 'Model Family': 'model_family', 'Device Model': 'device_model', 'Serial Number': 'serial_number', 'Firmware Version': 'firmware_version', } smart_attributes_whitelist = { 'airflow_temperature_cel', 'command_timeout', 'current_pending_sector', 'end_to_end_error', 'erase_fail_count_total', 'g_sense_error_rate', 'hardware_ecc_recovered', 'host_reads_mib', 'host_reads_32mib', 'host_writes_mib', 'host_writes_32mib', 'load_cycle_count', 'media_wearout_indicator', 'wear_leveling_count', 'nand_writes_1gib', 'offline_uncorrectable', 'power_cycle_count', 'power_on_hours', 'program_fail_count', 'raw_read_error_rate', 'reallocated_event_count', 'reallocated_sector_ct', 'reported_uncorrect', 'sata_downshift_count', 'seek_error_rate', 'spin_retry_count', 'spin_up_time', 'start_stop_count', 'temperature_case', 'temperature_celsius', 'temperature_internal', 'total_lbas_read', 'total_lbas_written', 'udma_crc_error_count', 'unsafe_shutdown_count', 'workld_host_reads_perc', 'workld_media_wear_indic', 'workload_minutes', } Metric = collections.namedtuple('Metric', 'name labels value') SmartAttribute = collections.namedtuple('SmartAttribute', [ 'id', 'name', 'flag', 'value', 'worst', 'threshold', 'type', 'updated', 'when_failed', 'raw_value', ]) class Device(collections.namedtuple('DeviceBase', 'path opts')): """Representation of a device as found by smartctl --scan output.""" @property def type(self): return self.opts.type @property def base_labels(self): return {'device': self.path, 'disk': self.type.partition('+')[2] or '0'} def smartctl_select(self): return ['--device', self.type, self.path] def metric_key(metric, prefix=''): return '{prefix}{metric.name}'.format(prefix=prefix, metric=metric) def metric_format(metric, prefix=''): key = metric_key(metric, prefix) labels = ','.join( '{k}="{v}"'.format(k=k, v=v.replace('"', '\\"')) for k, v in metric.labels.items()) value = decimal.Decimal(metric.value) return '{key}{{{labels}}} {value}'.format( key=key, labels=labels, value=value) def metric_print_meta(metric, prefix=''): key = metric_key(metric, prefix) print('# HELP {key} SMART metric {metric.name}'.format( key=key, metric=metric)) print('# TYPE {key} gauge'.format(key=key)) def metric_print(metric, prefix=''): print(metric_format(metric, prefix)) def smart_ctl(*args, check=True): """Wrapper around invoking the smartctl binary. Returns: (str) Data piped to stdout by the smartctl subprocess. """ return subprocess.run( ['smartctl', *args], stdout=subprocess.PIPE, check=check ).stdout.decode('utf-8') def smart_ctl_version(): return smart_ctl('-V').split('\n')[0].split()[1] def find_devices(): """Find SMART devices. Yields: (Device) Single device found by smartctl. """ parser = argparse.ArgumentParser() parser.add_argument('-d', '--device', dest='type') devices = smart_ctl('--scan-open') for device in devices.split('\n'): device = device.strip() if not device: continue tokens = shlex.split(device, comments=True) if not tokens: continue yield Device(tokens[0], parser.parse_args(tokens[1:])) def device_is_active(device): """Returns whenever the given device is currently active or not. Args: device: (Device) Device in question. Returns: (bool) True if the device is active and False otherwise. """ try: smart_ctl('--nocheck', 'standby', *device.smartctl_select()) except subprocess.CalledProcessError: return False return True def device_info(device): """Query device for basic model information. Args: device: (Device) Device in question. Returns: (generator): Generator yielding: key (str): Key describing the value. value (str): Actual value. """ info_lines = smart_ctl( '--info', *device.smartctl_select() ).strip().split('\n')[3:] matches = (device_info_re.match(line) for line in info_lines) return (m.groups() for m in matches if m is not None) def device_smart_capabilities(device): """Returns SMART capabilities of the given device. Args: device: (Device) Device in question. Returns: (tuple): tuple containing: (bool): True whenever SMART is available, False otherwise. (bool): True whenever SMART is enabled, False otherwise. """ groups = device_info(device) state = { g[1].split(' ', 1)[0] for g in groups if g[0] == 'SMART support'} smart_available = 'Available' in state smart_enabled = 'Enabled' in state return smart_available, smart_enabled def collect_device_info(device): """Collect basic device information. Args: device: (Device) Device in question. Yields: (Metric) metrics describing general device information. """ values = dict(device_info(device)) yield Metric('device_info', { **device.base_labels, **{v: values[k] for k, v in device_info_map.items() if k in values} }, True) def collect_device_health_self_assessment(device): """Collect metric about the device health self assessment. Args: device: (Device) Device in question. Yields: (Metric) Device health self assessment. """ out = smart_ctl('--health', *device.smartctl_select(), check=False) self_assessment_passed = bool(self_test_re.search(out)) yield Metric( 'device_smart_healthy', device.base_labels, self_assessment_passed) def collect_ata_metrics(device): # Fetch SMART attributes for the given device. attributes = smart_ctl( '--attributes', *device.smartctl_select() ) # replace multiple occurrences of whitespace with a single whitespace # so that the CSV Parser recognizes individual columns properly. attributes = re.sub(r'[\t\x20]+', ' ', attributes) # Turn smartctl output into a list of lines and skip to the table of # SMART attributes. attribute_lines = attributes.strip().split('\n')[7:] # Some attributes have multiple IDs but have the same name. Don't # yield attributes that already have been reported before. seen = set() reader = csv.DictReader( (line.strip() for line in attribute_lines), fieldnames=SmartAttribute._fields[:-1], restkey=SmartAttribute._fields[-1], delimiter=' ') for entry in reader: # We're only interested in the SMART attributes that are # whitelisted here. entry['name'] = entry['name'].lower() if entry['name'] not in smart_attributes_whitelist: continue # Ensure that only the numeric parts are fetched from the raw_value. # Attributes such as 194 Temperature_Celsius reported by my SSD # are in the format of "36 (Min/Max 24/40)" which can't be expressed # properly as a prometheus metric. m = re.match(r'^(\d+)', ' '.join(entry['raw_value'])) if not m: continue entry['raw_value'] = m.group(1) # Some device models report "---" in the threshold value where most # devices would report "000". We do the substitution here because # downstream code expects values to be convertable to integer. if entry['threshold'] == '---': entry['threshold'] = '0' if entry['name'] in smart_attributes_whitelist and entry['name'] not in seen: labels = { 'name': entry['name'], **device.base_labels, } for col in 'value', 'worst', 'threshold', 'raw_value': yield Metric( 'attr_{col}'.format(col=col), labels, entry[col]) seen.add(entry['name']) def collect_ata_error_count(device): """Inspect the device error log and report the amount of entries. Args: device: (Device) Device in question. Yields: (Metric) Device error count. """ error_log = smart_ctl( '-l', 'xerror,1', *device.smartctl_select(), check=False) m = ata_error_count_re.search(error_log) error_count = m.group(1) if m is not None else 0 yield Metric('device_errors', device.base_labels, error_count) def collect_disks_smart_metrics(wakeup_disks): now = int(datetime.datetime.utcnow().timestamp()) for device in find_devices(): yield Metric('smartctl_run', device.base_labels, now) is_active = device_is_active(device) yield Metric('device_active', device.base_labels, is_active) # Skip further metrics collection to prevent the disk from # spinning up. if not is_active and not wakeup_disks: continue yield from collect_device_info(device) smart_available, smart_enabled = device_smart_capabilities(device) yield Metric( 'device_smart_available', device.base_labels, smart_available) yield Metric( 'device_smart_enabled', device.base_labels, smart_enabled) # Skip further metrics collection here if SMART is disabled # on the device. Further smartctl invocations would fail # anyways. if not smart_available: continue yield from collect_device_health_self_assessment(device) if device.type.startswith('sat'): yield from collect_ata_metrics(device) yield from collect_ata_error_count(device) def main(): parser = argparse.ArgumentParser() parser.add_argument('-s', '--wakeup-disks', dest='wakeup_disks', action='store_true') args = parser.parse_args(sys.argv[1:]) version_metric = Metric('smartctl_version', { 'version': smart_ctl_version() }, True) metric_print_meta(version_metric, 'smartmon_') metric_print(version_metric, 'smartmon_') metrics = list(collect_disks_smart_metrics(args.wakeup_disks)) metrics.sort(key=lambda i: i.name) previous_name = None for m in metrics: if m.name != previous_name: metric_print_meta(m, 'smartmon_') previous_name = m.name metric_print(m, 'smartmon_') if __name__ == '__main__': main() ================================================ FILE: packages/woodpecker-pipeline/default.nix ================================================ # nix run .\#woodpecker-pipeline { pkgs, lib, flake-self, ... }: with pkgs; let supportedSystems = [ # "aarch64-linux" "x86_64-linux" ]; forAllSystems = lib.genAttrs supportedSystems; pipelineFor = forAllSystems ( system: writeText "pipeline" ( builtins.toJSON { configs = let # Map platform names between woodpecker and nix woodpecker-platforms = { "aarch64-linux" = "linux/arm64"; "x86_64-linux" = "linux/amd64"; }; nixFlakeShowStep = { name = "Nix flake show"; image = "bash"; commands = [ "nix flake show" ]; }; atticSetupStep = { name = "Setup Attic"; image = "bash"; commands = [ "attic login lounge-rocks https://cache.lounge.rocks $ATTIC_KEY --set-default" ]; environment = { ATTIC_KEY.from_secret = "attic_key"; }; }; buildAndCacheStep = { name = "Build all machines and push to cache"; image = "bash"; commands = [ ''nix-fast-build --no-nom --skip-cached --attic-cache lounge-rocks:nix-cache --flake ".#ciBuilds.${system}"'' ]; }; in pkgs.lib.lists.flatten [ (map (arch: { name = "Hosts with arch: ${arch}"; data = ( builtins.toJSON { labels = { backend = "local"; platform = woodpecker-platforms."${arch}"; }; when = [ { event = "manual"; } { event = "push"; branch = "main"; } ]; steps = pkgs.lib.lists.flatten ( [ nixFlakeShowStep ] ++ [ atticSetupStep ] ++ [ buildAndCacheStep ] ++ (map ( host: # Skip hosts with CISkip set or wrong architecture if flake-self.nixosConfigurations.${host}.config.pinpox.defaults.CISkip || (flake-self.nixosConfigurations.${host}.pkgs.stdenv.hostPlatform.system != arch) then [ ] else [ { name = "Build ${host}"; image = "bash"; failure = "ignore"; commands = [ "nix build --print-out-paths '.#ciBuilds.${system}.${host}' -o 'result-${host}'" ]; } { name = "Show ${host} info"; image = "bash"; failure = "ignore"; commands = [ "nix path-info --closure-size -h $(readlink -f 'result-${host}')" ]; } ] ) (builtins.attrNames flake-self.nixosConfigurations)) ); } ); }) [ "${system}" ] ) ]; } ) ); in pkgs.writeShellScriptBin "woodpecker-pipeline" '' # make sure .woodpecker folder exists mkdir -p .woodpecker # empty content of .woodpecker folder rm -rf .woodpecker/* # copy pipelines to .woodpecker folder ${lib.concatMapStringsSep "\n" (system: let name = builtins.replaceStrings [ "_" ] [ "-" ] (builtins.head (lib.splitString "-" system)); arch = builtins.elemAt (lib.splitString "-" system) 1; in "cat ${pipelineFor.${system}} | ${pkgs.jq}/bin/jq '.configs[].data' -r | ${pkgs.jq}/bin/jq > .woodpecker/${name}-${arch}.yaml" ) supportedSystems} '' ================================================ FILE: packages/zsh-abbrev-alias/default.nix ================================================ { stdenvNoCC, lib, fetchFromGitHub, inputs, }: stdenvNoCC.mkDerivation rec { pname = "zsh-abbrev-alias"; version = "latest"; src = inputs.zsh-abbrev-alias; dontConfigure = true; dontBuild = true; installPhase = '' plugindir="$out/share/zsh-abbrev-alias" mkdir -p "$plugindir" cp -r * "$plugindir"/ ''; meta = with lib; { description = "ZSH plugin with functionality similar to Vim's abbreviation expansion."; homepage = "https://github.com/momo-lab/zsh-abbrev-alias"; license = licenses.mit; platforms = platforms.unix; }; } ================================================ FILE: packages/zsh-async/default.nix ================================================ { stdenvNoCC, lib, fetchFromGitHub, inputs, }: stdenvNoCC.mkDerivation rec { pname = "zsh-async"; version = "latest"; src = inputs.zsh-async; dontConfigure = true; dontBuild = true; installPhase = '' plugindir="$out/share/zsh-async" mkdir -p "$plugindir" cp -r * "$plugindir"/ ''; meta = with lib; { description = "Because your terminal should be able to perform tasks asynchronously without external tools!"; homepage = "https://github.com/mafredri/zsh-async"; license = licenses.mit; platforms = platforms.unix; }; } ================================================ FILE: packages/zsh-colored-man-pages/default.nix ================================================ { stdenvNoCC, lib, fetchFromGitHub, inputs, }: stdenvNoCC.mkDerivation rec { pname = "zsh-colored-man-pages"; version = "latest"; src = inputs.zsh-colored-man-pages; dontConfigure = true; dontBuild = true; installPhase = '' plugindir="$out/share/zsh-colored-man-pages" mkdir -p "$plugindir" cp -r * "$plugindir"/ ''; meta = with lib; { description = "ZSH plugin that colorifies man page"; homepage = "https://github.com/ael-code/zsh-colored-man-pages"; license = licenses.gpl3; platforms = platforms.unix; }; } ================================================ FILE: templates/default/flake.nix ================================================ { description = "A simple Go package"; # Nixpkgs / NixOS version to use. inputs.nixpkgs.url = "nixpkgs/nixos-unstable"; outputs = { self, nixpkgs }: let # to work with older version of flakes lastModifiedDate = self.lastModifiedDate or self.lastModified or "19700101"; # Generate a user-friendly version number. version = builtins.substring 0 8 lastModifiedDate; # Helper function to generate an attrset '{ x86_64-linux = f "x86_64-linux"; ... }'. forAllSystems = nixpkgs.lib.genAttrs nixpkgs.lib.systems.flakeExposed; # Nixpkgs instantiated for supported system types. nixpkgsFor = forAllSystems (system: import nixpkgs { inherit system; }); in { # Provide some binary packages for selected system types. packages = forAllSystems ( system: let pkgs = nixpkgsFor.${system}; in { default = pkgs.buildGoModule { pname = "go-hello"; inherit version; # In 'nix develop', we don't need a copy of the source tree # in the Nix store. src = ./.; # This hash locks the dependencies of this package. It is # necessary because of how Go requires network access to resolve # VCS. See https://www.tweag.io/blog/2021-03-04-gomod2nix/ for # details. Normally one can build with a fake hash and rely on native Go # mechanisms to tell you what the hash should be or determine what # it should be "out-of-band" with other tooling (eg. gomod2nix). # To begin with it is recommended to set this, but one must # remember to bump this hash when your dependencies change. # vendorHash = pkgs.lib.fakeHash; vendorHash = "sha256-pQpattmS9VmO3ZIQUFn66az8GSmB4IvYhTTCFn6SUmo="; }; } ); # Add dependencies that are only needed for development devShells = forAllSystems ( system: let pkgs = nixpkgsFor.${system}; in { default = pkgs.mkShell { buildInputs = with pkgs; [ go gopls gotools go-tools ]; }; } ); }; } ================================================ FILE: users/pinpox-wraps/chromium/default.nix ================================================ { wlib, lib }: { apply = { pkgs, extensions ? [ ], profileName ? "wrapped", ... }: let # Normalize extensions to ensure updateUrl is present normalizedExtensions = map ( ext: ext // { updateUrl = ext.updateUrl or "https://clients2.google.com/service/update2/crx"; } ) extensions; # Create extension JSON files extensionFiles = map (ext: { name = "External Extensions/${ext.id}.json"; path = pkgs.writeText "${ext.id}.json" (builtins.toJSON { external_update_url = ext.updateUrl; }); }) normalizedExtensions; # Create config directory template with extension settings extensionsDir = pkgs.linkFarm "chromium-extensions" extensionFiles; preHookScript = '' PROFILE_DIR="$HOME/.config/chromium-${profileName}" EXT_DIR="$PROFILE_DIR/External Extensions" # Create profile directory mkdir -p "$EXT_DIR" # Symlink extension configs from Nix store ${lib.concatMapStringsSep "\n" (ext: '' ln -sf "${extensionsDir}/External Extensions/${ext.id}.json" "$EXT_DIR/${ext.id}.json" '') normalizedExtensions} ''; wrappedChromium = wlib.wrapPackage { inherit pkgs; package = pkgs.chromium; binName = "chromium-wrapped"; preHook = preHookScript; flagSeparator = "="; flags."--user-data-dir" = "$HOME/.config/chromium-${profileName}"; }; in { wrapper = wrappedChromium; }; } ================================================ FILE: users/pinpox-wraps/ffmpeg/default.nix ================================================ { wlib, lib }: wlib.wrapModule ( { config, # wlib, ... }: { options = { profile = lib.mkOption { type = lib.types.enum [ "fast" "quality" ]; default = "fast"; description = "Encoding profile to use"; }; outputDir = lib.mkOption { type = lib.types.str; default = "./output"; description = "Directory for output files"; }; }; config.package = config.pkgs.ffmpeg; config.flags = { "-preset" = if config.profile == "fast" then "veryfast" else "slow"; }; config.env = { FFMPEG_OUTPUT_DIR = config.outputDir; }; } ) ================================================ FILE: users/pinpox.nix ================================================ { pkgs, pinpox-keys, wrappers, ... }: let # Extend wrappers with custom modules by importing all subdirectories from pinpox-wraps myWrappers = wrappers // { wrapperModules = wrappers.wrapperModules // (builtins.listToAttrs ( map (name: { inherit name; value = import (./pinpox-wraps + "/${name}") { wlib = wrappers.lib; lib = pkgs.lib; }; }) (builtins.attrNames (builtins.readDir ./pinpox-wraps)) )); }; # Instantiate the wrapped ffmpeg with custom options # ffmpeg-wrapped = # (myWrappers.wrapperModules.ffmpeg.apply { # inherit pkgs; # profile = "quality"; # or "fast" # outputDir = "/home/pinpox/videos"; # customize as needed # }).wrapper; # Example using a built-in wrapper module # mpv-wrapped = # (myWrappers.wrapperModules.mpv.apply { # inherit pkgs; # scripts = [ pkgs.mpvScripts.mpris ]; # }).wrapper; # Chromium with extensions configured chromium-wrapped = (myWrappers.wrapperModules.chromium.apply { inherit pkgs; extensions = [ # { id = "clngdbkpkpeebahjckkjfobafhncgmne"; } # Stylus - adds a paintbrush icon to toolbar { id = "nngceckbapebfimnlniiiahkandclblb"; } # Bitwarden { id = "cjpalhdlnbpafiamejdnhcphjbkeiagm"; } # Ublock Origin { id = "gcbommkclmclpchllfjekcdonpmejbdp"; } # HTTPS everywhere { id = "mmpokgfcmbkfdeibafoafkiijdbfblfg"; } # Merge windows { id = "agldajbhchobfgjcmmigehfdcjbmipne"; } # Blank Dark New Tab ]; }).wrapper; in { # Define a user account. Don't forget to set a password with 'passwd'. users = { # For Virtualbox extraGroups.vboxusers.members = [ "pinpox" ]; # Shell is set to zsh for all users as default. defaultUserShell = pkgs.zsh; users.pinpox = { packages = [ # ffmpeg-wrapped # mpv-wrapped # chromium-wrapped ]; isNormalUser = true; home = "/home/pinpox"; description = "Pablo Ovelleiro Corral"; extraGroups = [ "plugdev" "docker" "wheel" "networkmanager" "audio" "libvirtd" "tty" "dialout" "video" "storage-users" ]; shell = pkgs.zsh; # Public ssh-keys that are authorized for the user. Fetched from github openssh.authorizedKeys.keyFiles = [ pinpox-keys ]; }; }; # Allow to run nix nix.settings.allowed-users = [ "pinpox" ]; } ================================================ FILE: users/root.nix ================================================ { pinpox-keys, ... }: { users.users.root.openssh.authorizedKeys.keyFiles = [ pinpox-keys ]; # Allow to run nix nix.settings.allowed-users = [ "root" ]; } ================================================ FILE: utils/default.nix ================================================ { pkgs, ... }: { mkEnvGenerator = envs: rec { files.envfile = { }; runtimeInputs = [ pkgs.coreutils ]; prompts = pkgs.lib.genAttrs envs (name: { persist = false; }); # Invalidate on env change validation.script = script; script = '' mkdir -p $out cat <> $out/envfile ${builtins.concatStringsSep "\n" (map (e: "${e}='$(cat $prompts/${e})'") envs)} EOT ''; }; renderMustache = name: template: data: # Render handlebars `template` called `name` by converting `data` to JSON pkgs.stdenv.mkDerivation { name = "${name}"; # Disable phases which are not needed. In particular the unpackPhase will # fail, if no src attribute is set nativeBuildInpts = [ pkgs.mustache-go ]; # Pass Json as file to avoid escaping passAsFile = [ "jsonData" ]; jsonData = builtins.toJSON data; phases = [ "buildPhase" "installPhase" ]; buildPhase = '' ${pkgs.mustache-go}/bin/mustache $jsonDataPath ${template} > rendered_file ''; installPhase = '' cp rendered_file $out ''; }; } ================================================ FILE: vars/per-machine/birne/data-mesher-node-identity/identity.pub/value ================================================ -----BEGIN PUBLIC KEY----- MCowBQYDK2VwAyEAHZgywFZxESjcz/U2cMGHCMOUEYtehXYTzPFRLeUE4BI= -----END PUBLIC KEY----- ================================================ FILE: vars/per-machine/birne/data-mesher-node-identity/peer.id/value ================================================ 12D3KooWBotbaPAYLi7r3Ew57SYVcdKYdbhWgjbVSmjJADvB8Xso ================================================ FILE: vars/per-machine/birne/dm-pull-deploy-status-key/signing.pub/value ================================================ -----BEGIN PUBLIC KEY----- MCowBQYDK2VwAyEArIvSEhBWHj5nR6MrJBZ93vKckmQb+UFR4kurJnzkBac= -----END PUBLIC KEY----- ================================================ FILE: vars/per-machine/birne/minio/.validation-hash ================================================ 254338cffe84c5c7b887f86fe600d1af1c074de339a69e3fba9ea94154f6c099 ================================================ FILE: vars/per-machine/birne/restic-server/.validation-hash ================================================ b40bd3130574d1d8e07160248cb6f48dcde7cf6aa948fda48bbedf8d0fcb6516 ================================================ FILE: vars/per-machine/birne/state-version/version/value ================================================ 20.03 ================================================ FILE: vars/per-machine/birne/wireguard/publickey/value ================================================ jvUOwDHp6tWMhfoRbM+0BkPqQVIPHqR4R7KU11i38Bs= ================================================ FILE: vars/per-machine/birne/wireguard-wg-clan/ipv4/value ================================================ 192.168.8.4 ================================================ FILE: vars/per-machine/birne/wireguard-wg-clan/publickey/value ================================================ fjn10ctGFiw8jP/nm9tez5IyOkYsQkRatuiz1utqMwk= ================================================ FILE: vars/per-machine/birne/wireguard-wg-clan-ip/ipv4/value ================================================ 192.168.8.4 ================================================ FILE: vars/per-machine/birne/yggdrasil/address/value ================================================ 201:f0e:8ac:ac78:64c7:c317:de6e:7392 ================================================ FILE: vars/per-machine/birne/yggdrasil/publicKey/value ================================================ -----BEGIN PUBLIC KEY----- MCowBQYDK2VwAyEAfDx91NTh5s4POghkYxtocgA9KCFz/yIHkPuxJGRoCPQ= -----END PUBLIC KEY----- ================================================ FILE: vars/per-machine/clementine/data-mesher-node-identity/identity.pub/value ================================================ -----BEGIN PUBLIC KEY----- MCowBQYDK2VwAyEATmflTMXHndP850khQlJqYikVOuwkPiTnP0egsBnHn/0= -----END PUBLIC KEY----- ================================================ FILE: vars/per-machine/clementine/data-mesher-node-identity/peer.id/value ================================================ 12D3KooWF6RrYtCUzuneQ1B2uMP9gw9afw691HgQ3GF8GkQDDazp ================================================ FILE: vars/per-machine/clementine/dm-pull-deploy-status-key/signing.pub/value ================================================ -----BEGIN PUBLIC KEY----- MCowBQYDK2VwAyEA3gdeUUAQUeGTwo6MRnDjObS3qrtqJxsiBmLd6N96g+M= -----END PUBLIC KEY----- ================================================ FILE: vars/per-machine/clementine/punchcard/.validation-hash ================================================ ae90f978bdc90b84f055e02edb2f029ebd880c0fc1d831cc8f5cd1fffbcee743 ================================================ FILE: vars/per-machine/clementine/punchcard2/.validation-hash ================================================ ae90f978bdc90b84f055e02edb2f029ebd880c0fc1d831cc8f5cd1fffbcee743 ================================================ FILE: vars/per-machine/clementine/state-version/version/value ================================================ 26.05 ================================================ FILE: vars/per-machine/clementine/trippy-track/.validation-hash ================================================ dc6b3b5bd2268b79a49157f951774ca8f86d44843c09727c334162a76eb089b8 ================================================ FILE: vars/per-machine/clementine/twitch-first/.validation-hash ================================================ 8cf5f6d31bdd4e28f9bbea5da1eede12fddb6caf180b2368bd9eaed9565b47d7 ================================================ FILE: vars/per-machine/clementine/wireguard-wg-clan/publickey/value ================================================ wE8QiS8iYDNezgDcsIcx4vvB9nc7ioU/EOUjJU09JEM= ================================================ FILE: vars/per-machine/clementine/wireguard-wg-clan-ip/ipv4/value ================================================ 10.100.0.3 ================================================ FILE: vars/per-machine/clementine/wireguard-wg-tunnel/publickey/value ================================================ XFe2p24MB+PspGaTXIRNci4qdUjcSIQu8UF4o4TexxQ= ================================================ FILE: vars/per-machine/clementine/wireguard-wg-tunnel-ip/ipv4/value ================================================ 10.100.0.1 ================================================ FILE: vars/per-machine/clementine/yggdrasil/address/value ================================================ 201:6d63:e4ff:d74e:7ee1:db69:5962:ac51 ================================================ FILE: vars/per-machine/clementine/yggdrasil/publicKey/value ================================================ 64a706c00a2c60478925a9a754eba88472ed4ccd911cb6d888e4509afabdc928 ================================================ FILE: vars/per-machine/clementine/zerotier/zerotier-ip/value ================================================ fd0c:cb9f:98da:6865:9c99:930c:cb9f:98da ================================================ FILE: vars/per-machine/clementine/zerotier/zerotier-network-id/value ================================================ 0ccb9f98da68659c ================================================ FILE: vars/per-machine/fichte/data-mesher-node-identity/identity.pub/value ================================================ -----BEGIN PUBLIC KEY----- MCowBQYDK2VwAyEAbu8AdNl8NGR6T5ynplprqQgc/6i/IxrYmUq1hjoCVK8= -----END PUBLIC KEY----- ================================================ FILE: vars/per-machine/fichte/data-mesher-node-identity/peer.id/value ================================================ 12D3KooWHHQPa45WXxnzDHNxecQKNf8wHWgxtGZDC9RFYeqkH74z ================================================ FILE: vars/per-machine/fichte/dm-pull-deploy-status-key/signing.pub/value ================================================ -----BEGIN PUBLIC KEY----- MCowBQYDK2VwAyEAAYCUUUjHByMhB6oByo7EfpzJEAPq8v425hQXTBECWgs= -----END PUBLIC KEY----- ================================================ FILE: vars/per-machine/fichte/state-version/version/value ================================================ 25.11 ================================================ FILE: vars/per-machine/fichte/yggdrasil/address/value ================================================ 200:a68a:bcfc:f1ca:9c55:aac1:81ea:de25 ================================================ FILE: vars/per-machine/fichte/yggdrasil/publicKey/value ================================================ -----BEGIN PUBLIC KEY----- MCowBQYDK2VwAyEArLqhgYcasdUqnz8KkO0NLnuTHtMgHgoOFP06GzEXgE0= -----END PUBLIC KEY----- ================================================ FILE: vars/per-machine/kartoffel/data-mesher-node-identity/identity.pub/value ================================================ -----BEGIN PUBLIC KEY----- MCowBQYDK2VwAyEA4rXbykPjSf/G1Ya5TqwGDkOPoqDwcm3bCTSJqdUuM/E= -----END PUBLIC KEY----- ================================================ FILE: vars/per-machine/kartoffel/data-mesher-node-identity/peer.id/value ================================================ 12D3KooWR5M9NUjNX97gU1L1LQCpm1Y3iuoqzxtAvp675CznSxvU ================================================ FILE: vars/per-machine/kartoffel/dm-pull-deploy-status-key/signing.pub/value ================================================ -----BEGIN PUBLIC KEY----- MCowBQYDK2VwAyEA24rRO7Vpsgchmi4aprNVm3UkzIWbBGX7SdwrHqAH3f8= -----END PUBLIC KEY----- ================================================ FILE: vars/per-machine/kartoffel/state-version/version/value ================================================ 20.03 ================================================ FILE: vars/per-machine/kartoffel/wireguard/publickey/value ================================================ HM7c/n99iblET+6myP8fG3L79L8nWWHHwaH+Oz0OPEE= ================================================ FILE: vars/per-machine/kartoffel/wireguard-wg-clan/publickey/value ================================================ 0MEAQID/ekklJgxAFr7iSWqVNbptJ+TP1y9B/yuC3D8= ================================================ FILE: vars/per-machine/kartoffel/wireguard-wg-clan-ip/ipv4/value ================================================ 192.168.8.3 ================================================ FILE: vars/per-machine/kartoffel/yggdrasil/address/value ================================================ 200:34c1:9a28:814a:c9da:ddfb:b815:618c ================================================ FILE: vars/per-machine/kartoffel/yggdrasil/publicKey/value ================================================ -----BEGIN PUBLIC KEY----- MCowBQYDK2VwAyEA5Z8y679amxKRAiP1TznLbmb2DZvWh6k+JjpNUy0V4U0= -----END PUBLIC KEY----- ================================================ FILE: vars/per-machine/kfbox/caddy/.validation-hash ================================================ 9aec7258b9d9753deca55cf69724c322103ff822c72fa6614fb3e5c41e45cdf8 ================================================ FILE: vars/per-machine/kfbox/cert-irc/.validation-hash ================================================ 2aaa4ab651853fc3e5127042322459c42a8746ac6453164f85289e09a4e43e83 ================================================ FILE: vars/per-machine/kfbox/cert-irc/irc.crt/value ================================================ -----BEGIN CERTIFICATE----- MIIFDTCCAvWgAwIBAgIUK5hrP+Hfe3HsE4pEf9OXCtP567QwDQYJKoZIhvcNAQEL BQAwFzEVMBMGA1UEAwwMQ2xhbiBSb290IENBMB4XDTI2MDIwODExNDIwMVoXDTI3 MDIwODExNDIwMVowEjEQMA4GA1UEAwwHaXJjLnBpbjCCAiIwDQYJKoZIhvcNAQEB BQADggIPADCCAgoCggIBAK4Nd4TbgTVCjCfp1I38BwIYRsBg+jJQPT/J23YmWi+l HGpNDv20U7wVDBwtxpvuUIiBiazT1u5p2lNU4inryO4bxr1htbdw/hZg9W5Ky3zS ZZVXj+O9ZSaHBfMS/LLjqc8vflaUi35XW7pwLqtARDdkjWpTgqrDW7RrT5kW/vSD urS672HuuIUGJmeTH/YQ9lhcFsDytrGaw2sHr1sXhEkti+4mp8+fzTjnn609z41d hPqaok1ul+YpUX81wEGtuG2aeKMdeOx/9eNnh0020TzG2jrgQkU2T/qx8DVstuRv uArQUzam2MyvjhQ6emjyyOTC1bE6m5AJJaJ8WC6bc9Nuxr1UfmN9ZxUqJKJGolz8 eAAFN4BmbJzYex02CkPviTPi9g86PV2uWBQ5RR1pA4hFQaNIjppy6SdPHT98wvKP Gxc0JuVk5NxI8FoPqez7cITylbQzgEU52mhgxuBx7OtiFMJIeEZbmLDkBKY+ayZe BqbrE4pWQOKJZtybZnrr2hcyWodBrPDd25mszUV65EjvkoxE2xumGf9FKclexS7j x3FAnY2NzDi9u4c64UzsuI+IlHYZQYweo5BDnpTeQu6CJQbTjwKK272KbyPys0Pm vWma3FBb3sGawHpV9NflGAEXlKVRWTZ3nfl5lba7ustWOGCLxmXL0FcMZnxZRE5L AgMBAAGjVjBUMBIGA1UdEQQLMAmCB2lyYy5waW4wHQYDVR0OBBYEFDncIbBM+vGm /1HcszLcYKAM4WWiMB8GA1UdIwQYMBaAFKWoP08/0GFUxRFYLzYvYIe2Sr3NMA0G CSqGSIb3DQEBCwUAA4ICAQAX9i4sjZoMGyOvOvzypK8Z21ecfaP6WQFopa0zMMLF HpFvvxy358Hh2eqqVoKgu34JB1aBYLpYnDoarAgkh9jEpL+9RmgFbZgVdBVW5dZz CpNYdvnVqOk7Ko8BoQ15Q7Ha5z+h+0yTOJ4L7SogQawMadwgLk7atSEzgldzqJyW xWpqGN9kNNsOHOYxWxeTZVYF2Zvt2qnFv86Cl8+WpfyXBhuWEpcgaUvt2xNO8wOy tii+T98PTNNj0JWlN2l8qodW2GsbNn9S4xcE6lzffhxBM0ogAvKUwj/5RvQ1Ki8L 99lKTlP01ubSjygShmgpC/HKbphKncvlss41GoMEhd8E/EeUCF2uhNVuErGkpUXO HzAKUfXqBVNkcP8a0jkyTyDbngEKOIv7kukDxaPaMQekLBtawCNwJ+Vv9ZlF4WOl gr1ULzuuw7mexppoaOk61d+GRMR9/cXPS84qhXdu/wT+PBlC4udJQAMwahqPCmqv vnKLP1Anv/Qkb6CnBNTpKLg/pAnfnJ3tH9yZ2FoeNmyjsomKmwDc6JrG8aNBKoPL 07pOmya5SZMYgAmrRusiaE8kwdLlXFHAPFP5LnqsQjcgHGAe3ZDk2SPscI0sxZG7 cB/h22Z5X9ZRqdfEAxidILFfpF/O+9ej1DKm353jRkxRizOzZcrzGn5klr9II3vO Ig== -----END CERTIFICATE----- ================================================ FILE: vars/per-machine/kfbox/cert-irc/irc.fullchain.crt/value ================================================ -----BEGIN CERTIFICATE----- MIIFDTCCAvWgAwIBAgIUK5hrP+Hfe3HsE4pEf9OXCtP567QwDQYJKoZIhvcNAQEL BQAwFzEVMBMGA1UEAwwMQ2xhbiBSb290IENBMB4XDTI2MDIwODExNDIwMVoXDTI3 MDIwODExNDIwMVowEjEQMA4GA1UEAwwHaXJjLnBpbjCCAiIwDQYJKoZIhvcNAQEB BQADggIPADCCAgoCggIBAK4Nd4TbgTVCjCfp1I38BwIYRsBg+jJQPT/J23YmWi+l HGpNDv20U7wVDBwtxpvuUIiBiazT1u5p2lNU4inryO4bxr1htbdw/hZg9W5Ky3zS ZZVXj+O9ZSaHBfMS/LLjqc8vflaUi35XW7pwLqtARDdkjWpTgqrDW7RrT5kW/vSD urS672HuuIUGJmeTH/YQ9lhcFsDytrGaw2sHr1sXhEkti+4mp8+fzTjnn609z41d hPqaok1ul+YpUX81wEGtuG2aeKMdeOx/9eNnh0020TzG2jrgQkU2T/qx8DVstuRv uArQUzam2MyvjhQ6emjyyOTC1bE6m5AJJaJ8WC6bc9Nuxr1UfmN9ZxUqJKJGolz8 eAAFN4BmbJzYex02CkPviTPi9g86PV2uWBQ5RR1pA4hFQaNIjppy6SdPHT98wvKP Gxc0JuVk5NxI8FoPqez7cITylbQzgEU52mhgxuBx7OtiFMJIeEZbmLDkBKY+ayZe BqbrE4pWQOKJZtybZnrr2hcyWodBrPDd25mszUV65EjvkoxE2xumGf9FKclexS7j x3FAnY2NzDi9u4c64UzsuI+IlHYZQYweo5BDnpTeQu6CJQbTjwKK272KbyPys0Pm vWma3FBb3sGawHpV9NflGAEXlKVRWTZ3nfl5lba7ustWOGCLxmXL0FcMZnxZRE5L AgMBAAGjVjBUMBIGA1UdEQQLMAmCB2lyYy5waW4wHQYDVR0OBBYEFDncIbBM+vGm /1HcszLcYKAM4WWiMB8GA1UdIwQYMBaAFKWoP08/0GFUxRFYLzYvYIe2Sr3NMA0G CSqGSIb3DQEBCwUAA4ICAQAX9i4sjZoMGyOvOvzypK8Z21ecfaP6WQFopa0zMMLF HpFvvxy358Hh2eqqVoKgu34JB1aBYLpYnDoarAgkh9jEpL+9RmgFbZgVdBVW5dZz CpNYdvnVqOk7Ko8BoQ15Q7Ha5z+h+0yTOJ4L7SogQawMadwgLk7atSEzgldzqJyW xWpqGN9kNNsOHOYxWxeTZVYF2Zvt2qnFv86Cl8+WpfyXBhuWEpcgaUvt2xNO8wOy tii+T98PTNNj0JWlN2l8qodW2GsbNn9S4xcE6lzffhxBM0ogAvKUwj/5RvQ1Ki8L 99lKTlP01ubSjygShmgpC/HKbphKncvlss41GoMEhd8E/EeUCF2uhNVuErGkpUXO HzAKUfXqBVNkcP8a0jkyTyDbngEKOIv7kukDxaPaMQekLBtawCNwJ+Vv9ZlF4WOl gr1ULzuuw7mexppoaOk61d+GRMR9/cXPS84qhXdu/wT+PBlC4udJQAMwahqPCmqv vnKLP1Anv/Qkb6CnBNTpKLg/pAnfnJ3tH9yZ2FoeNmyjsomKmwDc6JrG8aNBKoPL 07pOmya5SZMYgAmrRusiaE8kwdLlXFHAPFP5LnqsQjcgHGAe3ZDk2SPscI0sxZG7 cB/h22Z5X9ZRqdfEAxidILFfpF/O+9ej1DKm353jRkxRizOzZcrzGn5klr9II3vO Ig== -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIFDzCCAvegAwIBAgIUQjBDSURUCyly2/5c+Fw/iB+HcUAwDQYJKoZIhvcNAQEL BQAwFzEVMBMGA1UEAwwMQ2xhbiBSb290IENBMB4XDTI2MDIwNzE1MjkyNVoXDTM2 MDIwNTE1MjkyNVowFzEVMBMGA1UEAwwMQ2xhbiBSb290IENBMIICIjANBgkqhkiG 9w0BAQEFAAOCAg8AMIICCgKCAgEAwXHu+e90SAm7iqxXPenn+N7NfMM97rWpVkB8 X4g/2jQFfitN9M/CbbXYNPli010+nVzG/bzqBKrIybtIw+5TkCkU1BunRQzlAWxZ vOfX1bHj7qwn3q4d98h2wRJ1vkqt/0qf82g0AzmMfMZPRpAxoI4UlDQbVNLOGavg /usO20yfsdhCMAm2cNVS8MTYVO9xkgNiMUnDG2j8S/srZcrY8v21EG62atiM5I1k Y4tikfJSAYKzQL+OuvZjX+rrkpt084CzbKEHkNodz/+OmCqzRyMdR3j4dtMK1OSe +bH0tmp7JMR5nGOkhfVdrTE7VyOT1yIjRbRaneYnbrz4FUiO51jKdta08axXKcaJ afEd+CT4iWZfRCwVzf2SVhCXzk3KbR6IBM/UeQkIFA4k8YRQPWYmUkMe725f2y1t MQOFj/xbI36MzcQavbdLzpstBrbIidjWik5475clhx9NzsvHX9B6amBBUheKN4lE 6TstNK1uM3+053JQovHM9RS/Ff3AeFiOLf5+W+WL/0M6ue+h7PSe5w2KlYmhw0IR cblg3ixhA5CrjXktUURYJfDTaNqjDHuBoCUFwFjq6MowM8WttpViFDbN/5ZtZHMA 1n6dkLlSW1D0/TcvzAgmTQPq51KdEc9ek6/nwDRcM5OlAd8ohhQ9Ci6Jnm4t3Q0i BDzCYOcCAwEAAaNTMFEwHQYDVR0OBBYEFKWoP08/0GFUxRFYLzYvYIe2Sr3NMB8G A1UdIwQYMBaAFKWoP08/0GFUxRFYLzYvYIe2Sr3NMA8GA1UdEwEB/wQFMAMBAf8w DQYJKoZIhvcNAQELBQADggIBAGZKm92aAxwq2+9WZV3NDqPx3YDpTeiZ69UQF+up SsPZn7dAcL2fmMyYyIJ67OR5aiv+UH4+Z4Gp8vaP5y4PqHJKkXOhiSTx11oVNDV9 4He18FT55BVdHS6xdxnJVV19dFxz1whlv2fP/tiTAdihER0wNj/mJTQifcyUcBkD Rf6Qm+s1y1Ryu3lJp3yMff/7XBwdz2gg5dGp1+kYBYtRZnXPFfFvQy3uQvd8oqb3 y83EJS5Biz1ChfE9nydOzoTqNA+m+vKSvOfPpF5ZQ7UgRb855/eSORf1mRWvaD97 +OKRliXP37njHzzlBSfvi7RxfhLbR3dU6wRmGYOhnxzATXS7cFVp8ziNd3/oXLem K947ZsQaJsidyuq19MjQpeae9V8vp43fAB1i4TfzJC9KjgrZi9UUUKBmCHXyrH// lU4VBsQzaMvrQDUIdc+Cjl5+DlVleyL2kgJWm1EobgO9V7uHXi3GOl+9UyEaad8V ST7yg3pIDnrfmvQdBcJ9lA6xaaujVuHaddE+HlO2t+9iARPSdAdxDA/GQKAyDzni mO+SpoHu02ECdiUM8avICL0q7zIb/LdQHfsqnm7UU+Q4VAs13BkpaX0XK9TZoM80 ENFRyXIBJ4hP9+V57D0fA+46aDSZCJEKlcz7hj2R9Zl7EGJNaIcMe3CCcV2GXMYf GUo6 -----END CERTIFICATE----- ================================================ FILE: vars/per-machine/kfbox/cert-music/.validation-hash ================================================ 10c6db460ebbeaaf8d9f62a312b2e64391f9699b322c2d56bcb2e006134bc3d0 ================================================ FILE: vars/per-machine/kfbox/cert-music/music.crt/value ================================================ -----BEGIN CERTIFICATE----- MIIFETCCAvmgAwIBAgIUESqh8zJyQyHog+1YCftZFwbsbvcwDQYJKoZIhvcNAQEL BQAwFzEVMBMGA1UEAwwMQ2xhbiBSb290IENBMB4XDTI2MDIwNzE1MjkyNVoXDTI3 MDIwNzE1MjkyNVowFDESMBAGA1UEAwwJbXVzaWMucGluMIICIjANBgkqhkiG9w0B AQEFAAOCAg8AMIICCgKCAgEA6mt3nLZdhWbnaJO5slC9pudjCPQZgqJstmgWeT2g CLOKLGRvE3Hm0TWzbbt3ZYAPNnJZqAs2S3RBqEqynOp+zilxTyh+sBETeKUK1v39 D3RabYmvZwIHU2EKTMP5CvHwwH2Gl4CeEwzkeZnRTNKYxqNAmD4hg0omLtGByY+8 SFiCN7Pu7I1Aqvk2yjWA9l5M0EcqSRGg4+c2xRtG7fTYG9xpSpIeP40wc+fvspsZ b83K9a5KjOuR8wsNjkL7gtV4ap42PKmt1v9lGWzm6NgCHsPuQEC73NFmSDN70bma AwRkU72L6wBzFF8Tkfmtc+SdhBxvZr35P1L000dorQ08vU5rMbBF/eRtP0Dot7tw ANZO0IgGO4t3xS8gjwhOOu/rkfiJykKKJWmnc0/3XhY6FRTly6VkOz4TRFnnKGFg 46F1rwXd3qjg0d5IFlcYvq0k8PwUPhaLBVEIKsGJrflhHnG//5qjgpyegXB4qP+z o0q3bkpeDAt/Udhrx7tr+bewUTITOR+v8HonF1nIy6n3A84kB+LmNzvDgrB6WBwx wq3oyS3leheC7OPeulD2i701K6d3rtTARHMw4VKHfwpfT+ASu+oxI0sek0hjVFRK yYhym4N2kbU1u2FycQIsR6PZqoPMlRRfcy0k1c/FyoXLA20al6thaHAT7mhhjDmV EeUCAwEAAaNYMFYwFAYDVR0RBA0wC4IJbXVzaWMucGluMB0GA1UdDgQWBBQIg/B0 p8RhoIewVQ9BR0HJCh5hsTAfBgNVHSMEGDAWgBSlqD9PP9BhVMURWC82L2CHtkq9 zTANBgkqhkiG9w0BAQsFAAOCAgEAPQmGD31NWT4n5c0bqTUDy6Xcmn/XNCtbi8e6 ppLy/hPR0ho8hNlh5E1MdrntzipWKdy9gOe84efpGqqvu2gSKoH1lUvfkRBb1T/U q7onKi5K4YyzIjlijiI+3hC8AJLk50GWSGy1N0COQkEMlk6YARAJQume/jqIbypJ VWhBeiBp5ELnuxQ4z/cP8f4Pw7DmTEJ0G/qEO2TbvfZHS966RN8g05ZY2po9w3XY g6BDzQbQCESEBB/LPa2g5kzO8K1CDdQcjNgh1k7YomIivw+EykNjGNORVAjji8aD Yvqv3pUOay26LhPDrPsTqnf05mQgLLXu43hbfalyMsN/1WgifhcelCo46f5c+pAq wgRDv4maTDgdiiutL5RuwdvidKMxjQHzSMYXTEgepwgmMSEQJgnqk5ctIIDDFgED bc0BPRR5aQpJLtOeCyuH/pY7Qh6cTziz7vsg4SeoBPLc2DK9SESPa00S6IOxuwud N847znj7w3SgEzUjnRX+pCRz0+UtQxp75Z65ZDMwW82C1nJgvs5IgcEvZikGJCLN HieSwByNgnl5cniUbdC+RLAAPUWAnUWhJr0qGFHjNwnQYVVbJ1Y1x7pC5cf9P2KR HP9sbQ3skBcSigcZnAhQfDKW4/SvgMs4Flk56QwZmw/WlcfqP14F28xB2YJILWhF 6RwQ+Lw= -----END CERTIFICATE----- ================================================ FILE: vars/per-machine/kfbox/cert-music/music.fullchain.crt/value ================================================ -----BEGIN CERTIFICATE----- MIIFETCCAvmgAwIBAgIUESqh8zJyQyHog+1YCftZFwbsbvcwDQYJKoZIhvcNAQEL BQAwFzEVMBMGA1UEAwwMQ2xhbiBSb290IENBMB4XDTI2MDIwNzE1MjkyNVoXDTI3 MDIwNzE1MjkyNVowFDESMBAGA1UEAwwJbXVzaWMucGluMIICIjANBgkqhkiG9w0B AQEFAAOCAg8AMIICCgKCAgEA6mt3nLZdhWbnaJO5slC9pudjCPQZgqJstmgWeT2g CLOKLGRvE3Hm0TWzbbt3ZYAPNnJZqAs2S3RBqEqynOp+zilxTyh+sBETeKUK1v39 D3RabYmvZwIHU2EKTMP5CvHwwH2Gl4CeEwzkeZnRTNKYxqNAmD4hg0omLtGByY+8 SFiCN7Pu7I1Aqvk2yjWA9l5M0EcqSRGg4+c2xRtG7fTYG9xpSpIeP40wc+fvspsZ b83K9a5KjOuR8wsNjkL7gtV4ap42PKmt1v9lGWzm6NgCHsPuQEC73NFmSDN70bma AwRkU72L6wBzFF8Tkfmtc+SdhBxvZr35P1L000dorQ08vU5rMbBF/eRtP0Dot7tw ANZO0IgGO4t3xS8gjwhOOu/rkfiJykKKJWmnc0/3XhY6FRTly6VkOz4TRFnnKGFg 46F1rwXd3qjg0d5IFlcYvq0k8PwUPhaLBVEIKsGJrflhHnG//5qjgpyegXB4qP+z o0q3bkpeDAt/Udhrx7tr+bewUTITOR+v8HonF1nIy6n3A84kB+LmNzvDgrB6WBwx wq3oyS3leheC7OPeulD2i701K6d3rtTARHMw4VKHfwpfT+ASu+oxI0sek0hjVFRK yYhym4N2kbU1u2FycQIsR6PZqoPMlRRfcy0k1c/FyoXLA20al6thaHAT7mhhjDmV EeUCAwEAAaNYMFYwFAYDVR0RBA0wC4IJbXVzaWMucGluMB0GA1UdDgQWBBQIg/B0 p8RhoIewVQ9BR0HJCh5hsTAfBgNVHSMEGDAWgBSlqD9PP9BhVMURWC82L2CHtkq9 zTANBgkqhkiG9w0BAQsFAAOCAgEAPQmGD31NWT4n5c0bqTUDy6Xcmn/XNCtbi8e6 ppLy/hPR0ho8hNlh5E1MdrntzipWKdy9gOe84efpGqqvu2gSKoH1lUvfkRBb1T/U q7onKi5K4YyzIjlijiI+3hC8AJLk50GWSGy1N0COQkEMlk6YARAJQume/jqIbypJ VWhBeiBp5ELnuxQ4z/cP8f4Pw7DmTEJ0G/qEO2TbvfZHS966RN8g05ZY2po9w3XY g6BDzQbQCESEBB/LPa2g5kzO8K1CDdQcjNgh1k7YomIivw+EykNjGNORVAjji8aD Yvqv3pUOay26LhPDrPsTqnf05mQgLLXu43hbfalyMsN/1WgifhcelCo46f5c+pAq wgRDv4maTDgdiiutL5RuwdvidKMxjQHzSMYXTEgepwgmMSEQJgnqk5ctIIDDFgED bc0BPRR5aQpJLtOeCyuH/pY7Qh6cTziz7vsg4SeoBPLc2DK9SESPa00S6IOxuwud N847znj7w3SgEzUjnRX+pCRz0+UtQxp75Z65ZDMwW82C1nJgvs5IgcEvZikGJCLN HieSwByNgnl5cniUbdC+RLAAPUWAnUWhJr0qGFHjNwnQYVVbJ1Y1x7pC5cf9P2KR HP9sbQ3skBcSigcZnAhQfDKW4/SvgMs4Flk56QwZmw/WlcfqP14F28xB2YJILWhF 6RwQ+Lw= -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIFDzCCAvegAwIBAgIUQjBDSURUCyly2/5c+Fw/iB+HcUAwDQYJKoZIhvcNAQEL BQAwFzEVMBMGA1UEAwwMQ2xhbiBSb290IENBMB4XDTI2MDIwNzE1MjkyNVoXDTM2 MDIwNTE1MjkyNVowFzEVMBMGA1UEAwwMQ2xhbiBSb290IENBMIICIjANBgkqhkiG 9w0BAQEFAAOCAg8AMIICCgKCAgEAwXHu+e90SAm7iqxXPenn+N7NfMM97rWpVkB8 X4g/2jQFfitN9M/CbbXYNPli010+nVzG/bzqBKrIybtIw+5TkCkU1BunRQzlAWxZ vOfX1bHj7qwn3q4d98h2wRJ1vkqt/0qf82g0AzmMfMZPRpAxoI4UlDQbVNLOGavg /usO20yfsdhCMAm2cNVS8MTYVO9xkgNiMUnDG2j8S/srZcrY8v21EG62atiM5I1k Y4tikfJSAYKzQL+OuvZjX+rrkpt084CzbKEHkNodz/+OmCqzRyMdR3j4dtMK1OSe +bH0tmp7JMR5nGOkhfVdrTE7VyOT1yIjRbRaneYnbrz4FUiO51jKdta08axXKcaJ afEd+CT4iWZfRCwVzf2SVhCXzk3KbR6IBM/UeQkIFA4k8YRQPWYmUkMe725f2y1t MQOFj/xbI36MzcQavbdLzpstBrbIidjWik5475clhx9NzsvHX9B6amBBUheKN4lE 6TstNK1uM3+053JQovHM9RS/Ff3AeFiOLf5+W+WL/0M6ue+h7PSe5w2KlYmhw0IR cblg3ixhA5CrjXktUURYJfDTaNqjDHuBoCUFwFjq6MowM8WttpViFDbN/5ZtZHMA 1n6dkLlSW1D0/TcvzAgmTQPq51KdEc9ek6/nwDRcM5OlAd8ohhQ9Ci6Jnm4t3Q0i BDzCYOcCAwEAAaNTMFEwHQYDVR0OBBYEFKWoP08/0GFUxRFYLzYvYIe2Sr3NMB8G A1UdIwQYMBaAFKWoP08/0GFUxRFYLzYvYIe2Sr3NMA8GA1UdEwEB/wQFMAMBAf8w DQYJKoZIhvcNAQELBQADggIBAGZKm92aAxwq2+9WZV3NDqPx3YDpTeiZ69UQF+up SsPZn7dAcL2fmMyYyIJ67OR5aiv+UH4+Z4Gp8vaP5y4PqHJKkXOhiSTx11oVNDV9 4He18FT55BVdHS6xdxnJVV19dFxz1whlv2fP/tiTAdihER0wNj/mJTQifcyUcBkD Rf6Qm+s1y1Ryu3lJp3yMff/7XBwdz2gg5dGp1+kYBYtRZnXPFfFvQy3uQvd8oqb3 y83EJS5Biz1ChfE9nydOzoTqNA+m+vKSvOfPpF5ZQ7UgRb855/eSORf1mRWvaD97 +OKRliXP37njHzzlBSfvi7RxfhLbR3dU6wRmGYOhnxzATXS7cFVp8ziNd3/oXLem K947ZsQaJsidyuq19MjQpeae9V8vp43fAB1i4TfzJC9KjgrZi9UUUKBmCHXyrH// lU4VBsQzaMvrQDUIdc+Cjl5+DlVleyL2kgJWm1EobgO9V7uHXi3GOl+9UyEaad8V ST7yg3pIDnrfmvQdBcJ9lA6xaaujVuHaddE+HlO2t+9iARPSdAdxDA/GQKAyDzni mO+SpoHu02ECdiUM8avICL0q7zIb/LdQHfsqnm7UU+Q4VAs13BkpaX0XK9TZoM80 ENFRyXIBJ4hP9+V57D0fA+46aDSZCJEKlcz7hj2R9Zl7EGJNaIcMe3CCcV2GXMYf GUo6 -----END CERTIFICATE----- ================================================ FILE: vars/per-machine/kfbox/data-mesher-host-key/public_key/value ================================================ -----BEGIN PUBLIC KEY----- MCowBQYDK2VwAyEA9lBef9cvOwKBdAkIhPX60ggufLXkZLMqCsyugWSKoaA= -----END PUBLIC KEY----- ================================================ FILE: vars/per-machine/kfbox/data-mesher-node-identity/identity.pub/value ================================================ -----BEGIN PUBLIC KEY----- MCowBQYDK2VwAyEAMiXKPkr1NZ3JhrpxMKq2TVmEJ9lCAUYgWuAESqqZeUw= -----END PUBLIC KEY----- ================================================ FILE: vars/per-machine/kfbox/data-mesher-node-identity/peer.id/value ================================================ 12D3KooWDC7yXRWZEFBoeWaZaKZEz6mqyv5ZW1ejN3ciCkzyMi19 ================================================ FILE: vars/per-machine/kfbox/dex/.validation-hash ================================================ d2b8da6120ff1bcbaec6e423e507d423c13a4e2c55cee7b8f0ddbe33363312eb ================================================ FILE: vars/per-machine/kfbox/dm-pull-deploy-status-key/signing.pub/value ================================================ -----BEGIN PUBLIC KEY----- MCowBQYDK2VwAyEAMu8cVts+1YkOvtJfpsI0OfunwPF3j1l8/dMqQxDCkok= -----END PUBLIC KEY----- ================================================ FILE: vars/per-machine/kfbox/go-karma-bot/.validation-hash ================================================ 24d59f70cba7b4ee0c772354f0c041e925448e52431083a8bf37cda6b48b11a0 ================================================ FILE: vars/per-machine/kfbox/hedgedoc/.validation-hash ================================================ 96ba40a19e0a2b2696bb565184f28251d63c35f31f86ed8f85e526f660aa79ae ================================================ FILE: vars/per-machine/kfbox/jitsi-presence/.validation-hash ================================================ 3ce0ea6332ad94bb81f83a03ee7c89514f4e6b218a43956b03ba4f48e4383432 ================================================ FILE: vars/per-machine/kfbox/restic-exporter/.validation-hash ================================================ 2281613666175c6d6f41a42c0c5136e29409c86f7821fa5e73be344cf9fd18d7 ================================================ FILE: vars/per-machine/kfbox/state-version/version/value ================================================ 22.05 ================================================ FILE: vars/per-machine/kfbox/vikunja/.validation-hash ================================================ d46d7942b025ed4866f10969d73fb73fc90b2e3d7e30752d3c45d8a0142ecca5 ================================================ FILE: vars/per-machine/kfbox/wireguard/publickey/value ================================================ N5rFljGz1BMiF3hxsRChK9VvZxmQchZFcQrIkECVEnU= ================================================ FILE: vars/per-machine/kfbox/wireguard-wg-clan/ipv4/value ================================================ 192.168.8.5 ================================================ FILE: vars/per-machine/kfbox/wireguard-wg-clan/publickey/value ================================================ p4aaNlsIcPQi+z1cMDj/OX8EK6qwDT2KdzHYul8fSno= ================================================ FILE: vars/per-machine/kfbox/wireguard-wg-clan-ip/ipv4/value ================================================ 192.168.8.5 ================================================ FILE: vars/per-machine/kfbox/wireguard-wg-star/ipv6/value ================================================ fda1:05c8:00::c6e7:1e61:22a7:de2e ================================================ FILE: vars/per-machine/kfbox/wireguard-wg-star/publickey/value ================================================ 2UBwODAKKGFL+xBPBBZ2Xl5U9hnFL+InBlicP77lXls= ================================================ FILE: vars/per-machine/kfbox/yggdrasil/address/value ================================================ 200:2065:4635:7d71:2877:fc16:1fae:197d ================================================ FILE: vars/per-machine/kfbox/yggdrasil/publicKey/value ================================================ -----BEGIN PUBLIC KEY----- MCowBQYDK2VwAyEA781c5UFHa8QB9PAo80Em7BHnbf042/37Dvg6DVlL3sE= -----END PUBLIC KEY----- ================================================ FILE: vars/per-machine/kiwi/cert-claw/.validation-hash ================================================ 976efc6b3915b2db6922e211aba08e18d24c99902262244b902825a4d1bfbcf8 ================================================ FILE: vars/per-machine/kiwi/cert-claw/claw.crt/value ================================================ -----BEGIN CERTIFICATE----- MIIFDzCCAvegAwIBAgIUKdsmT2t/pcjdIfg8DCgBVQaiZMAwDQYJKoZIhvcNAQEL BQAwFzEVMBMGA1UEAwwMQ2xhbiBSb290IENBMB4XDTI2MDIxMzE2NDQ1N1oXDTI3 MDIxMzE2NDQ1N1owEzERMA8GA1UEAwwIY2xhdy5waW4wggIiMA0GCSqGSIb3DQEB AQUAA4ICDwAwggIKAoICAQCxCO2H2mz0XuAd2fSvg0EwWzE6Zr8tcFBT5q9xqq0l Nqn0xVqSjEXnnxrCc7KQdKikLJNFv0oXJ9wQ7xsmJVhgZ9AxYjZ967lLcLs5Oa5Q mX+1Ew1gx12qHNXWzateH/7T58EtLB0ZfGIIz1pq1DMnYYsfkh5pWDgxY3a5mujy RM/LbCTy8r4Bn30NJvSIPinoehjCiPfz4dGiNwOVVZ7b1ANS/4Bo7+/Y+VLxyzzF g3KtwegKXMDoTrh6Kd6JMnzk65GzCVs1a6L78mSSJnaQG3enVv/2L21yHwrQ5p9q QTsJ/rE92wVY7B0slCDDVTTKsLcEw956c4xjqmXyY7xYi7kTOzh3wbLs02Hgf503 CAHnxaC54vSXDG7N7ba1g/MdQN84kbRUQveKz915ZsNKaOxuizRMyZdmbaU/eSjx LciXkXNx09FIdXGDUj1WCsTxQzHqXW76lnpDyUr9Dq1HXMIxk9kjLZkasm5nkHYw hGY60GfIiDJpmiZj0YOArAG5F3k05ZfcEhO11jqi9UMKqQFr1U/R8fRLFwj3PkNc Fvm8krD6yvYnKDcEiVSkgWTnOLCCL1I+ADIiMQ/sbipxDOLLUb+HvX0DeCG9Jwnu 1qCTYdue89HyuOg/CUZciI/MLuyZICgJyNK4eNPhxzjKM3pIfjY1gW+SnMxoXbKz MQIDAQABo1cwVTATBgNVHREEDDAKgghjbGF3LnBpbjAdBgNVHQ4EFgQUKOerRsZK AxHTrldLiNxg6PLheUowHwYDVR0jBBgwFoAUpag/Tz/QYVTFEVgvNi9gh7ZKvc0w DQYJKoZIhvcNAQELBQADggIBALpI/yLuEs7udHJ1IWspbdS/FOgUPVqf6bIStgMn UkEYcrRORWB8HSSDgvabZ/no7B/uyF9xdlyn62L+J04LYoZI28h2FPiq+JvwPQ6P 3d32dsa7u6uBYERT55rVbCj5zGyG7k+hmHynjv81+EQIYkZEB1yYLLvmXo+RoEft tRQHwD6/ATXA9UaOSokleiGRnw58vRjo60AgOyoC9zQKEq4jMwufGFZuugNmzAnU yit9cZkqOf1/szPDeh7bJ9RrhFAx2vUqisTznHt7xG40THb46H+QYeu/mE11zjYi nBmJry4vNp6JoDOpjKjuevoigQxqq/7a/YI97WJ4QgcVS0o+UR7Iw50cW1CQyPKM /8/1NnnMady5RT7DP/qe4DlF7I4f1ei0s9Ma51sFUPhym+YnRcbAkgxG5tLSBApR oE2Rh6hqesgkycMm4R/ipsbmHNlYPOLKeAPhafYwL7etuNsjvdwqA4GaPG0iKC2e D26Is1RCtmrHuaAk0jXRixZQSCKfQ+lJx/y8Pezu/VhVGFsJEwRdGDryepWKLi1l zSI3ZImORpXQgMbYOptXwkBh6c6w4NbML6h+C+OiOs0S4zG/Evo3sBaKlckpDAFv 8ty2oB/9ZMkhr1V0M3VndG+ErogKaWLam7KJrHI0jEVKDn6L4uPPyM2B4vEoA7xI ELfp -----END CERTIFICATE----- ================================================ FILE: vars/per-machine/kiwi/cert-claw/claw.fullchain.crt/value ================================================ -----BEGIN CERTIFICATE----- MIIFDzCCAvegAwIBAgIUKdsmT2t/pcjdIfg8DCgBVQaiZMAwDQYJKoZIhvcNAQEL BQAwFzEVMBMGA1UEAwwMQ2xhbiBSb290IENBMB4XDTI2MDIxMzE2NDQ1N1oXDTI3 MDIxMzE2NDQ1N1owEzERMA8GA1UEAwwIY2xhdy5waW4wggIiMA0GCSqGSIb3DQEB AQUAA4ICDwAwggIKAoICAQCxCO2H2mz0XuAd2fSvg0EwWzE6Zr8tcFBT5q9xqq0l Nqn0xVqSjEXnnxrCc7KQdKikLJNFv0oXJ9wQ7xsmJVhgZ9AxYjZ967lLcLs5Oa5Q mX+1Ew1gx12qHNXWzateH/7T58EtLB0ZfGIIz1pq1DMnYYsfkh5pWDgxY3a5mujy RM/LbCTy8r4Bn30NJvSIPinoehjCiPfz4dGiNwOVVZ7b1ANS/4Bo7+/Y+VLxyzzF g3KtwegKXMDoTrh6Kd6JMnzk65GzCVs1a6L78mSSJnaQG3enVv/2L21yHwrQ5p9q QTsJ/rE92wVY7B0slCDDVTTKsLcEw956c4xjqmXyY7xYi7kTOzh3wbLs02Hgf503 CAHnxaC54vSXDG7N7ba1g/MdQN84kbRUQveKz915ZsNKaOxuizRMyZdmbaU/eSjx LciXkXNx09FIdXGDUj1WCsTxQzHqXW76lnpDyUr9Dq1HXMIxk9kjLZkasm5nkHYw hGY60GfIiDJpmiZj0YOArAG5F3k05ZfcEhO11jqi9UMKqQFr1U/R8fRLFwj3PkNc Fvm8krD6yvYnKDcEiVSkgWTnOLCCL1I+ADIiMQ/sbipxDOLLUb+HvX0DeCG9Jwnu 1qCTYdue89HyuOg/CUZciI/MLuyZICgJyNK4eNPhxzjKM3pIfjY1gW+SnMxoXbKz MQIDAQABo1cwVTATBgNVHREEDDAKgghjbGF3LnBpbjAdBgNVHQ4EFgQUKOerRsZK AxHTrldLiNxg6PLheUowHwYDVR0jBBgwFoAUpag/Tz/QYVTFEVgvNi9gh7ZKvc0w DQYJKoZIhvcNAQELBQADggIBALpI/yLuEs7udHJ1IWspbdS/FOgUPVqf6bIStgMn UkEYcrRORWB8HSSDgvabZ/no7B/uyF9xdlyn62L+J04LYoZI28h2FPiq+JvwPQ6P 3d32dsa7u6uBYERT55rVbCj5zGyG7k+hmHynjv81+EQIYkZEB1yYLLvmXo+RoEft tRQHwD6/ATXA9UaOSokleiGRnw58vRjo60AgOyoC9zQKEq4jMwufGFZuugNmzAnU yit9cZkqOf1/szPDeh7bJ9RrhFAx2vUqisTznHt7xG40THb46H+QYeu/mE11zjYi nBmJry4vNp6JoDOpjKjuevoigQxqq/7a/YI97WJ4QgcVS0o+UR7Iw50cW1CQyPKM /8/1NnnMady5RT7DP/qe4DlF7I4f1ei0s9Ma51sFUPhym+YnRcbAkgxG5tLSBApR oE2Rh6hqesgkycMm4R/ipsbmHNlYPOLKeAPhafYwL7etuNsjvdwqA4GaPG0iKC2e D26Is1RCtmrHuaAk0jXRixZQSCKfQ+lJx/y8Pezu/VhVGFsJEwRdGDryepWKLi1l zSI3ZImORpXQgMbYOptXwkBh6c6w4NbML6h+C+OiOs0S4zG/Evo3sBaKlckpDAFv 8ty2oB/9ZMkhr1V0M3VndG+ErogKaWLam7KJrHI0jEVKDn6L4uPPyM2B4vEoA7xI ELfp -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIFDzCCAvegAwIBAgIUQjBDSURUCyly2/5c+Fw/iB+HcUAwDQYJKoZIhvcNAQEL BQAwFzEVMBMGA1UEAwwMQ2xhbiBSb290IENBMB4XDTI2MDIwNzE1MjkyNVoXDTM2 MDIwNTE1MjkyNVowFzEVMBMGA1UEAwwMQ2xhbiBSb290IENBMIICIjANBgkqhkiG 9w0BAQEFAAOCAg8AMIICCgKCAgEAwXHu+e90SAm7iqxXPenn+N7NfMM97rWpVkB8 X4g/2jQFfitN9M/CbbXYNPli010+nVzG/bzqBKrIybtIw+5TkCkU1BunRQzlAWxZ vOfX1bHj7qwn3q4d98h2wRJ1vkqt/0qf82g0AzmMfMZPRpAxoI4UlDQbVNLOGavg /usO20yfsdhCMAm2cNVS8MTYVO9xkgNiMUnDG2j8S/srZcrY8v21EG62atiM5I1k Y4tikfJSAYKzQL+OuvZjX+rrkpt084CzbKEHkNodz/+OmCqzRyMdR3j4dtMK1OSe +bH0tmp7JMR5nGOkhfVdrTE7VyOT1yIjRbRaneYnbrz4FUiO51jKdta08axXKcaJ afEd+CT4iWZfRCwVzf2SVhCXzk3KbR6IBM/UeQkIFA4k8YRQPWYmUkMe725f2y1t MQOFj/xbI36MzcQavbdLzpstBrbIidjWik5475clhx9NzsvHX9B6amBBUheKN4lE 6TstNK1uM3+053JQovHM9RS/Ff3AeFiOLf5+W+WL/0M6ue+h7PSe5w2KlYmhw0IR cblg3ixhA5CrjXktUURYJfDTaNqjDHuBoCUFwFjq6MowM8WttpViFDbN/5ZtZHMA 1n6dkLlSW1D0/TcvzAgmTQPq51KdEc9ek6/nwDRcM5OlAd8ohhQ9Ci6Jnm4t3Q0i BDzCYOcCAwEAAaNTMFEwHQYDVR0OBBYEFKWoP08/0GFUxRFYLzYvYIe2Sr3NMB8G A1UdIwQYMBaAFKWoP08/0GFUxRFYLzYvYIe2Sr3NMA8GA1UdEwEB/wQFMAMBAf8w DQYJKoZIhvcNAQELBQADggIBAGZKm92aAxwq2+9WZV3NDqPx3YDpTeiZ69UQF+up SsPZn7dAcL2fmMyYyIJ67OR5aiv+UH4+Z4Gp8vaP5y4PqHJKkXOhiSTx11oVNDV9 4He18FT55BVdHS6xdxnJVV19dFxz1whlv2fP/tiTAdihER0wNj/mJTQifcyUcBkD Rf6Qm+s1y1Ryu3lJp3yMff/7XBwdz2gg5dGp1+kYBYtRZnXPFfFvQy3uQvd8oqb3 y83EJS5Biz1ChfE9nydOzoTqNA+m+vKSvOfPpF5ZQ7UgRb855/eSORf1mRWvaD97 +OKRliXP37njHzzlBSfvi7RxfhLbR3dU6wRmGYOhnxzATXS7cFVp8ziNd3/oXLem K947ZsQaJsidyuq19MjQpeae9V8vp43fAB1i4TfzJC9KjgrZi9UUUKBmCHXyrH// lU4VBsQzaMvrQDUIdc+Cjl5+DlVleyL2kgJWm1EobgO9V7uHXi3GOl+9UyEaad8V ST7yg3pIDnrfmvQdBcJ9lA6xaaujVuHaddE+HlO2t+9iARPSdAdxDA/GQKAyDzni mO+SpoHu02ECdiUM8avICL0q7zIb/LdQHfsqnm7UU+Q4VAs13BkpaX0XK9TZoM80 ENFRyXIBJ4hP9+V57D0fA+46aDSZCJEKlcz7hj2R9Zl7EGJNaIcMe3CCcV2GXMYf GUo6 -----END CERTIFICATE----- ================================================ FILE: vars/per-machine/kiwi/data-mesher-host-key/public_key/value ================================================ -----BEGIN PUBLIC KEY----- MCowBQYDK2VwAyEAsRoSObpLWJCKPVxUeNfOMMl2p+1eLe7NDF3cJaUedY0= -----END PUBLIC KEY----- ================================================ FILE: vars/per-machine/kiwi/data-mesher-node-identity/identity.pub/value ================================================ -----BEGIN PUBLIC KEY----- MCowBQYDK2VwAyEAp9vEAaIR35W84gOrNY+SiPV7yssU315KwMQpIANdG+k= -----END PUBLIC KEY----- ================================================ FILE: vars/per-machine/kiwi/data-mesher-node-identity/peer.id/value ================================================ 12D3KooWM7cch7yvMeNbxUEwjBM91EKNxfeWuhPto4kT6nK7UzZE ================================================ FILE: vars/per-machine/kiwi/dm-pull-deploy-status-key/signing.pub/value ================================================ -----BEGIN PUBLIC KEY----- MCowBQYDK2VwAyEAjoIxttD5fONZ20nAUWCpwSzcyxUAzz2fk8KQ+ll+CrE= -----END PUBLIC KEY----- ================================================ FILE: vars/per-machine/kiwi/openclaw/.validation-hash ================================================ 98598a2e147fff6f46d9ca8512ddd72c5f76c35d708a41195edb97437f1535f4 ================================================ FILE: vars/per-machine/kiwi/opencrow/.validation-hash ================================================ b08299c38b79b4a8195a4fe2e0c204c793278a003f4210a0dc778304366c3666 ================================================ FILE: vars/per-machine/kiwi/opencrow-email/.validation-hash ================================================ bbeae7cc7a6c2e9e4c900505b7678737dcd4f5b02d6489439298e7bb5b6d0902 ================================================ FILE: vars/per-machine/kiwi/opencrow-eversports/.validation-hash ================================================ 5e33339ca7ab2f9fa413ff7a129654b234f7b80e2a5f523ce75535b5e0ca6116 ================================================ FILE: vars/per-machine/kiwi/opencrow-geninf/pubkey/value ================================================ 43359cc017f0cea6a8c5082abbb51020e00ad0f77eba0540d045ca8be90d23ff ================================================ FILE: vars/per-machine/kiwi/opencrow-geninf-user/pubkey/value ================================================ 152553c8e87ca942bc8bae1b4af709002368bfa1186603759ab261c7d8a0df97 ================================================ FILE: vars/per-machine/kiwi/opencrow-nextcloud/.validation-hash ================================================ d877886ea3c25f02f8005f8e0c336a27e0b756ef09c1075707847b009070b3ec ================================================ FILE: vars/per-machine/kiwi/opencrow-nextcloud-work/.validation-hash ================================================ 77236f357fa2b9a3e9b633553abc4616a787272f3752388439197409aaf9dea7 ================================================ FILE: vars/per-machine/kiwi/opencrow-nostr-bot/nostr-public-key/value ================================================ 44e9c1fba70d51e8cd6d0b4bf99315c686cac547feaab5c2539a4f843c55e046 ================================================ FILE: vars/per-machine/kiwi/opencrow-nostr-user/nostr-public-key/value ================================================ npub1evf9p0304tplxqdja8m2hjr9r77hmetz87nuexuc6fs07fnvapuqg5ak9j ================================================ FILE: vars/per-machine/kiwi/state-version/version/value ================================================ 20.03 ================================================ FILE: vars/per-machine/kiwi/tor_tor/hostname/value ================================================ 7rf5fpxif4ufzo23imdirhfqoaqcthtixexjippwvo72yiejeqfrh3yd.onion ================================================ FILE: vars/per-machine/kiwi/wireguard-wg-clan/ipv4/value ================================================ 192.168.8.6 ================================================ FILE: vars/per-machine/kiwi/wireguard-wg-clan/publickey/value ================================================ rrN1oYQhcmJgjha/DwBtWRRDCXE1RwTzG2iAIh5mX0s= ================================================ FILE: vars/per-machine/kiwi/wireguard-wg-clan-ip/ipv4/value ================================================ 192.168.8.6 ================================================ FILE: vars/per-machine/kiwi/wireguard-wg-star/ipv6/value ================================================ fda1:05c8:00::522c:5caa:22b4:f678 ================================================ FILE: vars/per-machine/kiwi/wireguard-wg-star/publickey/value ================================================ XGwUmveHixXWlkA8pUlnkj9MHMM97y6juXkOxsqvfU0= ================================================ FILE: vars/per-machine/kiwi/wireguard-wg-tunnel/publickey/value ================================================ MueYIZYNfGm6j1KNpdvwy4RpFML/8cArh7Gfn892BTw= ================================================ FILE: vars/per-machine/kiwi/wireguard-wg-tunnel-ip/ipv4/value ================================================ 10.100.0.2 ================================================ FILE: vars/per-machine/kiwi/yggdrasil/address/value ================================================ 200:c9ba:df22:4f62:4973:ed2d:208b:232b ================================================ FILE: vars/per-machine/kiwi/yggdrasil/publicKey/value ================================================ 9b22906ed84edb4609696fba6e6a511e9d63df45308ebe43956e2ba196eb9266 ================================================ FILE: vars/per-machine/kiwi/zerotier/zerotier-ip/value ================================================ fd0c:cb9f:98da:6865:9c99:9365:d8fa:9922 ================================================ FILE: vars/per-machine/limette/data-mesher-node-identity/identity.pub/value ================================================ -----BEGIN PUBLIC KEY----- MCowBQYDK2VwAyEAtY2UUMDDL66GkKeZ40Pm/l4FoZE2zsdMZ+QcQLmZNsU= -----END PUBLIC KEY----- ================================================ FILE: vars/per-machine/limette/data-mesher-node-identity/peer.id/value ================================================ 12D3KooWN35B8zZhA9t1anJuATwjeruzDbT6TJw9duq8EWHQBHHe ================================================ FILE: vars/per-machine/limette/dm-pull-deploy-status-key/signing.pub/value ================================================ -----BEGIN PUBLIC KEY----- MCowBQYDK2VwAyEAN3r9Tg32a58F+WnbnjxdAeSgXWvADa0+wcH/nYu4L2w= -----END PUBLIC KEY----- ================================================ FILE: vars/per-machine/limette/state-version/version/value ================================================ 20.03 ================================================ FILE: vars/per-machine/limette/wireguard/publickey/value ================================================ TCDZ1RBxqVBEp699/3UUBm9icI9UERswFlw4kJWd/jE= ================================================ FILE: vars/per-machine/limette/wireguard-wg-clan/publickey/value ================================================ 8DaOeJAmGyrRR2o9W5lmik+VqaRO958XXu+eRHy8yHQ= ================================================ FILE: vars/per-machine/limette/wireguard-wg-clan-ip/ipv4/value ================================================ 192.168.8.8 ================================================ FILE: vars/per-machine/limette/yggdrasil/address/value ================================================ 201:242d:a12e:958:f77e:4c95:eef7:3975 ================================================ FILE: vars/per-machine/limette/yggdrasil/publicKey/value ================================================ -----BEGIN PUBLIC KEY----- MCowBQYDK2VwAyEAdvSXtH2pwiBs2oRCMaKLLFPQPXBG/TZF6jhTbbuwByk= -----END PUBLIC KEY----- ================================================ FILE: vars/per-machine/porree/alertmanager-ntfy/.validation-hash ================================================ 7c21c80d4620962abd3d601c5b4302caa54dc10981de5982c6821d853b67a949 ================================================ FILE: vars/per-machine/porree/caddy/.validation-hash ================================================ 39b8a0ffd9699b029fa80287910104a5a646388572aeeff18edd9aff32c566e0 ================================================ FILE: vars/per-machine/porree/caddy-basicauth/.validation-hash ================================================ e652f45df5a63f6f33855447f0b9aea66e664fc29399567bd01d1efd85a55cec ================================================ FILE: vars/per-machine/porree/cert-prometheus/.validation-hash ================================================ 1cf8c129b76a777ce93c9d791880170480804991bbce933ed118df57236741ea ================================================ FILE: vars/per-machine/porree/cert-prometheus/prometheus.crt/value ================================================ -----BEGIN CERTIFICATE----- MIIFGzCCAwOgAwIBAgIUB/eKfajvUEO3Fg6Pwo8+A3HuZo0wDQYJKoZIhvcNAQEL BQAwFzEVMBMGA1UEAwwMQ2xhbiBSb290IENBMB4XDTI2MDQwODIyMDQ1NFoXDTI3 MDQwODIyMDQ1NFowGTEXMBUGA1UEAwwOcHJvbWV0aGV1cy5waW4wggIiMA0GCSqG SIb3DQEBAQUAA4ICDwAwggIKAoICAQC0p9V+S1YRa+KVJD14u53EqzsSYdN5xvQL AexgIMyO6JdWBnPHD+GuSKnHvx5OzYH9A0bXdmrQWW694JVwvzn0tj00Qro3YDnT j+WH9vKZCJW3Sp6BzPtCh+trzjl4sWA+fMhrdJKoO2F2vZN/1eOTGpDHYHjXKsaI HY1pJuVehFd6EDGTIzo6obykp6im3M15AlDWAU2aLZgBvKtOimRCZyFtVTs6vmF1 W+ALGV+uNOJwc75UF69FFCJB3AWa+xZDxCfN8xDbDs0KshXlwxh1UWQcATEcNmx5 GYgkEbUxPqj0S4IMskygPNTCNZvMgjVOiLTKmgxMcXb6YT5UtktZb1smmjd/iSvs PyLhrK3qs9pi/i9kiZrD5N17OceHUlAm+7PmlKybKGwndZUVqStrN+eHr8zkiXVE 12X5HPbxfISjkkXsFSJ1oSdSBxeeYCE5FKnTSIHLqVEuBDFoDMXmDg6uxfwI6jNF yVvsjTtiZAfYPbpw325JwPfMZBRVmvKuUya77vWK5DCn+7cAVuv9NsP2X5+ENNxi Imwg3NMh/NsuqdydQMBO1ubuGj9CFHb40wCrjVno0sFhjFUcx9DwxAowlSALFkFq gofW5OpMXSM1iqntI/p0k4NcND1FJYd0yZee5CUxkhk6quZpDMVhvqoBDO5dEwss Qc/WV/YbaQIDAQABo10wWzAZBgNVHREEEjAQgg5wcm9tZXRoZXVzLnBpbjAdBgNV HQ4EFgQUxI3Q4zH8y+TGlpr5ngdJYTUYRKIwHwYDVR0jBBgwFoAUpag/Tz/QYVTF EVgvNi9gh7ZKvc0wDQYJKoZIhvcNAQELBQADggIBAIYe7fap9MDJHyPVPyGwoG/0 XYjUMM1qB8pbVHeKhtaTFiiD3oeyOrI4TLGgkqNoIrcQ7m4xA50GE/DRCQpxQvsJ hQ0BBtCxu4JAW76W2q1auEH26sGSh5AMjcwkccbvhO8H0zwvuSSJEQvsCq6x2e9q vPuxqKJC/8Kvz3fJdQ0u4B/wDx1nSB4G4MTrCaOMBkyx8PPNoFHOWKNEPiBxKqHQ jbhs6+ggZ0+o+QvbivWJEgyyREsHuXwp7L5s0sqaq2hrQzmgkn/qiJaBV7sjVZU1 KblqImo89ZJL/4sZRi60TCh6GKEgxyMHGKdgh0/0iFfoRw4YKUz6IPlGSNc0j6zD p0DD+9YFgjue1Vre1tz5zaZMlRz1S38JOP3BF37BfIB9X8IKPwcSe4f7m5D9Die9 fjHn8XJoURWPOMjJ1C6hn9H3a8SHNEbG0bT1x6IXwPvrdXTz2rXJ9JhYGi3qwMrG /h7tW5BdVrZ33ikFI/8ceM2+6we8NdaYw6WPko8vrzo9HzuDoxDwesy3Lj3rbk4a MnKrUB9H2dlniXcDa9gmPcFUVmldr4/Diq4EYi7N017haFtKTsSmzZLJ1+ZR8lX/ AcKAufd9xxh6PBUIisCBCK/wm/R+aI89nsaCnaE+1GQf1XrPbiVXMNjDVn3KgcbQ Zrte4cCk4+TXsGgNnPXb -----END CERTIFICATE----- ================================================ FILE: vars/per-machine/porree/cert-prometheus/prometheus.fullchain.crt/value ================================================ -----BEGIN CERTIFICATE----- MIIFGzCCAwOgAwIBAgIUB/eKfajvUEO3Fg6Pwo8+A3HuZo0wDQYJKoZIhvcNAQEL BQAwFzEVMBMGA1UEAwwMQ2xhbiBSb290IENBMB4XDTI2MDQwODIyMDQ1NFoXDTI3 MDQwODIyMDQ1NFowGTEXMBUGA1UEAwwOcHJvbWV0aGV1cy5waW4wggIiMA0GCSqG SIb3DQEBAQUAA4ICDwAwggIKAoICAQC0p9V+S1YRa+KVJD14u53EqzsSYdN5xvQL AexgIMyO6JdWBnPHD+GuSKnHvx5OzYH9A0bXdmrQWW694JVwvzn0tj00Qro3YDnT j+WH9vKZCJW3Sp6BzPtCh+trzjl4sWA+fMhrdJKoO2F2vZN/1eOTGpDHYHjXKsaI HY1pJuVehFd6EDGTIzo6obykp6im3M15AlDWAU2aLZgBvKtOimRCZyFtVTs6vmF1 W+ALGV+uNOJwc75UF69FFCJB3AWa+xZDxCfN8xDbDs0KshXlwxh1UWQcATEcNmx5 GYgkEbUxPqj0S4IMskygPNTCNZvMgjVOiLTKmgxMcXb6YT5UtktZb1smmjd/iSvs PyLhrK3qs9pi/i9kiZrD5N17OceHUlAm+7PmlKybKGwndZUVqStrN+eHr8zkiXVE 12X5HPbxfISjkkXsFSJ1oSdSBxeeYCE5FKnTSIHLqVEuBDFoDMXmDg6uxfwI6jNF yVvsjTtiZAfYPbpw325JwPfMZBRVmvKuUya77vWK5DCn+7cAVuv9NsP2X5+ENNxi Imwg3NMh/NsuqdydQMBO1ubuGj9CFHb40wCrjVno0sFhjFUcx9DwxAowlSALFkFq gofW5OpMXSM1iqntI/p0k4NcND1FJYd0yZee5CUxkhk6quZpDMVhvqoBDO5dEwss Qc/WV/YbaQIDAQABo10wWzAZBgNVHREEEjAQgg5wcm9tZXRoZXVzLnBpbjAdBgNV HQ4EFgQUxI3Q4zH8y+TGlpr5ngdJYTUYRKIwHwYDVR0jBBgwFoAUpag/Tz/QYVTF EVgvNi9gh7ZKvc0wDQYJKoZIhvcNAQELBQADggIBAIYe7fap9MDJHyPVPyGwoG/0 XYjUMM1qB8pbVHeKhtaTFiiD3oeyOrI4TLGgkqNoIrcQ7m4xA50GE/DRCQpxQvsJ hQ0BBtCxu4JAW76W2q1auEH26sGSh5AMjcwkccbvhO8H0zwvuSSJEQvsCq6x2e9q vPuxqKJC/8Kvz3fJdQ0u4B/wDx1nSB4G4MTrCaOMBkyx8PPNoFHOWKNEPiBxKqHQ jbhs6+ggZ0+o+QvbivWJEgyyREsHuXwp7L5s0sqaq2hrQzmgkn/qiJaBV7sjVZU1 KblqImo89ZJL/4sZRi60TCh6GKEgxyMHGKdgh0/0iFfoRw4YKUz6IPlGSNc0j6zD p0DD+9YFgjue1Vre1tz5zaZMlRz1S38JOP3BF37BfIB9X8IKPwcSe4f7m5D9Die9 fjHn8XJoURWPOMjJ1C6hn9H3a8SHNEbG0bT1x6IXwPvrdXTz2rXJ9JhYGi3qwMrG /h7tW5BdVrZ33ikFI/8ceM2+6we8NdaYw6WPko8vrzo9HzuDoxDwesy3Lj3rbk4a MnKrUB9H2dlniXcDa9gmPcFUVmldr4/Diq4EYi7N017haFtKTsSmzZLJ1+ZR8lX/ AcKAufd9xxh6PBUIisCBCK/wm/R+aI89nsaCnaE+1GQf1XrPbiVXMNjDVn3KgcbQ Zrte4cCk4+TXsGgNnPXb -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIFDzCCAvegAwIBAgIUQjBDSURUCyly2/5c+Fw/iB+HcUAwDQYJKoZIhvcNAQEL BQAwFzEVMBMGA1UEAwwMQ2xhbiBSb290IENBMB4XDTI2MDIwNzE1MjkyNVoXDTM2 MDIwNTE1MjkyNVowFzEVMBMGA1UEAwwMQ2xhbiBSb290IENBMIICIjANBgkqhkiG 9w0BAQEFAAOCAg8AMIICCgKCAgEAwXHu+e90SAm7iqxXPenn+N7NfMM97rWpVkB8 X4g/2jQFfitN9M/CbbXYNPli010+nVzG/bzqBKrIybtIw+5TkCkU1BunRQzlAWxZ vOfX1bHj7qwn3q4d98h2wRJ1vkqt/0qf82g0AzmMfMZPRpAxoI4UlDQbVNLOGavg /usO20yfsdhCMAm2cNVS8MTYVO9xkgNiMUnDG2j8S/srZcrY8v21EG62atiM5I1k Y4tikfJSAYKzQL+OuvZjX+rrkpt084CzbKEHkNodz/+OmCqzRyMdR3j4dtMK1OSe +bH0tmp7JMR5nGOkhfVdrTE7VyOT1yIjRbRaneYnbrz4FUiO51jKdta08axXKcaJ afEd+CT4iWZfRCwVzf2SVhCXzk3KbR6IBM/UeQkIFA4k8YRQPWYmUkMe725f2y1t MQOFj/xbI36MzcQavbdLzpstBrbIidjWik5475clhx9NzsvHX9B6amBBUheKN4lE 6TstNK1uM3+053JQovHM9RS/Ff3AeFiOLf5+W+WL/0M6ue+h7PSe5w2KlYmhw0IR cblg3ixhA5CrjXktUURYJfDTaNqjDHuBoCUFwFjq6MowM8WttpViFDbN/5ZtZHMA 1n6dkLlSW1D0/TcvzAgmTQPq51KdEc9ek6/nwDRcM5OlAd8ohhQ9Ci6Jnm4t3Q0i BDzCYOcCAwEAAaNTMFEwHQYDVR0OBBYEFKWoP08/0GFUxRFYLzYvYIe2Sr3NMB8G A1UdIwQYMBaAFKWoP08/0GFUxRFYLzYvYIe2Sr3NMA8GA1UdEwEB/wQFMAMBAf8w DQYJKoZIhvcNAQELBQADggIBAGZKm92aAxwq2+9WZV3NDqPx3YDpTeiZ69UQF+up SsPZn7dAcL2fmMyYyIJ67OR5aiv+UH4+Z4Gp8vaP5y4PqHJKkXOhiSTx11oVNDV9 4He18FT55BVdHS6xdxnJVV19dFxz1whlv2fP/tiTAdihER0wNj/mJTQifcyUcBkD Rf6Qm+s1y1Ryu3lJp3yMff/7XBwdz2gg5dGp1+kYBYtRZnXPFfFvQy3uQvd8oqb3 y83EJS5Biz1ChfE9nydOzoTqNA+m+vKSvOfPpF5ZQ7UgRb855/eSORf1mRWvaD97 +OKRliXP37njHzzlBSfvi7RxfhLbR3dU6wRmGYOhnxzATXS7cFVp8ziNd3/oXLem K947ZsQaJsidyuq19MjQpeae9V8vp43fAB1i4TfzJC9KjgrZi9UUUKBmCHXyrH// lU4VBsQzaMvrQDUIdc+Cjl5+DlVleyL2kgJWm1EobgO9V7uHXi3GOl+9UyEaad8V ST7yg3pIDnrfmvQdBcJ9lA6xaaujVuHaddE+HlO2t+9iARPSdAdxDA/GQKAyDzni mO+SpoHu02ECdiUM8avICL0q7zIb/LdQHfsqnm7UU+Q4VAs13BkpaX0XK9TZoM80 ENFRyXIBJ4hP9+V57D0fA+46aDSZCJEKlcz7hj2R9Zl7EGJNaIcMe3CCcV2GXMYf GUo6 -----END CERTIFICATE----- ================================================ FILE: vars/per-machine/porree/cert-status/.validation-hash ================================================ a3ebf639b19ed4de7953512fa29f44a446f14f1d566791c1979e356c212dcb75 ================================================ FILE: vars/per-machine/porree/cert-status/status.crt/value ================================================ -----BEGIN CERTIFICATE----- MIIFEzCCAvugAwIBAgIUMmWSeM2DttIlig7T72VOoLtLhikwDQYJKoZIhvcNAQEL BQAwFzEVMBMGA1UEAwwMQ2xhbiBSb290IENBMB4XDTI2MDQwODIxMDcyN1oXDTI3 MDQwODIxMDcyN1owFTETMBEGA1UEAwwKc3RhdHVzLnBpbjCCAiIwDQYJKoZIhvcN AQEBBQADggIPADCCAgoCggIBAMdS1RImh07qq+BfzIGkWzYmXj6k1lNZouHX4JM8 KbzSNL30I8DJg7mqQ+3CyGD6Ze5B1IebGuHib/EKKMXOEPeMEuklh5uUJVUMz6LS VfJCDAbe9UEriqjrusOxqdio3aw5SlsEPOhIfgwJpXmdR72b5/ag5L9PVggH97X0 K30WW48+ACBwI4rJs4uJZkQhLakRBKLZOwrpt4TjbYuKqOH15jiwXIWgxYFoC2fq E3pgickNAHc823BXENmGjDCOlNfiXmbEtv/QLOooJF1vnP+JxAb1EjPgheW4YkvD znfxSnHJCmENKkX1uzvMsGmdGnOW+8PgGlzmFSUwy9HeY0g/eC+LXiOMYQJuUb/l G17sfXrA2K3xxrtna70ktX+Vnj1O6k3WBIIeos7O48PDo/POtVyyOtjCPm4a+qsI gqeIES/0Fbul8z2jVdZ6sQVbxSt0GEAHctjl937y5F7OdWKBLJRYjvyvIVYpeOeX YN10UJ5hU0hamBoZFEtMdzh6F+aS+vvAQoo1T6gS7SiLQ4oDNYhDbWYhmTTBPE4r BgV0EOsMLJR8eUN420Zjoli9UTXwjiXr+FNIEKcC6H2EZRWM1Y02Y87PQSWhGfud RvvCh8y7qIiewuATZkmNAtLqyy24o4eGL8ib6ys+j7m7/LhIOk00Ftil3bxTbGyQ C5GVAgMBAAGjWTBXMBUGA1UdEQQOMAyCCnN0YXR1cy5waW4wHQYDVR0OBBYEFE73 kHDe6fgKvK2maqpgl/rwmEHKMB8GA1UdIwQYMBaAFKWoP08/0GFUxRFYLzYvYIe2 Sr3NMA0GCSqGSIb3DQEBCwUAA4ICAQCHBNk2OGVJTKUjslB+/vSz2KoQzYVA6HKt 3ElDJBCig6r8chgyi8LeXg+dsX3CzLcUKWLidlxpGc+BY8P14mlaa3/16rL/0mTw 1WmDJdcciP17IHN2P1xNk8xSHIN2zTrrvRe1Nmkx0DQfYOnKXTIt4w4r0XyFs3SH 7k0Kkk669isch8eSLkt78dInC9jLYXg4sPNqV9tF9mh5X7dufUdchBA6IYJYpMtB zXmYD7u4+A7hiUnmsAnxjeEVa8tirGTQX250EBR9KKCEeASv6gJ98AjoP3kWyaHK Oaqt+OHkVCmvExmAib0Xt3+W52usSj0t52rJq9wZ1bWWaQWdUCHGkZBL59d1K8I5 zmAVrPOoiHNKaBARK466hOsayLH76huVG7H4TbtVKKw8zyEnbyvaACtY09RVi+Pm GWi5f/WwMMbYoI6hs7EtjsuWAj317vEwuTahaMpr1XaTcDlOoHOUhqkCJxhTTbf1 8v9T6h6o/GMiWisSVFL5MuVMI8yjvs39YeQOTVelO4SrNKLOvTAVw4Q873P6guT+ q7RtHgUcMOJPxw0+pICbsI2tIbLbOq+oAzqmXIFv7aadovrKjq66zF5wv1sLjiWU J3dJq87IDdgEfb8WsXOu37YW2SDVc4m6mdqmytYr6tU7pXz7FQACccR60F0sKS6J INwSwduKhg== -----END CERTIFICATE----- ================================================ FILE: vars/per-machine/porree/cert-status/status.fullchain.crt/value ================================================ -----BEGIN CERTIFICATE----- MIIFEzCCAvugAwIBAgIUMmWSeM2DttIlig7T72VOoLtLhikwDQYJKoZIhvcNAQEL BQAwFzEVMBMGA1UEAwwMQ2xhbiBSb290IENBMB4XDTI2MDQwODIxMDcyN1oXDTI3 MDQwODIxMDcyN1owFTETMBEGA1UEAwwKc3RhdHVzLnBpbjCCAiIwDQYJKoZIhvcN AQEBBQADggIPADCCAgoCggIBAMdS1RImh07qq+BfzIGkWzYmXj6k1lNZouHX4JM8 KbzSNL30I8DJg7mqQ+3CyGD6Ze5B1IebGuHib/EKKMXOEPeMEuklh5uUJVUMz6LS VfJCDAbe9UEriqjrusOxqdio3aw5SlsEPOhIfgwJpXmdR72b5/ag5L9PVggH97X0 K30WW48+ACBwI4rJs4uJZkQhLakRBKLZOwrpt4TjbYuKqOH15jiwXIWgxYFoC2fq E3pgickNAHc823BXENmGjDCOlNfiXmbEtv/QLOooJF1vnP+JxAb1EjPgheW4YkvD znfxSnHJCmENKkX1uzvMsGmdGnOW+8PgGlzmFSUwy9HeY0g/eC+LXiOMYQJuUb/l G17sfXrA2K3xxrtna70ktX+Vnj1O6k3WBIIeos7O48PDo/POtVyyOtjCPm4a+qsI gqeIES/0Fbul8z2jVdZ6sQVbxSt0GEAHctjl937y5F7OdWKBLJRYjvyvIVYpeOeX YN10UJ5hU0hamBoZFEtMdzh6F+aS+vvAQoo1T6gS7SiLQ4oDNYhDbWYhmTTBPE4r BgV0EOsMLJR8eUN420Zjoli9UTXwjiXr+FNIEKcC6H2EZRWM1Y02Y87PQSWhGfud RvvCh8y7qIiewuATZkmNAtLqyy24o4eGL8ib6ys+j7m7/LhIOk00Ftil3bxTbGyQ C5GVAgMBAAGjWTBXMBUGA1UdEQQOMAyCCnN0YXR1cy5waW4wHQYDVR0OBBYEFE73 kHDe6fgKvK2maqpgl/rwmEHKMB8GA1UdIwQYMBaAFKWoP08/0GFUxRFYLzYvYIe2 Sr3NMA0GCSqGSIb3DQEBCwUAA4ICAQCHBNk2OGVJTKUjslB+/vSz2KoQzYVA6HKt 3ElDJBCig6r8chgyi8LeXg+dsX3CzLcUKWLidlxpGc+BY8P14mlaa3/16rL/0mTw 1WmDJdcciP17IHN2P1xNk8xSHIN2zTrrvRe1Nmkx0DQfYOnKXTIt4w4r0XyFs3SH 7k0Kkk669isch8eSLkt78dInC9jLYXg4sPNqV9tF9mh5X7dufUdchBA6IYJYpMtB zXmYD7u4+A7hiUnmsAnxjeEVa8tirGTQX250EBR9KKCEeASv6gJ98AjoP3kWyaHK Oaqt+OHkVCmvExmAib0Xt3+W52usSj0t52rJq9wZ1bWWaQWdUCHGkZBL59d1K8I5 zmAVrPOoiHNKaBARK466hOsayLH76huVG7H4TbtVKKw8zyEnbyvaACtY09RVi+Pm GWi5f/WwMMbYoI6hs7EtjsuWAj317vEwuTahaMpr1XaTcDlOoHOUhqkCJxhTTbf1 8v9T6h6o/GMiWisSVFL5MuVMI8yjvs39YeQOTVelO4SrNKLOvTAVw4Q873P6guT+ q7RtHgUcMOJPxw0+pICbsI2tIbLbOq+oAzqmXIFv7aadovrKjq66zF5wv1sLjiWU J3dJq87IDdgEfb8WsXOu37YW2SDVc4m6mdqmytYr6tU7pXz7FQACccR60F0sKS6J INwSwduKhg== -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIFDzCCAvegAwIBAgIUQjBDSURUCyly2/5c+Fw/iB+HcUAwDQYJKoZIhvcNAQEL BQAwFzEVMBMGA1UEAwwMQ2xhbiBSb290IENBMB4XDTI2MDIwNzE1MjkyNVoXDTM2 MDIwNTE1MjkyNVowFzEVMBMGA1UEAwwMQ2xhbiBSb290IENBMIICIjANBgkqhkiG 9w0BAQEFAAOCAg8AMIICCgKCAgEAwXHu+e90SAm7iqxXPenn+N7NfMM97rWpVkB8 X4g/2jQFfitN9M/CbbXYNPli010+nVzG/bzqBKrIybtIw+5TkCkU1BunRQzlAWxZ vOfX1bHj7qwn3q4d98h2wRJ1vkqt/0qf82g0AzmMfMZPRpAxoI4UlDQbVNLOGavg /usO20yfsdhCMAm2cNVS8MTYVO9xkgNiMUnDG2j8S/srZcrY8v21EG62atiM5I1k Y4tikfJSAYKzQL+OuvZjX+rrkpt084CzbKEHkNodz/+OmCqzRyMdR3j4dtMK1OSe +bH0tmp7JMR5nGOkhfVdrTE7VyOT1yIjRbRaneYnbrz4FUiO51jKdta08axXKcaJ afEd+CT4iWZfRCwVzf2SVhCXzk3KbR6IBM/UeQkIFA4k8YRQPWYmUkMe725f2y1t MQOFj/xbI36MzcQavbdLzpstBrbIidjWik5475clhx9NzsvHX9B6amBBUheKN4lE 6TstNK1uM3+053JQovHM9RS/Ff3AeFiOLf5+W+WL/0M6ue+h7PSe5w2KlYmhw0IR cblg3ixhA5CrjXktUURYJfDTaNqjDHuBoCUFwFjq6MowM8WttpViFDbN/5ZtZHMA 1n6dkLlSW1D0/TcvzAgmTQPq51KdEc9ek6/nwDRcM5OlAd8ohhQ9Ci6Jnm4t3Q0i BDzCYOcCAwEAAaNTMFEwHQYDVR0OBBYEFKWoP08/0GFUxRFYLzYvYIe2Sr3NMB8G A1UdIwQYMBaAFKWoP08/0GFUxRFYLzYvYIe2Sr3NMA8GA1UdEwEB/wQFMAMBAf8w DQYJKoZIhvcNAQELBQADggIBAGZKm92aAxwq2+9WZV3NDqPx3YDpTeiZ69UQF+up SsPZn7dAcL2fmMyYyIJ67OR5aiv+UH4+Z4Gp8vaP5y4PqHJKkXOhiSTx11oVNDV9 4He18FT55BVdHS6xdxnJVV19dFxz1whlv2fP/tiTAdihER0wNj/mJTQifcyUcBkD Rf6Qm+s1y1Ryu3lJp3yMff/7XBwdz2gg5dGp1+kYBYtRZnXPFfFvQy3uQvd8oqb3 y83EJS5Biz1ChfE9nydOzoTqNA+m+vKSvOfPpF5ZQ7UgRb855/eSORf1mRWvaD97 +OKRliXP37njHzzlBSfvi7RxfhLbR3dU6wRmGYOhnxzATXS7cFVp8ziNd3/oXLem K947ZsQaJsidyuq19MjQpeae9V8vp43fAB1i4TfzJC9KjgrZi9UUUKBmCHXyrH// lU4VBsQzaMvrQDUIdc+Cjl5+DlVleyL2kgJWm1EobgO9V7uHXi3GOl+9UyEaad8V ST7yg3pIDnrfmvQdBcJ9lA6xaaujVuHaddE+HlO2t+9iARPSdAdxDA/GQKAyDzni mO+SpoHu02ECdiUM8avICL0q7zIb/LdQHfsqnm7UU+Q4VAs13BkpaX0XK9TZoM80 ENFRyXIBJ4hP9+V57D0fA+46aDSZCJEKlcz7hj2R9Zl7EGJNaIcMe3CCcV2GXMYf GUo6 -----END CERTIFICATE----- ================================================ FILE: vars/per-machine/porree/data-mesher-node-identity/identity.pub/value ================================================ -----BEGIN PUBLIC KEY----- MCowBQYDK2VwAyEAvQRxGpJCf8TRIi8Kl5SSuM6A6pu5t0jCdcutNWvh81U= -----END PUBLIC KEY----- ================================================ FILE: vars/per-machine/porree/data-mesher-node-identity/peer.id/value ================================================ 12D3KooWNYD9mk3j8e2KnnnQytStdsgEcVMHDDd39GnAkwnE4AtU ================================================ FILE: vars/per-machine/porree/dm-pull-deploy-status-key/signing.pub/value ================================================ -----BEGIN PUBLIC KEY----- MCowBQYDK2VwAyEAh6lOesBKMf3jisZPF/5rGKKEqn0YWBNJdrPZQuj1NGg= -----END PUBLIC KEY----- ================================================ FILE: vars/per-machine/porree/matrix-hook/.validation-hash ================================================ ad87e37f3326763c110d056e28f87471da0e88bb6891cfae314b43d38a79d214 ================================================ FILE: vars/per-machine/porree/opencrow/.validation-hash ================================================ b08299c38b79b4a8195a4fe2e0c204c793278a003f4210a0dc778304366c3666 ================================================ FILE: vars/per-machine/porree/opencrow-email/.validation-hash ================================================ bbeae7cc7a6c2e9e4c900505b7678737dcd4f5b02d6489439298e7bb5b6d0902 ================================================ FILE: vars/per-machine/porree/opencrow-eversports/.validation-hash ================================================ 5e33339ca7ab2f9fa413ff7a129654b234f7b80e2a5f523ce75535b5e0ca6116 ================================================ FILE: vars/per-machine/porree/opencrow-local/.validation-hash ================================================ 466f6a77204ea2ecea3cb9088ad26418fb61f819feb69a6b7a0574aa7919ae08 ================================================ FILE: vars/per-machine/porree/opencrow-nextcloud/.validation-hash ================================================ d877886ea3c25f02f8005f8e0c336a27e0b756ef09c1075707847b009070b3ec ================================================ FILE: vars/per-machine/porree/opencrow-nextcloud-work/.validation-hash ================================================ 77236f357fa2b9a3e9b633553abc4616a787272f3752388439197409aaf9dea7 ================================================ FILE: vars/per-machine/porree/restic-exporter/.validation-hash ================================================ 2281613666175c6d6f41a42c0c5136e29409c86f7821fa5e73be344cf9fd18d7 ================================================ FILE: vars/per-machine/porree/state-version/version/value ================================================ 20.03 ================================================ FILE: vars/per-machine/porree/tor_tor/hostname/value ================================================ av3w5wysdbsjvpqdokbk5hvslkajebi3oawjayzz2rwnqx27lynbalad.onion ================================================ FILE: vars/per-machine/porree/vaultwarden/.validation-hash ================================================ 763d277911fb0f2d246a13714dddc4f2e1a26c9f9850a96e39d21d77aa45345a ================================================ FILE: vars/per-machine/porree/wireguard/publickey/value ================================================ 9CSzhcAPiBTmH2cXvQ2f9oDE3TlTjCORyzUKDd2FaSA= ================================================ FILE: vars/per-machine/porree/wireguard-wg-clan/publickey/value ================================================ V7t8UAixnIJHSL2ygKJQ/9q2Ga31uzgTFrjAj/Oc/VM= ================================================ FILE: vars/per-machine/porree/wireguard-wg-clan-ip/ipv4/value ================================================ 192.168.8.1 ================================================ FILE: vars/per-machine/porree/wireguard-wg-star/ipv6/value ================================================ fda1:05c8:00::4c64:13e6:ade5:b27b ================================================ FILE: vars/per-machine/porree/wireguard-wg-star/publickey/value ================================================ tyeT5qTZxTa/CDmlVSDVr30lONoX52YwqBj9juV8nig= ================================================ FILE: vars/per-machine/porree/yggdrasil/address/value ================================================ 200:b393:48bf:71a9:e823:b1e3:7f7b:35b1 ================================================ FILE: vars/per-machine/porree/yggdrasil/publicKey/value ================================================ -----BEGIN PUBLIC KEY----- MCowBQYDK2VwAyEApjZboEcrC+4nDkBCZSdu5QUzGOURDqxfEoAYK2+yXPk= -----END PUBLIC KEY----- ================================================ FILE: vars/per-machine/tanne/data-mesher-node-identity/identity.pub/value ================================================ -----BEGIN PUBLIC KEY----- MCowBQYDK2VwAyEAiZ6WD14E8xKvoFm3POefj3R8kHBwXu/z6LsRBcSe5Wk= -----END PUBLIC KEY----- ================================================ FILE: vars/per-machine/tanne/data-mesher-node-identity/peer.id/value ================================================ 12D3KooWK5aH4qJEmHcBBbiFmVUmhsBuhMJtKxv9cRvLR8d1E4pG ================================================ FILE: vars/per-machine/tanne/dm-pull-deploy-status-key/signing.pub/value ================================================ -----BEGIN PUBLIC KEY----- MCowBQYDK2VwAyEAAS8EOkj6QymUn70aS/OceW3aptbdL7l/ye/IEikGFsY= -----END PUBLIC KEY----- ================================================ FILE: vars/per-machine/tanne/state-version/version/value ================================================ 25.11 ================================================ FILE: vars/per-machine/tanne/yggdrasil/address/value ================================================ 201:e616:fc1d:6f16:39b4:b222:b09:919a ================================================ FILE: vars/per-machine/tanne/yggdrasil/publicKey/value ================================================ -----BEGIN PUBLIC KEY----- MCowBQYDK2VwAyEARnpA+KQ6cZLTd309m5lQQBCl2Sgb7m8i+QOoc5/eGm8= -----END PUBLIC KEY----- ================================================ FILE: vars/per-machine/traube/data-mesher-node-identity/identity.pub/value ================================================ -----BEGIN PUBLIC KEY----- MCowBQYDK2VwAyEAt8oDdpITSE28hLV1f044+FqO0kLd5W2uYLrjDfjuLQw= -----END PUBLIC KEY----- ================================================ FILE: vars/per-machine/traube/data-mesher-node-identity/peer.id/value ================================================ 12D3KooWNBoSS2C8mX64NBjDiJb7bB2T799o8c16WdXe8x8s27QP ================================================ FILE: vars/per-machine/traube/dm-pull-deploy-status-key/signing.pub/value ================================================ -----BEGIN PUBLIC KEY----- MCowBQYDK2VwAyEAU8+DHWE2Pq9M8mP5TZrXp4O2VYAlkQbcD3Qa6VUw0ZM= -----END PUBLIC KEY----- ================================================ FILE: vars/per-machine/traube/opencrow/.validation-hash ================================================ b08299c38b79b4a8195a4fe2e0c204c793278a003f4210a0dc778304366c3666 ================================================ FILE: vars/per-machine/traube/opencrow-traube/.validation-hash ================================================ 466f6a77204ea2ecea3cb9088ad26418fb61f819feb69a6b7a0574aa7919ae08 ================================================ FILE: vars/per-machine/traube/state-version/version/value ================================================ 26.05 ================================================ FILE: vars/per-machine/traube/yggdrasil/address/value ================================================ 200:1fdd:ec44:6540:159c:1b73:ffff:af ================================================ FILE: vars/per-machine/traube/yggdrasil/publicKey/value ================================================ f01109ddcd5ff531f24600007fa816248498a57b27552449fa2f7dbd2c7f227e ================================================ FILE: vars/per-machine/tunnelmonster/abiotic-docker/.validation-hash ================================================ 767d345465b3e7b464d7915995eebcb802fde48ec6d68f2a98f5e85c6d521318 ================================================ FILE: vars/per-machine/tunnelmonster/porkbun-dns/.validation-hash ================================================ 89fe28a9686f5ad73bcaa8569c0a91dceeac2303dab6268e855544c62401df90 ================================================ FILE: vars/per-machine/tunnelmonster/state-version/version/value ================================================ 25.11 ================================================ FILE: vars/per-machine/uconsole/data-mesher-node-identity/identity.pub/value ================================================ -----BEGIN PUBLIC KEY----- MCowBQYDK2VwAyEAFIdLLWmFHcpY4lP2ksXRoUudfTNO/qvlEfk/gbMOvcY= -----END PUBLIC KEY----- ================================================ FILE: vars/per-machine/uconsole/data-mesher-node-identity/peer.id/value ================================================ 12D3KooWBCVydSSCb17yQG5MDakfX6pGWZCMx1rkQkuS4o968WZB ================================================ FILE: vars/per-machine/uconsole/dm-pull-deploy-status-key/signing.pub/value ================================================ -----BEGIN PUBLIC KEY----- MCowBQYDK2VwAyEANNGShgLvIVT84/KEacPe6cLJFBIXIQOaguKXA12WNoo= -----END PUBLIC KEY----- ================================================ FILE: vars/per-machine/uconsole/state-version/version/value ================================================ 26.05 ================================================ FILE: vars/per-machine/uconsole/wireguard-wg-clan/publickey/value ================================================ Cfdk+ZcKRgsBdlunDjJNDYdL/XWTnb9wr5U13TEbxws= ================================================ FILE: vars/per-machine/uconsole/wireguard-wg-clan-ip/ipv4/value ================================================ 192.168.8.7 ================================================ FILE: vars/per-machine/uconsole/yggdrasil/address/value ================================================ 201:63db:5f30:9e74:1e84:ef2f:a06b:5465 ================================================ FILE: vars/per-machine/uconsole/yggdrasil/publicKey/value ================================================ 67092833d862f85ec43417e52ae69843be0951ba70f7a4bbf0da92bec6662887 ================================================ FILE: vars/shared/bulletin-signing-key/signing.pub/value ================================================ -----BEGIN PUBLIC KEY----- MCowBQYDK2VwAyEAccTy8KpteT2Nb0XGQapmPGrIuQW3VZ6u5jQG0ERgMkU= -----END PUBLIC KEY----- ================================================ FILE: vars/shared/data-mesher-ca/ca.pub/value ================================================ -----BEGIN PUBLIC KEY----- MCowBQYDK2VwAyEA86WG8R6leepwlI1cXp10yiOw/sK7jZGPnVHkeX6CMl0= -----END PUBLIC KEY----- ================================================ FILE: vars/shared/data-mesher-network/network.pub/value ================================================ -----BEGIN PUBLIC KEY----- MCowBQYDK2VwAyEABv/K4aNwUgHG5KNr5vbqXADWDxUkRE7q3WepqQkMzQE= -----END PUBLIC KEY----- ================================================ FILE: vars/shared/data-mesher-network-key/public_key/value ================================================ -----BEGIN PUBLIC KEY----- MCowBQYDK2VwAyEAKv4CCHplPWe2IgqtDKeVeGLRxtIjdGZNrESDVbRwDFI= -----END PUBLIC KEY----- ================================================ FILE: vars/shared/data-mesher-signing-key/signing.pub/value ================================================ -----BEGIN PUBLIC KEY----- MCowBQYDK2VwAyEA8tcaE7z2zHmpEbTdicvOVy2zTGpAOcsyNeY2PAXkIww= -----END PUBLIC KEY----- ================================================ FILE: vars/shared/dm-dns/.validation-hash ================================================ 1428c04cb97c4f2294527292e10dea76847b5fb3598a3d5da8c9caad0ff05ee1 ================================================ FILE: vars/shared/dm-dns/zone.conf/value ================================================ local-zone: "pin." transparent local-data: "status.pin. CNAME porree.pin." local-data: "prometheus.pin. CNAME porree.pin." local-data: "irc.pin. CNAME kfbox.pin." ================================================ FILE: vars/shared/dm-dns-signing-key/signing.pub/value ================================================ -----BEGIN PUBLIC KEY----- MCowBQYDK2VwAyEAf5E1o5fdkm2LfOYwbhQtQ4duJONhhthxOH5Z+fo3lH0= -----END PUBLIC KEY----- ================================================ FILE: vars/shared/dm-pull-deploy-signing-key/signing.pub/value ================================================ -----BEGIN PUBLIC KEY----- MCowBQYDK2VwAyEAnJopkWCbSkcOIZydLrn5KKDPoY2DRY/8pMesPIRTdbU= -----END PUBLIC KEY----- ================================================ FILE: vars/shared/dm-wallpaper-signing-key/signing.pub/value ================================================ -----BEGIN PUBLIC KEY----- MCowBQYDK2VwAyEATpRyueno7oq68F/14v5TfT8GSf2vcnloaJbd/UMD9sw= -----END PUBLIC KEY----- ================================================ FILE: vars/shared/dm-wg-star-wg-star-signing-key/signing.pub/value ================================================ -----BEGIN PUBLIC KEY----- MCowBQYDK2VwAyEAK7yaemgaNxay7QLucIuBSnyZo/oeuk2n4Iq/JdCZGsU= -----END PUBLIC KEY----- ================================================ FILE: vars/shared/dns-mesher/.validation-hash ================================================ 5ac66cf8dd4bda704331135b1369acea19e581b68bfa87cf1e15b908915c6382 ================================================ FILE: vars/shared/dns-mesher/entries/value ================================================ otherservice birne exampleservice kiwi ================================================ FILE: vars/shared/dns-mesher/zone.conf/value ================================================ local-zone: "pin." transparent local-data: "music.pin. CNAME kfbox.pin." ================================================ FILE: vars/shared/pki-root-ca/ca.crt/value ================================================ -----BEGIN CERTIFICATE----- MIIFDzCCAvegAwIBAgIUQjBDSURUCyly2/5c+Fw/iB+HcUAwDQYJKoZIhvcNAQEL BQAwFzEVMBMGA1UEAwwMQ2xhbiBSb290IENBMB4XDTI2MDIwNzE1MjkyNVoXDTM2 MDIwNTE1MjkyNVowFzEVMBMGA1UEAwwMQ2xhbiBSb290IENBMIICIjANBgkqhkiG 9w0BAQEFAAOCAg8AMIICCgKCAgEAwXHu+e90SAm7iqxXPenn+N7NfMM97rWpVkB8 X4g/2jQFfitN9M/CbbXYNPli010+nVzG/bzqBKrIybtIw+5TkCkU1BunRQzlAWxZ vOfX1bHj7qwn3q4d98h2wRJ1vkqt/0qf82g0AzmMfMZPRpAxoI4UlDQbVNLOGavg /usO20yfsdhCMAm2cNVS8MTYVO9xkgNiMUnDG2j8S/srZcrY8v21EG62atiM5I1k Y4tikfJSAYKzQL+OuvZjX+rrkpt084CzbKEHkNodz/+OmCqzRyMdR3j4dtMK1OSe +bH0tmp7JMR5nGOkhfVdrTE7VyOT1yIjRbRaneYnbrz4FUiO51jKdta08axXKcaJ afEd+CT4iWZfRCwVzf2SVhCXzk3KbR6IBM/UeQkIFA4k8YRQPWYmUkMe725f2y1t MQOFj/xbI36MzcQavbdLzpstBrbIidjWik5475clhx9NzsvHX9B6amBBUheKN4lE 6TstNK1uM3+053JQovHM9RS/Ff3AeFiOLf5+W+WL/0M6ue+h7PSe5w2KlYmhw0IR cblg3ixhA5CrjXktUURYJfDTaNqjDHuBoCUFwFjq6MowM8WttpViFDbN/5ZtZHMA 1n6dkLlSW1D0/TcvzAgmTQPq51KdEc9ek6/nwDRcM5OlAd8ohhQ9Ci6Jnm4t3Q0i BDzCYOcCAwEAAaNTMFEwHQYDVR0OBBYEFKWoP08/0GFUxRFYLzYvYIe2Sr3NMB8G A1UdIwQYMBaAFKWoP08/0GFUxRFYLzYvYIe2Sr3NMA8GA1UdEwEB/wQFMAMBAf8w DQYJKoZIhvcNAQELBQADggIBAGZKm92aAxwq2+9WZV3NDqPx3YDpTeiZ69UQF+up SsPZn7dAcL2fmMyYyIJ67OR5aiv+UH4+Z4Gp8vaP5y4PqHJKkXOhiSTx11oVNDV9 4He18FT55BVdHS6xdxnJVV19dFxz1whlv2fP/tiTAdihER0wNj/mJTQifcyUcBkD Rf6Qm+s1y1Ryu3lJp3yMff/7XBwdz2gg5dGp1+kYBYtRZnXPFfFvQy3uQvd8oqb3 y83EJS5Biz1ChfE9nydOzoTqNA+m+vKSvOfPpF5ZQ7UgRb855/eSORf1mRWvaD97 +OKRliXP37njHzzlBSfvi7RxfhLbR3dU6wRmGYOhnxzATXS7cFVp8ziNd3/oXLem K947ZsQaJsidyuq19MjQpeae9V8vp43fAB1i4TfzJC9KjgrZi9UUUKBmCHXyrH// lU4VBsQzaMvrQDUIdc+Cjl5+DlVleyL2kgJWm1EobgO9V7uHXi3GOl+9UyEaad8V ST7yg3pIDnrfmvQdBcJ9lA6xaaujVuHaddE+HlO2t+9iARPSdAdxDA/GQKAyDzni mO+SpoHu02ECdiUM8avICL0q7zIb/LdQHfsqnm7UU+Q4VAs13BkpaX0XK9TZoM80 ENFRyXIBJ4hP9+V57D0fA+46aDSZCJEKlcz7hj2R9Zl7EGJNaIcMe3CCcV2GXMYf GUo6 -----END CERTIFICATE----- ================================================ FILE: vars/shared/restic-cert/.validation-hash ================================================ a3da374e9cad9051dce842a991c18e71feb879b073bca0f4ef291cb33c01a0a7 ================================================ FILE: vars/shared/restic-cert/restic-cert/value ================================================ -----BEGIN CERTIFICATE----- MIIDgDCCAmigAwIBAgIUDBp2agTh90M4T0oU59HTKguVW+QwDQYJKoZIhvcNAQEL BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yNTAxMjIxMzE3MTlaFw0yNTAy MjExMzE3MTlaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEB AQUAA4IBDwAwggEKAoIBAQDLq1xdCjYpFoKoNTde+ca+Av3Q2aeeE1iug+GO7mAi R4WXUlIxBncwvzAqlQBEixsPWsYvL/MND570NvzGfUmfY204fyMb46sMOBAL0Rfi /gVaVXLErTGJrOGPUdZgWpdLAATbwMVLqUawKM66lcr+A+ftxUjyosTPpQwrq/Zv arlORTW4Ekzwo9nX9VQ+ZzoRlokay1H/Nzd3no8lY16IKxleuif+l0O96NsUFgZL VFvZHsGBwn5PfWxXvUDqdRjh3QJ3gbdlFq6+B+znsgkj38jW7YdvPUKYYojXEgk/ aAaii33RB4SjyCFeaO9QSTxQC3z8dxG+R9HXfVqOMD2pAgMBAAGjaDBmMB0GA1Ud DgQWBBQuz8nhTizE/kt8ePJzj4s8f3BXgDAfBgNVHSMEGDAWgBQuz8nhTizE/kt8 ePJzj4s8f3BXgDAPBgNVHRMBAf8EBTADAQH/MBMGA1UdEQQMMAqCCCoucmVzdGlj MA0GCSqGSIb3DQEBCwUAA4IBAQB5knuIq6d3EjfBoiCDJwHFVIjgaWqo7g3Z1Rg7 GFe4s4HZYMHQcmKFaKDMiqEENstMopUO/iT8dmmeNEIXMuofAVdfd2PJri0hMmCo jcpswgtOtCHqSE0mALV2R/tEUt0nzSTRbmH1PfDlYfvt8y00kyQLqE8qQwjx+1Pd ldlxK3b6qZqvfOZzwhvYhRDYON6UFY6u/qc8mF5Qr5qFnzoasz0XDhlhhz5ogvst I4Wn9Kb9SxKPL42t5jQVinAT+KZ0x3/cIgl20qVOdiQdCx57p3ZdgDru5g6BOyGk CZ7HRSaXpO8dgDZ5bmR3NNlNwvA2TRT0oLcBR3y1tohbp9Ye -----END CERTIFICATE----- ================================================ FILE: vars/shared/restic-credentials/.validation-hash ================================================ 406e5d73f5915d7762fdbbe0dfafb7b37fd5ad1f0e7c47895a7279180d88b810 ================================================ FILE: vars/shared/restic-credentials-backblaze/.validation-hash ================================================ 406e5d73f5915d7762fdbbe0dfafb7b37fd5ad1f0e7c47895a7279180d88b810 ================================================ FILE: vars/shared/restic-kfbox/.validation-hash ================================================ 4bfe1766afd037ef15eeeaa180acb87117e957d87631f545782b5f69a1657e79 ================================================ FILE: vars/shared/sftp-credentials/ssh-public-key/value ================================================ ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIP7VcxqV5v9vAd4ebHeZew/eOrDVoLwB/r6BMSkG/h0s seed-inbox@kiwi ================================================ FILE: vars/shared/step-ca/ca.crt/value ================================================ -----BEGIN CERTIFICATE----- MIIBcDCCARegAwIBAgIQTe4Ng6fJCiNj5//dN4M5CDAKBggqhkjOPQQDAjAXMRUw EwYDVQQDEwxDbGFuIFJvb3QgQ0EwHhcNMjUwOTAyMDEwNTA2WhcNMjYwOTAyMTMw NTA2WjAXMRUwEwYDVQQDEwxDbGFuIFJvb3QgQ0EwWTATBgcqhkjOPQIBBggqhkjO PQMBBwNCAARa5QaVes9sLb8FDppziGLYslExaSFyOI5PhQDR+aFLyKFi3qrJcNIg xcvKTPIF7dNnmRXeaOf5lV7EKrJnujj+o0UwQzAOBgNVHQ8BAf8EBAMCAQYwEgYD VR0TAQH/BAgwBgEB/wIBATAdBgNVHQ4EFgQUXm7vDWvMgJugl2NPa5HcUyY4Zdcw CgYIKoZIzj0EAwIDRwAwRAIgAPGLorxuaJa/aJ8gWWeej8gPBhXuVNrTt4Gojaao ybkCIEX2JVMs5GVmGUiu1igA/x40H6+GtUSHIdOqDWWQgu7M -----END CERTIFICATE----- ================================================ FILE: vars/shared/storagebox-ssh/ssh-public-key/value ================================================ ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHhbO2XmZHSfHUPGYWG4RV93WF3L8/g1y2oIiNn+859r kiwi-storagebox ================================================ FILE: vars/shared/zerotier-controller/zerotier-ip/value ================================================ fd0c:cb9f:98da:6865:9c99:930c:cb9f:98da ================================================ FILE: vars/shared/zerotier-controller/zerotier-network-id/value ================================================ 0ccb9f98da68659c