Skip to content

franzos/panther

Repository files navigation

My guix channel "panther"

This repository contains GUIX package defintions maintained primarily by Franz Geffke.

Channel Definition

(cons* (channel
        (name 'pantherx)
        (url "https://codeberg.org/gofranz/panther.git")
        ;; Enable signature verification
        (introduction
         (make-channel-introduction
          "54b4056ac571611892c743b65f4c47dc298c49da"
          (openpgp-fingerprint
           "A36A D41E ECC7 A871 1003  5D24 524F EB1A 9D33 C9CB"))))
       %default-channels)

Substitute Server

URL: https://substitutes.guix.gofranz.com

;; Public key
(public-key
 (ecc
  (curve Ed25519)
  (q #0096373009D945F86C75DFE96FC2D21E2F82BA8264CB69180AA4F9D3C45BAA47#)))
# Authorize
sudo guix archive --authorize < /path/to/key.pub

Already configured if using %os-base-services or %os-desktop-services.

Home Services

Darkman

Darkman is a framework for managing dark/light mode transitions. It automatically switches themes based on sunrise/sunset times.

Usage:

(use-modules (px home services darkman))

;; Default configuration (uses geoclue2 for location)
(service home-darkman-service-type)

;; With manual coordinates
(service home-darkman-service-type
         (home-darkman-configuration
          (latitude 52.52)
          (longitude 13.405)
          (use-geoclue? #f)))

;; Custom configuration
(service home-darkman-service-type
         (home-darkman-configuration
          (latitude 37.7749)
          (longitude -122.4194)
          (use-geoclue? #f)
          (dbus-server? #t)
          (portal? #f)))

Mode-switching scripts:

Place executable scripts in:

  • ~/.local/share/dark-mode.d/ - Executed when switching to dark mode
  • ~/.local/share/light-mode.d/ - Executed when switching to light mode

Manual control:

darkman get          # Show current mode
darkman set dark     # Switch to dark mode
darkman set light    # Switch to light mode
darkman toggle       # Toggle between modes

Foot Server

Foot server mode runs the foot terminal emulator as a background daemon, allowing fast terminal startup with footclient.

Usage:

(use-modules (px home services foot))

;; Default configuration
(service home-foot-server-service-type)

;; With custom config file
(service home-foot-server-service-type
         (home-foot-server-configuration
          (config-file "/path/to/foot.ini")))

;; With hold option (remain open after child exits)
(service home-foot-server-service-type
         (home-foot-server-configuration
          (hold? #t)))

Connecting to server:

footclient           # Open new terminal window
footclient -- htop   # Open terminal running specific command

Service management:

herd start foot-server    # Start server
herd stop foot-server     # Stop server
herd status foot-server   # Check status

Unattended Upgrade

Periodically runs guix pull followed by guix home reconfigure. Drop-in home equivalent of the system unattended-upgrade-service-type with battery awareness — upgrades are skipped when the laptop is on battery power.

Usage:

(use-modules (px home services unattended-upgrade))

;; Minimal configuration (config-file is required)
(service home-unattended-upgrade-service-type
         (home-unattended-upgrade-configuration
          (config-file "/home/user/dotfiles/home/home.scm")
          (channels #~
                    (cons* (channel
                            (name 'my-channel)
                            (url "https://example.com/channel.git"))
                           %default-channels))))

;; Full configuration
(service home-unattended-upgrade-service-type
         (home-unattended-upgrade-configuration
          (config-file "/home/user/dotfiles/home/home.scm")
          (skip-on-battery? #t)
          (schedule "0 19 * * *")
          (channels #~ %default-channels)))

Configuration options:

Field Default Description
config-file (required) Path to home.scm configuration file
channels %default-channels Gexp producing a list of channels for guix pull -C
schedule "0 19 * * *" Cron schedule string
system-expiration 90 days Max age of home generations before deletion
maximum-duration 3600 Max seconds the upgrade may run
skip-on-battery? #f Skip upgrade when on battery power
log-file ~/.local/state/unattended-home-upgrade.log Log file path
warm-packages '() List of package names to guix build after reconfigure

Service management:

herd status unattended-home-upgrade   # Check status
herd trigger unattended-home-upgrade  # Trigger upgrade now

Podman Healthcheckd

Runs podman container healthchecks on systems without systemd.

Usage:

(use-modules (px services containers))

;; Default configuration
(service home-podman-healthcheckd-service-type)

;; With custom log level
(service home-podman-healthcheckd-service-type
         (home-podman-healthcheckd-configuration
          (log-level "debug")))

Service management:

herd start podman-healthcheckd    # Start daemon
herd stop podman-healthcheckd     # Stop daemon
herd status podman-healthcheckd   # Check status

System Services

Unattended Upgrade

Drop-in replacement for (gnu services admin) unattended-upgrade-service-type with battery awareness. All upstream fields are preserved; additions are skip-on-battery? and system-load-paths.

Usage:

(use-modules (px services unattended-upgrade))

(service unattended-upgrade-service-type
         (unattended-upgrade-configuration
          (schedule "0 17 * * *")
          (skip-on-battery? #t)
          (system-load-paths '("/home/user/dotfiles/system"))
          (channels #~
                    (cons* (channel
                            (name 'my-channel)
                            (url "https://example.com/channel.git"))
                           %default-channels))))

Additional configuration options:

Field Default Description
skip-on-battery? #f Skip upgrade when on battery power
system-load-paths '() Extra -L load paths for guix system reconfigure

All other fields (operating-system-file, schedule, channels, reboot?, services-to-restart, system-expiration, maximum-duration, log-file) match the upstream (gnu services admin) defaults.

Caveat on system-load-paths: Guix only stores the top-level configuration file in the store (/run/current-system/configuration.scm), not its imported modules. If your config imports local modules (e.g. (common)) that live outside a channel, you need system-load-paths so the unattended upgrade can find them. These modules are resolved from disk at upgrade time — not from a stored snapshot — so they should be kept in sync with your configuration.

Battery detection: Reads /sys/class/power_supply/*/type to locate AC adapters and checks their online status via sysfs. Desktops without battery info proceed normally.

Chrony

Runs chronyd, the NTP daemon from the Chrony project. Keeps the system clock in sync with the configured time servers. The service creates a dedicated chrony:chrony system user and the drift directory at /var/lib/chrony — no manual setup required.

Default configuration uses NTS (RFC 8915) to authenticate time packets via TLS, preventing on-path attackers from forging NTP responses. The default sources are a geographically diverse mix of Stratum 1 servers:

server time.cloudflare.com iburst nts
server nts.netnod.se iburst nts
server ptbtime1.ptb.de iburst nts
server ptbtime2.ptb.de iburst nts
server ntppool1.time.nl iburst nts
driftfile /var/lib/chrony/drift
ntsdumpdir /var/lib/chrony
makestep 1.0 3
rtcsync

ntsdumpdir caches NTS cookies across restarts so the TLS handshake isn't repeated on every boot. There is currently no NTS pool — TLS certificates break the traditional pool.ntp.org pooling model, so sources are listed individually.

Firewall requirement: NTS needs outbound TCP/4460 (NTS-KE handshake) in addition to the usual UDP/123 (NTP). If TCP/4460 is blocked, chronyc -N authdata will show zeros in the KeyID/Type/KLen columns and the sources will never come up.

First-boot caveat: NTS certificate validation requires a roughly-correct clock. If the RTC is badly wrong, the TLS handshake will fail and chronyd won't be able to bootstrap. On fresh systems with unreliable RTCs, temporarily add an unauthenticated pool 2.pool.ntp.org iburst line until the clock is close enough for TLS to work.

Usage:

(use-modules (px services ntp))

;; Default — five NTS-enabled sources (see above)
(service chrony-service-type)

;; With a custom chrony.conf
(service chrony-service-type
         (chrony-service-configuration
          (config "server time.cloudflare.com iburst nts
server ptbtime1.ptb.de iburst nts
driftfile /var/lib/chrony/drift
ntsdumpdir /var/lib/chrony
makestep 1.0 3
rtcsync
")))

Configuration options:

Field Default Description
package chrony The chrony package to use
config Five NTS sources + driftfile / ntsdumpdir / makestep / rtcsync Raw chrony.conf contents

Service management:

sudo herd status chrony          # Check status
sudo herd configuration chrony   # Print path to generated chrony.conf
sudo chronyc sources             # Show NTP sources and reachability
sudo chronyc tracking            # Show clock sync status
sudo chronyc -N authdata         # Verify NTS: KeyID/Type/KLen should be non-zero

IOTA Node

Run an IOTA full node or validator node.

Prerequisites:

  1. Create configuration file (e.g., /etc/iota/fullnode.yaml)
  2. Download genesis blob for your network (mainnet/testnet/devnet)
  3. For validators: generate key pairs

Usage:

(use-modules (px services iota))

;; Basic full node
(service iota-node-service-type
         (iota-node-configuration
          (config-file "/etc/iota/fullnode.yaml")))

;; With custom settings
(service iota-node-service-type
         (iota-node-configuration
          (config-file "/etc/iota/validator.yaml")
          (data-directory "/var/lib/iota")
          (log-file "/var/log/iota-node.log")
          (log-level "info,iota_core=debug,consensus=debug")))

Service management:

herd status iota-node   # Check status
herd start iota-node    # Start node
herd stop iota-node     # Stop node

Configuration options:

Field Default Description
config-file (required) Path to fullnode.yaml or validator.yaml
user "iota" System user to run as
group "iota" System group
data-directory "/var/lib/iota" Database and state storage
log-file "/var/log/iota-node.log" Log output location
log-level "info,iota_core=debug,..." Rust log levels

Required ports:

  • TCP/9000 - JSON-RPC
  • UDP/8084 - P2P sync

RealtimeKit

RealtimeKit grants real-time scheduling to user processes on request. Required by PipeWire and PulseAudio for low-latency audio.

Usage:

(use-modules (px services audio))

(service rtkit-daemon-service-type)

Tailscale

Tailscale is a zero-config VPN built on WireGuard.

Usage:

(use-modules (px services networking))

(service tailscale-service-type)

After reconfiguration:

tailscale up         # Authenticate and connect
tailscale status     # Check connection status

USBGuard

Runs usbguard-daemon to enforce a USB device authorization policy — a whitelist for USB devices that blocks BadUSB-style attacks. The generated usbguard-daemon.conf lives in the store; rules are kept at /etc/usbguard/rules.conf so they can be updated without a reconfigure.

Usage:

(use-modules (px services usbguard))

;; Default: block everything not explicitly allowed, only root can use IPC
(service usbguard-service-type)

;; Allow members of the 'usbguard' group to manage rules via the CLI
(service usbguard-service-type
         (usbguard-configuration
          (ipc-allowed-groups '("usbguard"))))

Add yourself to the usbguard group (created by the service) to use the CLI without sudo.

Managing rules without reconfiguring:

# Via the CLI — daemon persists changes to /etc/usbguard/rules.conf
sudo usbguard list-devices
sudo usbguard allow-device <id> -p       # -p = permanent
sudo usbguard append-rule 'allow id 1d6b:0002'

# Or edit the file directly
sudo $EDITOR /etc/usbguard/rules.conf
sudo herd restart usbguard

Changing fields in usbguard-configuration (policy targets, IPC allow-lists, audit settings) does require guix system reconfigure — those are baked into the store config.

Configuration options:

Field Default Description
package usbguard The usbguard package to use
rule-file "/etc/usbguard/rules.conf" Persistent rules file
rule-folder "/etc/usbguard/rules.d/" Directory of additional rule files
implicit-policy-target 'block Action for devices not matching any rule ('allow, 'block, 'reject)
present-device-policy 'apply-policy How to treat devices already connected at daemon start
present-controller-policy 'keep Same, for USB controllers
inserted-device-policy 'apply-policy How to treat newly-inserted devices
authorized-default 'none Default authorization for new devices ('none, 'all, 'keep, 'internal)
device-manager-backend 'uevent Backend ('uevent or 'umockdev)
ipc-allowed-users '("root") Users permitted to use the IPC interface
ipc-allowed-groups '() Groups permitted to use the IPC interface
audit-backend 'FileAudit 'FileAudit or 'LinuxAudit
audit-file-path "/var/log/usbguard/usbguard-audit.log" Audit log location
hide-pii? #f Strip serial numbers and descriptor hashes from audit entries
log-file "/var/log/usbguard.log" Shepherd log file
auto-start? #t Start the daemon automatically at boot

Service management:

herd status usbguard    # Check status
herd restart usbguard   # Reload after editing rules.conf by hand

Hardening: The daemon is launched with -C (drop capabilities after startup) and -W (seccomp syscall allowlist). The D-Bus configuration and Polkit action from the usbguard package are registered automatically, so usbguard-dbus and desktop front-ends work without extra wiring.

System Configuration

This channel provides pre-configured building blocks for Guix system definitions. Import with:

(use-modules (px system os))

Packages

Variable Description
%os-base-packages Extends %base-packages with wpa-supplicant, libimobiledevice, neovim

Services

Variable Description
%os-base-services Extends %base-services with panther channel and substitute servers
%os-desktop-services Extends %desktop-services with panther channel and substitute servers
%os-desktop-services-minimal Desktop services without login/display managers and audio (for custom greeter setups)

Operating Systems

Variable Description
%os-base Minimal OS with %os-base-services and %os-base-packages

When to Use What

  • Headless server: Use %os-base directly or inherit from it
  • Desktop with GDM/SDDM: Inherit %os-base and use %os-desktop-services
  • Desktop with custom greeter (greetd, etc.): Use %os-desktop-services-minimal to avoid conflicts

Usage

Inherit from an OS definition and customize:

(operating-system
  (inherit %os-base)
  (host-name "my-workstation")
  (timezone "Europe/Berlin")
  ;; Add your file-systems, users, etc.
  (services
   (cons* (service openssh-service-type)
          %os-base-services)))

Or use just the services/packages in your own OS:

(operating-system
  ;; ...your configuration...
  (packages
   (cons* my-extra-package
          %os-base-packages))
  (services
   (modify-services %os-desktop-services-minimal
     (elogind-service-type config =>
       (elogind-configuration
         (inherit config)
         (handle-lid-switch 'suspend))))))

Time Travel

When things break because of upstream changes, this will allow you to run a future guix commit, to fix and test the channel without updating the whole system.

Create a channels file that includes only the guix channel:

(list (channel
        (name 'guix)
        (url "https://codeberg.org/guix/guix.git")
        (branch "master")
        ;; Specify commit
        (commit "dc1a77267f03e37b331c8597b066c5ee52a75445")
        (introduction
          (make-channel-introduction
            "9edb3f66fd807b096b48283debdcddccfea34bad"
            (openpgp-fingerprint
              "BBB0 2DDF 2CEA F6A8 0D1D  E643 A2A0 6DF2 A33A 54FA")))))

Spawn a shell for a clean environment:

guix shell --container --nesting --network openssl nss-certs coreutils guix

And build the target package:

guix time-machine --channels=default-channel.scm -- build -L panther pimsync

Known Issues

  • Channel modules shadowed by system profile (bug #74396): After guix pull, new package versions may not be available until you also run guix system reconfigure. Workaround: keep system and user guix in sync, or use guix time-machine -C ~/.config/guix/channels.scm -- shell <package>.

Upstream

Packages here may be upstreamed to Guix if they meet Guix's free software requirements. Please include me in the copyright notice if you do.

About

[Mirror] My guix channel "panther"

Topics

Resources

License

Stars

Watchers

Forks

Contributors

Languages