Skip to content

dangirsh/neurosys

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

neurosys

About

My neurosys encompasses configuration for:

These define a my computing environment, which strives to be

  • Effective: interactions with code/data should occur at the speed of thought.
  • Extensible: otherwise, it’s not really mine.
  • Long-lived: resist entropy/bitrot/bankruptcy until the end of the keyboard + screen era.
  • Minimal: less is more for the base configuration - Nix enables project-level dependency management.
  • Reproducible: enabled by the content-based hashes of Nix + Git. No fear of hitting un-recoverable states.

This literate org file, along with another for Doom, are the only two source files. The rest are generated via tangling.

If you are viewing this in a browser, consider opening the original org file in Emacs for the full experience. The HTML version is an imperfect rendering.

The name neurosys is used half-jokingly.

Inspirations

In no particular order, I found the following configurations especially helpful:

Helpful Resources

Table of Contents

Host setup

Install NixOS

Followed: https://www.linode.com/docs/tools-reference/custom-kernels-distros/install-nixos-on-linode/

Add channels

Stable

NIXOS_VERSION=20.03

nix-channel --add "https://nixos.org/channels/nixos-${NIXOS_VERSION}" nixos
nix-channel --add "https://github.com/rycee/home-manager/archive/release-${NIXOS_VERSION}.tar.gz" home-manager
nix-channel --add "https://nixos.org/channels/nixpkgs-${NIXOS_VERSION}" nixpkgs

nix-channel update

Unstable

nix-channel --add https://github.com/rycee/home-manager/archive/master.tar.gz home-manager
nix-channel --add https://nixos.org/channels/nixos-unstable nixos
nix-channel --add "https://nixos.org/channels/nixpkgs-unstable" nixpkgs-unstable

nix-channel update

Deployment

Get the neurosys source

Don’t forget to clone recursively, because of the submodules:

git clone --recursive git@github.com:dangirsh/neurosys.git

Sync to host

Run neurosys/deploy-to-host, which will tangle this file and rsync the results to a specified host.

Rsync Script

HOST=$1
HOST_HOME=$2

rsync -Pav --rsync-path="sudo rsync" nixos/ $HOST:/etc/nixos/
rsync -Pav home/ $HOST:$HOST_HOME

Doom Emacs

My Doom Emacs Configuration is tracked in the home folder as a git submodule.

Until projects like nix-doom-emacs are stable, I’m not yet tracking my Emacs packages / config in Nix. For now, I track known-good commits via submodules / straight.el, and tie them to external dependencies (all managed by Nix) in this repo. If you know a better way to do this, please let me know.

Emacs itself is tracked via the emacs-overlay, which is version pinned via niv in sources.json. See the nix delaration.

XMonad

I use XMonad as a window manager and minimal desktop environment. I don’t run any additional desktop environment (e.g. XFCE). Instead, I have the interface to the few things I need configured here in Haskell, or elsewhere (Emacs).

I’ve don’t use any system trays / status bars / panels, since the Emacs modeline is enough for me. This choice reduces the complexity of the XMonad configuration, and avoids depending on xmobar/polybar.

Haskell Configuration

Imports

import XMonad

import XMonad.Hooks.SetWMName
import XMonad.Hooks.DynamicProperty (dynamicTitle)
import XMonad.Hooks.EwmhDesktops
import XMonad.Hooks.ManageHelpers
import XMonad.Hooks.UrgencyHook

import XMonad.Layout.Grid
import XMonad.Layout.Fullscreen
import XMonad.Layout.Minimize
import XMonad.Actions.Minimize
import XMonad.Layout.NoBorders
import XMonad.Layout.NoFrillsDecoration (noFrillsDeco, shrinkText,
                                         inactiveBorderColor, inactiveColor, inactiveTextColor, activeBorderColor,
                                         activeColor, activeTextColor, urgentBorderColor, urgentTextColor, decoHeight)
import XMonad.Layout.Tabbed (simpleTabbed)
import XMonad.Layout.ResizableTile
import XMonad.Layout.MultiColumns

import XMonad.Actions.CycleWS (toggleWS)
import XMonad.Actions.CycleRecentWS (cycleRecentWS)
import qualified XMonad.StackSet as W

import XMonad.Prompt
import XMonad.Prompt.AppLauncher as AL
import XMonad.Util.Run

import Data.Monoid
import Data.Default (def)
import Data.Map as M (fromList,union, Map())
import Data.List (isPrefixOf)

Main

main :: IO ()
main = xmonad $
  withUrgencyHook NoUrgencyHook $
  ewmh $
  fullscreenSupport def {
    borderWidth = 1
  , focusedBorderColor = blue
  , terminal = "alacritty"
  , layoutHook = smartBorders $  -- no borders for sole windows
                 noFrillsDeco shrinkText topBarTheme $   -- visually mark the focused window with a top bar
                 minimize
                 (ResizableTall 1 (3/100) (1/2) []
                   ||| Mirror (ResizableTall 1 (3/100) (1/2) [])
                   ||| multiCol [1] 1 0.01 (-0.5)
                   ||| noBorders Full
                   ||| simpleTabbed
                   ||| Grid)

  , workspaces = map show $ [1..9] ++ [0 :: Int]
  , modMask = mod4Mask  -- super key as modifier
  , keys = \c -> myKeys c `M.union` keys def c
  , manageHook = myManageHook <+> manageZoomHook
  , handleEventHook = ewmhDesktopsEventHook <+> myHandleEventHook
  , startupHook = do
      -- http://hackage.haskell.org/package/xmonad-contrib-0.16/docs/XMonad-Hooks-SetWMName.html
      setWMName "LG3D"
      windows $ W.greedyView "1"
  }

Keybindings

Default

From https://xmonad.org/manpage.html#default-keyboard-bindings

source

bindingcommand
mod-shift-returnLaunch terminal
mod-pLaunch dmenu rofi
mod-shift-pLaunch gmrun
mod-shift-cClose the focused window
mod-spaceRotate through the available layout algorithms
mod-shift-spaceReset the layouts on the current workspace to default
mod-nResize viewed windows to the correct size
mod-tabMove focus to the next window
mod-shift-tabMove focus to the previous window
mod-jMove focus to the next window
mod-kMove focus to the previous window
mod-mMove focus to the master window
mod-returnSwap the focused window and the master window
mod-shift-jSwap the focused window with the next window
mod-shift-kSwap the focused window with the previous window
mod-hShrink the master area
mod-lExpand the master area
mod-tPush window back into tiling
mod-commaIncrement the number of windows in the master area
mod-periodDeincrement the number of windows in the master area
mod-shift-qQuit xmonad
mod-qRestart xmonad
mod-shift-slashRun xmessage with a summary of the default keybindings (useful for beginners)
mod-[1..9]Switch to workspace N
mod-shift-[1..9]Move client to workspace N
mod-{w,e,r}Switch to physical/Xinerama screens 1, 2, or 3
mod-shift-{w,e,r}Move client to screen 1, 2, or 3
mod-button1Set the window to floating mode and move by dragging
mod-button2Raise the window to the top of the stack
mod-button3Set the window to floating mode and resize by dragging

Custom

myKeys :: XConfig t -> M.Map (KeyMask, KeySym) (X ())
myKeys XConfig {modMask = m, terminal = term} = M.fromList $ [
Rebooting / Restarting
  ((m .|. shiftMask .|. mod1Mask, xK_r), spawn "reboot")
, ((m .|. shiftMask .|. mod1Mask, xK_i), spawn "xmonad --recompile && xmonad --restart")
Add Workspace 0
, ((m, xK_0), windows $ W.greedyView "0")
, ((m .|. shiftMask, xK_0), windows $ W.shift "0")
Launcher / Window Switcher

I currently use rofi to run programs or switch between open windows. It’s simple, fast, and supports fuzzy search.

, ((m, xK_p), spawn "rofi -show drun -modi drun -show-icons -matching fuzzy")
, ((m .|. shiftMask, xK_p), spawn "GDK_SCALE=2 rofi -show drun -modi drun -show-icons -matching fuzzy")
, ((m, xK_b), spawn "rofi -show window -show-icons -matching fuzzy")
-- Like M-y for helm-show-kill-ring in Emacs
, ((m, xK_y), spawn "rofi -modi \"clipboard:greenclip print\" -show clipboard -run-command '{cmd}'")
-- Text espander

, ((m .|. shiftMask .|. mod1Mask, xK_j), spawn "texpander.sh")
Running Emacs
, ((m, xK_n), spawn "emacsclient -c")
, ((m .|. shiftMask .|. mod1Mask, xK_n), spawn "EMACS_NON_WORK_MODE=1 ~/scripts/run_emacs.sh")
, ((m .|. shiftMask, xK_n), spawn "~/scripts/run_emacs.sh")
Lock Screen
, ((m .|. shiftMask .|. mod1Mask, xK_o), spawn "xtrlock -b")
Horizontal Resizing

An obvious missing default…

, ((m .|. shiftMask, xK_h), sendMessage MirrorShrink)
, ((m .|. shiftMask, xK_l), sendMessage MirrorExpand)
Window Minimization / Restoration
, ((m, xK_m), withFocused minimizeWindow)
, ((m .|. shiftMask, xK_m), withLastMinimized maximizeWindowAndFocus)
Fullscreen
, ((m .|. shiftMask, xK_f), withFocused $ \f -> windows =<< appEndo `fmap` runQuery doFullFloat f)
Workspace Swapping

Using mod+comma quickly swap between workspaces is very handy.

, ((m, xK_comma), toggleWS)
Changing number of master windows

Some layouts, like ResizableTall, have a “master” area, with 1 window initially assigned there. These commands enable incrementing or decrementing that number.

They are bound by default to mod+, and mod+., but mod+,~ is much more useful for [[*Workspace Swapping][Workspace Swapping]]. Here I add ~Shift to the defaults.

, ((m .|. shiftMask, xK_comma), sendMessage (IncMasterN 1))
, ((m .|. shiftMask, xK_period), sendMessage (IncMasterN (-1)))
Easier Kill Binding
  • I find the default mod+shift+c binding to be clumbsy for killing windows.
  • mod+q is easier / more natural.
  • The default mod+q for killing XMonad is something I’ve never needed.
, ((m, xK_q), kill)
Volume Control

I don’t run a desktop environment, so the volume buttons on my keyboard don’t do anything by default.

, ((m .|. shiftMask, xK_Up), spawn "amixer -c 0 -q set Master 2dB+ unmute")
, ((m .|. shiftMask, xK_Down),spawn "amixer -c 0 -q set Master 2dB- unmute")
Screenshots
,((0, xK_Print), myScreenshot)
,((m, xK_Print), myScreenshotClipboard)
Keyboard
, ((m .|. shiftMask, xK_i), spawn "setxkbmap -option 'ctrl:nocaps' && xset r rate 160 80")
Arandr

I have main.sh and laptop.sh symlinked to whatever the current xrandr scripts are for my desk / laptop.

, ((m, xK_s), spawn "/home/dan/.screenlayout/main.sh && feh --bg-fill --no-xinerama --randomize ~/Media/images/wallpaper/* &" )
, ((m .|. shiftMask, xK_s), spawn "/home/dan/.screenlayout/laptop.sh && feh --bg-fill --no-xinerama --randomize ~/Media/images/wallpaper/*" )
Cycle Recent Workspace
, ((m .|. shiftMask, xK_Tab), cycleRecentWS [xK_Super_L] xK_Tab xK_BackSpace)
Multiple Monitors

The functionality here is a primary reason for choosing XMonad: a natural, keyboard-driven way for coordinating workspaces across multiple monitors. I’m genuinely curious to know if others have found something on-par/better elsewhere. Please contact me if you do.

  • Bind mod-{w, e, r} to switch focus between monitors.
  • Bind mod-shift-{w, e, r} to move workspaces between monitors.
] ++
[((m .|. nilOrShift, key), screenWorkspace sc
        >>= flip whenJust (windows . f))
     | (key, sc) <- zip [xK_w, xK_r, xK_e] [0..]
     , (f, nilOrShift) <- [(W.view, 0), (W.shift, shiftMask)]]
Try XMonad.Actions.PhysicalScreens to order mod-{w, e, r} based on physical screen layout

Asthetics

red     = "#dc322f"
blue    = "#268bd2"
yellow  = "#b58900"
inactive  = "#002b36"
active      = blue

topBarTheme = def
    { inactiveBorderColor   = inactive
    , inactiveColor         = inactive
    , inactiveTextColor     = inactive
    , activeBorderColor     = active
    , activeColor           = active
    , activeTextColor       = active
    , urgentBorderColor     = red
    , urgentTextColor       = yellow
    , decoHeight            = 10
    }


myShellPrompt = def
       { font              = "xft:Hack:pixelsize=30"
       , promptBorderWidth = 1
       , position          = Top
       , height            = 42
       , defaultText       = []
       }

Float certain apps

manageZoomHook =
  composeAll $
    [ (className =? zoomClassName) <&&> shouldFloat <$> title --> doFloat,
      (className =? zoomClassName) <&&> shouldSink <$> title --> doSink
    ]
  where
    zoomClassName = "zoom"
    tileTitles =
      [ "Zoom - Free Account", -- main window
        "Zoom - Licensed Account", -- main window
        "Zoom", -- meeting window on creation
        "Zoom Meeting" -- meeting window shortly after creation
      ]
    shouldFloat title = title `notElem` tileTitles
    shouldSink title = title `elem` tileTitles
    doSink = (ask >>= doF . W.sink) <+> doF W.swapDown

myHandleEventHook =
  mconcat
    [ dynamicTitle manageZoomHook,
      handleEventHook defaultConfig
    ]


myManageHook = composeAll [ appName =? "Open Files" --> doFloat,
                            className =? "Zenity" --> doFloat]

Screenshot

myScreenshot = do
  -- init takes care of the trailing newline character returned by date
  date <- init <$> runProcessWithInput "date" ["+%Y-%m-%d-%H:%M:%S"] []
  AL.launchApp myShellPrompt { defaultText = "~/screenshots/" ++ date ++ ".png"} "maim -s"
myScreenshotClipboard :: X ()
myScreenshotClipboard = spawn  "maim -s | xclip -selection clipboard -t image/png"

Start Script

This is currently only used by when on non-NixOS systems (e.g. Ubuntu).

#!/bin/bash

# Identify the home of our gtkrc file, important for setting styles of
# gtk-based applications
export GTK2_RC_FILES="$HOME/.gtkrc-2.0"

# enable for hidpi displays
export GDK_SCALE=1

setxkbmap -option 'ctrl:nocaps'
setxkbmap us -variant colemak_dh

# set keyboard rate
xset r rate 160 80

sudo timedatectl set-timezone Europe/Berlin
# sudo timedatectl set-timezone America/New_York
# sudo timedatectl set-timezone America/Los_Angeles

# Ensure Zoom output volume stays constant
# /home/dan/scripts/fuck_zoom.sh 100 &

# Clipboard manager (used with rofi)
greenclip daemon &

# Start espanso
# espanso restart  # broken in Ubuntu 22

# set trackball rate
xinput --set-prop "Primax Kensington Eagle Trackball" "libinput Accel Speed" 1 || true

# eye breaks
# safeeyes &

# Wallpaper
feh --bg-fill --no-xinerama --randomize ~/Media/images/wallpaper/*
# xsetroot -solid black

# Config in ~/.config/redshift/redshift.conf
redshift &

picom -b

eval $(ssh-agent)

syncthing serve &

# Now, finally, start xmonad
exec xmonad

Desktop Entry Pointing to Start Script

/usr/share/xsessions/xmonad.desktop

[Desktop Entry]
Name=XMonad
Comment=Lightweight tiling window manager
Exec=/home/dan/.xmonad/start-xmonad
Icon=xmonad.png
Type=XSession

Nixos Config

System-level Config

{ config, pkgs, ... }:
let
  sources = import ./nix/sources.nix;
  # ghcide-nix = import sources."ghcide-nix" { };
in {
  imports =
    [ ./hardware-configuration.nix
      ./settings.nix
      "${builtins.fetchTarball https://github.com/rycee/home-manager/archive/release-20.03.tar.gz}/nixos"
    ];

  system.stateVersion = "20.03";

  nixpkgs.config = {
    # Allow unfree, which is required for some drivers.
    allowUnfree = true;
  };

Nix

nix = {
  useSandbox = true;
  autoOptimiseStore = true;
  maxJobs = 3; # should be 1 per CPU logical core
  binaryCaches = [
    "https://cache.nixos.org/"
    "https://ghcide-nix.cachix.org"
    "https://hercules-ci.cachix.org"
    "https://iohk.cachix.org"
    "https://nix-tools.cachix.org"
  ];
  binaryCachePublicKeys = [
    "ghcide-nix.cachix.org-1:ibAY5FD+XWLzbLr8fxK6n8fL9zZe7jS+gYeyxyWYK5c="
    "hercules-ci.cachix.org-1:ZZeDl9Va+xe9j+KqdzoBZMFJHVQ42Uu/c/1/KMC5Lw0="
    "iohk.cachix.org-1:DpRUyj7h7V830dp/i6Nti+NEO2/nhblbov/8MW7Rqoo="
    "nix-tools.cachix.org-1:ebBEBZLogLxcCvipq2MTvuHlP7ZRdkazFSQsbs0Px1A="
  ];
  gc = {
    automatic = true;
    dates = "23:00";
    options = "--delete-older-than 30d";
  };
};

Timezone

time.timeZone = "America/Los_Angeles";

Boot

boot = {
  cleanTmpDir = true;

  loader = {
    timeout = 1; # Timeout (in seconds) until loader boots the default menu item.
    grub = {
      enable = true;
      version = 2;
      device = "nodev";
      copyKernels = true;
      fsIdentifier = "provided";
      extraConfig = "serial; terminal_input serial; terminal_output serial";
    };
    systemd-boot.enable = false;
    efi.canTouchEfiVariables = false;

  };
};

Networking

networking.useDHCP = false;
networking.usePredictableInterfaceNames = false;
networking.interfaces.eth0.useDHCP = true;
networking.firewall.enable = false;
networking.firewall.allowPing = true;
# networking.networkmanager.enable = true;
networking.hostName = "nixos-dev";

networking.interfaces.eth0.tempAddress = "disabled";

Services

services = {

  xserver = {
    enable = true;
    layout = "us";

    windowManager.xmonad = {
      enable = true;
      enableContribAndExtras = true;
      extraPackages = haskellPackges: [
        haskellPackges.xmonad-contrib
        haskellPackges.xmonad-extras
        haskellPackges.xmonad
      ];
    };

    displayManager = {
      defaultSession = "none+xmonad";
      lightdm.enable = true;
    };
    desktopManager.xterm.enable = false;
  };

Syncthing

# https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/services/networking/syncthing.nix
syncthing = {
  enable = true;
  openDefaultPorts = true;
  user = "${config.settings.username}";
  configDir = "/home/${config.settings.username}/.config/syncthing";
  dataDir = "/home/${config.settings.username}/.local/share/syncthing";
  declarative = {
    devices = {
      nixos-dev.id = "EEMRJQE-TBONTUL-UBGJ6FT-AAUS25K-COP3VHE-WERN7IN-PTNZ63Z-GZZX2AY";
      x1carbon9.id = "EIZV5LR-F3JILKF-7MD5UMZ-KYRW37L-RVJ2WI4-7LQD7VC-U5BSEBD-YMGQPQ3";
      pixel6-pro.id = "DISECZT-3ILXZ2S-B7BTYBI-R6KKLH2-YYIDZGD-4LP2OEF-NAURN57-ZPR6XAD";
    };
    folders = {
      sync = rec {
        id = "at23u-zmxto";
        devices = [ "nixos-dev" "x1carbon9" "pixel6-pro"];
        path = "/bkp/Sync";
        watch = false;
        rescanInterval = 3600 * 1;
        type = "receiveonly"; # sendreceive
        enable = true;
        versioning.type = "simple";
        versioning.params.keep = "5";
      };
      media = rec {
        id = "media";
        devices = [ "nixos-dev"  "x1carbon9" "pixel6-pro"];
        path = "/bkp/Media";
        watch = false;
        rescanInterval = 3600 * 6;
        type = "receiveonly"; # sendreceive
        enable = true;
        versioning.type = "simple";
        versioning.params.keep = "5";
      };
      work = rec {
        id = "d7svv-zjsz2";
        devices = [ "nixos-dev" "x1carbon9" "pixel6-pro"];
        path = "/bkp/Work";
        watch = false;
        rescanInterval = 3600 * 6;
        type = "receiveonly"; # sendreceive
        enable = true;
        versioning.type = "simple";
        versioning.params.keep = "5";
      };
    };
  };
};

Tarsnap

nixpkgs/tarsnap.nix at master · NixOS/nixpkgs · GitHub

tarsnap.enable = true;
tarsnap.keyfile = "/bkp/Sync/keys/nixos-dev-tarsnap.key";
tarsnap.archives = {
  main = {
    keyfile = "/bkp/Sync/keys/nixos-dev-tarsnap.key";
    directories = ["/bkp/Sync" "/bkp/Work" "/bkp/Media"];
  };
};

End

};
# virtualisation.docker.enable = true;

Packages

environment.systemPackages = with pkgs; [
  coreutils binutils
  curl wget
  zip unzip
  git
  killall
  syncthing-cli
  sshfs
  mtr # traceroute
  sysstat
  htop
];

Fonts

fonts = {
  enableFontDir = true;
  enableGhostscriptFonts = true;
  fonts = with pkgs; [
    corefonts
    hack-font
  ];
};

User Definition

security.sudo.wheelNeedsPassword = false;

users.mutableUsers = false;

users.extraUsers.${config.settings.username} = {
  isNormalUser = true;
  uid = 1000;
  createHome = true;
  home = "/home/${config.settings.username}";
  description = "${config.settings.name}";
  extraGroups = [
    "audio"
    "networkmanager"
    "systemd-journal"
    "vboxusers"
    "video"
    "wheel"
  ];
};

home-manager.users.dan = import ./home.nix ;

SSH

services.openssh = {
  enable = true;
  forwardX11 = true;
  permitRootLogin = "without-password";
  passwordAuthentication = false;
};

users.users.${config.settings.username}.openssh.authorizedKeys.keys = [
  "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC+yJ5sv7iO9PBuozfmitR0JJfqDsJ7w+rlryq5CwdatO3tkRdR5dMYdFTFCeHbmeakPTC/uys08fziEUXh3DL206jDKQEMBoMGXNowZHyYzr25nIogHbveqeNTgP8jsTw5uBaJu8LFzHHey4Sw9WlRrvIqguUT5jB3omZh8yDWcxTrTJlTsN2TM3HILvirfVwBkD2uNTDdd5LplbZhx6x87VCs6ZNYhBjJ4CPcO4zTQuEdyyxUHEgtMkYgrS4Jb/Kl6Tleftlh55E74SZ3XXnw3lWdH9ra8ewH265iqNr/RwysagnalslBZDLl8yJcrMsCVi4tPrZZc4vaeCsIWK4X dan@x1carbon"
];

programs.ssh.startAgent = true;

X2Go Client

# programs.x2goserver.enable = true;

End

}

User-level Config

{ config, pkgs, ... }:

let
  homeDir = builtins.getEnv "HOME";
  syncDir = builtins.toPath("${homeDir}/Sync");
  sources = import ./nix/sources.nix;
  nixos20_03 = import sources."nixpkgs-20.03" { };
  emacs-overlay = import (import ./nix/sources.nix)."emacs-overlay";
in {
  imports = [
    ./settings.nix
  ];

  home.stateVersion = "20.03";

  nixpkgs.config = {
    allowUnfree = true;
    packageOverrides = pkgs: { stable = nixos20_03; };
  };

  nixpkgs.overlays = [ emacs-overlay ];

Environment Variables

home.sessionVariables = {
  EDITOR = "emacsclient --create-frame --alternate-editor emacs";
  PASSWORD_STORE_DIR = "${syncDir}/.password-store";
  GNUPGHOME = "${syncDir}/.gnupg/";
  # GTK2_RC_FILES="${homeDir}/.gtkrc-2.0";
  # https://github.com/xmonad/xmonad/issues/126
  _JAVA_AWT_WM_NONREPARENTING = "1";
};

# gtk = {
#   enable = true;
#   iconTheme = {
#     name = "Adwaita";
#     package = pkgs.gnome3.adwaita-icon-theme;
#   };
#   theme = {
#     name = "Adwaita-dark";
#     package = pkgs.gnome3.gnome_themes_standard;
#   };
# };

xdg.enable = true;

Packages

home.packages = with pkgs; [
  rofi
  gnupg

  (pass.withExtensions (exts: [
    exts.pass-otp
    exts.pass-genphrase
  ]))

  xtrlock-pam  # screen locking
  maim  # screenshots
  rofi-pass  # interface to password manager
  xclip  # programmatic access to clipbaord
  arandr  # gui for xrandr (monitor layout)

  # direnv

  # Upstream failing :(
  # julia_13

  ## Doom dependencies

  (ripgrep.override {withPCRE2 = true;})
  gnutls              # for TLS connectivity

  ## Optional dependencies
  fd                  # faster projectile indexing
  imagemagick         # for image-dired
  pinentry_emacs

  ## Module dependencies
  # :tools lookup & :lang org +roam
  sqlite
  # :lang latex & :lang org (latex previews)
  texlive.combined.scheme-tetex

  firefox-beta-bin
];

Programs

programs = {

Home Manager

# Let Home Manager install and manage itself.
home-manager.enable = true;

Emacs

emacs = {
  enable = true;
  # Compile with imagemagick support so I can resize images.
  package = pkgs.emacsGit.override { inherit (pkgs) imagemagick; };
};

Bash

bash = {
  enable = true;
  historyFile = "${syncDir}/.config/bash/.bash_history";
  # FIXME: Document and reduce these
  shellOptions = [
    "autocd" "cdspell" "dirspell" "globstar" # bash >= 4
    "cmdhist" "nocaseglob" "histappend" "extglob"];
  # TODO: Test this
  # https://github.com/akermu/emacs-libvterm#directory-tracking-and-prompt-tracking
  initExtra = [
    ''
    vterm_prompt_end(){
      vterm_printf \"51;A$(whoami)@$(hostname):$(pwd)\"
    }
    PS1=$PS1'\\[$(vterm_prompt_end)\\]'
    ''
  ]
};

Git

git = {
  enable = true;
  userName = "${config.settings.name}";
  userEmail = "${config.settings.email}";
};

Direnv

# direnv.enable = true;

SSH

ssh = {
  enable = true;

  controlMaster  = "auto";
  controlPath    = "/tmp/ssh-%u-%r@%h:%p";
  controlPersist = "1800";

  forwardAgent = true;
  serverAliveInterval = 60;

  hashKnownHosts = true;
  userKnownHostsFile = "${homeDir}/.ssh/known_hosts";

  matchBlocks = {
    droplet = {
      hostname = "45.55.5.197";
      identityFile = "${homeDir}/.ssh/id_rsa";
      user = "dgirsh";
    };
    dangirsh = {
      host = "dangirsh.org";
      hostname = "ssh.phx.nearlyfreespeech.net";
      identityFile = "${homeDir}/.ssh/id_rsa";
      user = "dangirsh_dangirsh";
    };
    nixos-dev = {
      hostname = "45.79.58.229";
      identityFile = "${homeDir}/.ssh/id_rsa";
      user = "dan";
    };
  };
};

End

};

Services

services = {
  emacs.enable = true;

  # redshift = {
  #   enable = true;
  #   latitude = "33";
  #   longitude = "-97";
  #   temperature.day = 6500;
  #   temperature.night = 3000;
  # };

  # https://www.reddit.com/r/emacsporn/comments/euf7m8/doomoutrunelectric_theme_xmonad_nixos/
  # https://github.com/willbush/system/blob/371cfa9933f24bca585a3c6c952c41c864d97aa0/nixos/home.nix#L178
  # compton = {
  #     enable = true;
  #     fade = true;
  #     backend = "xrender";
  #     fadeDelta = 1;
  #     # I only want transparency for a couple of applications.
  #     opacityRule = [
  #       "90:class_g ?= 'emacs' && focused"
  #       "75:class_g ?= 'emacs' && !focused"
  #       "90:class_g ?= 'alacritty' && focused"
  #       "75:class_g ?= 'alacritty' && !focused"
  #     ];
  #   };

  # lorri.enable = true;
};

End

}

To test [0/5]

To add [0/1]

  • keyboard config: currently requires xset & setxbmap
  • [ ] Messenging
    • unified messenger (if it exists) for dealing with the madness of:
      • sms
      • signal
      • fb
      • whatsapp
      • keybase
      • slack
      • irc
      • riot
    • To try
      • [X] pidgin - just bad. no hidpi, things like signal don’t sync with mobile state, clunky ui.
    • Otherwise:
      • Browser tabs for everything sans signal
      • Signal-destop
      • maybe slack desktop
      • caprine for fb is nice on ubuntu

Global Constants

{config, pkgs, lib, ...}:

with lib;

{
  options = {
    settings = {
      name = mkOption {
        default = "Dan Girshovich";
        type = with types; uniq str;
      };
      username = mkOption {
        default = "dan";
        type = with types; uniq str;
      };
      email = mkOption {
        default = "dan.girsh@gmail.com";
        type = with types; uniq str;
      };
    };
  };
}

Hardware-specific Config

# 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, ... }:

{
  imports =
    [ <nixpkgs/nixos/modules/profiles/qemu-guest.nix>
    ];

  boot.initrd.availableKernelModules = [ "virtio_pci" "ahci" "sd_mod" ];
  boot.initrd.kernelModules = [ ];
  boot.kernelModules = [ ];
  boot.extraModulePackages = [ ];

  nix.maxJobs = lib.mkDefault 1;

Disk Mounts

From Chapter 8. File Systems: “Mount points are created automatically if they don’t already exist.”

  fileSystems."/" =
    { device = "/dev/disk/by-uuid/bf38bdde-34dd-4d57-9bfe-07de465f0f29";
      fsType = "ext4";
    };

  # Linode Volume "bkp". Targetted by syncthing.
  fileSystems."/bkp" =
    { device = "/dev/disk/by-id/scsi-0Linode_Volume_bkp";
      fsType = "ext4";
    };

  swapDevices =
    [ { device = "/dev/disk/by-uuid/7596d600-d2c6-4d77-b138-7f595283af00"; }
    ];
}

Version Pinning

These are generated via niv.

{
    "emacs-overlay": {
        "branch": "master",
        "description": "Bleeding edge emacs overlay [maintainer=@adisbladis] ",
        "homepage": "",
        "owner": "nix-community",
        "repo": "emacs-overlay",
        "rev": "0feda8b31b52f3ea008555dfe79dba3989d3e585",
        "sha256": "1ijr9pl0czzbgj35vj8kq4xvcana6w24ljcmzriz7cyxln4pgvln",
        "type": "tarball",
        "url": "https://github.com/nix-community/emacs-overlay/archive/0feda8b31b52f3ea008555dfe79dba3989d3e585.tar.gz",
        "url_template": "https://github.com/<owner>/<repo>/archive/<rev>.tar.gz"
    },
    "ghcide-nix": {
        "branch": "master",
        "description": "Nix installation for ghcide",
        "homepage": "https://github.com/digital-asset/ghcide",
        "owner": "cachix",
        "repo": "ghcide-nix",
        "rev": "f940ec611cc6914693874ee5e024eba921cab19e",
        "sha256": "0vri0rivdzjvxrh6lzlwwkh8kzxsn82jp1c2w5rqzhp87y6g2k8z",
        "type": "tarball",
        "url": "https://github.com/cachix/ghcide-nix/archive/f940ec611cc6914693874ee5e024eba921cab19e.tar.gz",
        "url_template": "https://github.com/<owner>/<repo>/archive/<rev>.tar.gz"
    },
    "nixpkgs-20.03": {
        "branch": "release-20.03",
        "description": "A read-only mirror of NixOS/nixpkgs tracking the released channels. Send issues and PRs to",
        "homepage": "https://github.com/NixOS/nixpkgs",
        "owner": "NixOS",
        "repo": "nixpkgs",
        "rev": "7829e5791ba1f6e6dbddbb9b43dda72024dd2bd1",
        "sha256": "0hs9swpz0kibjc8l3nx4m10kig1fcjiyy35qy2zgzm0a33pj114w",
        "type": "tarball",
        "url": "https://github.com/NixOS/nixpkgs/archive/7829e5791ba1f6e6dbddbb9b43dda72024dd2bd1.tar.gz",
        "url_template": "https://github.com/<owner>/<repo>/archive/<rev>.tar.gz"
    }
}
# This file has been generated by Niv.

# A record, from name to path, of the third-party packages
with rec
{
  pkgs =
    if hasNixpkgsPath
    then
        if hasThisAsNixpkgsPath
        then import (builtins_fetchTarball { inherit (sources_nixpkgs) url sha256; }) {}
        else import <nixpkgs> {}
    else
        import (builtins_fetchTarball { inherit (sources_nixpkgs) url sha256; }) {};

  sources_nixpkgs =
    if builtins.hasAttr "nixpkgs" sources
    then sources.nixpkgs
    else abort
    ''
        Please specify either <nixpkgs> (through -I or NIX_PATH=nixpkgs=...) or
        add a package called "nixpkgs" to your sources.json.
    '';

  # fetchTarball version that is compatible between all the versions of Nix
  builtins_fetchTarball =
      { url, sha256 }@attrs:
      let
        inherit (builtins) lessThan nixVersion fetchTarball;
      in
        if lessThan nixVersion "1.12" then
          fetchTarball { inherit url; }
        else
          fetchTarball attrs;

  # fetchurl version that is compatible between all the versions of Nix
  builtins_fetchurl =
      { url, sha256 }@attrs:
      let
        inherit (builtins) lessThan nixVersion fetchurl;
      in
        if lessThan nixVersion "1.12" then
          fetchurl { inherit url; }
        else
          fetchurl attrs;

  # A wrapper around pkgs.fetchzip that has inspectable arguments,
  # annoyingly this means we have to specify them
  fetchzip = { url, sha256 }@attrs: pkgs.fetchzip attrs;

  # A wrapper around pkgs.fetchurl that has inspectable arguments,
  # annoyingly this means we have to specify them
  fetchurl = { url, sha256 }@attrs: pkgs.fetchurl attrs;

  hasNixpkgsPath = (builtins.tryEval <nixpkgs>).success;
  hasThisAsNixpkgsPath =
    (builtins.tryEval <nixpkgs>).success && <nixpkgs> == ./.;

  sources = builtins.fromJSON (builtins.readFile ./sources.json);

  mapAttrs = builtins.mapAttrs or
    (f: set: with builtins;
      listToAttrs (map (attr: { name = attr; value = f attr set.${attr}; }) (attrNames set)));

  # borrowed from nixpkgs
  functionArgs = f: f.__functionArgs or (builtins.functionArgs f);
  callFunctionWith = autoArgs: f: args:
    let auto = builtins.intersectAttrs (functionArgs f) autoArgs;
    in f (auto // args);

  getFetcher = spec:
    let fetcherName =
      if builtins.hasAttr "type" spec
      then builtins.getAttr "type" spec
      else "builtin-tarball";
    in builtins.getAttr fetcherName {
      "tarball" = fetchzip;
      "builtin-tarball" = builtins_fetchTarball;
      "file" = fetchurl;
      "builtin-url" = builtins_fetchurl;
    };
};
# NOTE: spec must _not_ have an "outPath" attribute
mapAttrs (_: spec:
  if builtins.hasAttr "outPath" spec
  then abort
    "The values in sources.json should not have an 'outPath' attribute"
  else
    if builtins.hasAttr "url" spec && builtins.hasAttr "sha256" spec
    then
      spec //
      { outPath = callFunctionWith spec (getFetcher spec) { }; }
    else spec
  ) sources

Future Work

[#A] Floating Emacs popup for quick commands

It’s usually most convenient to use/implement functions in Emacs than other tools. This was a huge draw towards EXWM, and I got used to that workflow.

For example, generating / getting passwords stored in pass has a nice Emacs interface. Currently, this often means switching from Firefox to Emacs to run a command, then back to Firefox. In this situation, a common solution seems to be eschewing Emacs for things like rofi (e.g. rofi-pass).

However, if I could popup a fast, floating Emacs window (similar to Rofi), I’d be able to leverage Elisp for most tasks like this. This might fully obviate Rofi and the XMonad prompt.

The floating window would default to a counsel-M-x fuzzy search over all incremental commands, and be bound to s-x in XMonad.

Examples:

  • screenshotting
  • pass interface
  • redshift / brightness controls
  • (maybe) finding windows / launching programs
  • clipboard management (helm kill ring)

As a small added benefit, I’d have the full Emacs bindings available in the input area.

[#B] HiDPI autotoggle

I’m occasionally lucky enough to plug into a 4k screen, which is not yet a seamless experience in linux.

For now I manually do the following on HiDPI screens:

export GDK_SCALE=2

and change layout.css.devPixelsPerPx in Firefox about:config from -1 to 2.

tweak font size in Firefox and Emacs.

Keybase Tweak

Per keybase/client#5797 increase font size in client, edit the ExecStart line to have --force-device-scale-factor=2. Then, reload with:

systemctl --user daemon-reload
systemctl --user restart keybase.gui.service

[#B] Consistent copy/paste bindings

In EXWM, I used the simulation keys to have consistent Emacs copy/paste bindings everywhere. Need a solution for XMonad. Setting the GTK bindings to “emacs” didn’t seem to affect for Firefox, which is (by far) my primary non-Emacs window.

[#B] Consolidate XMonad and Emacs window management

Ideally, XMonad could manage all windows, including Emacs windows.

Possible solutions:

  • frames-only-mode
  • frame-mode

These seemed to cause more problems than they fixed for me.

[#C] Fix indentation in tangled files

Even with org-src-preserve-indentation set to t, files like home.nix aren’t properly indented. This is because they are split across several org blocks, and in each on I use the nix-mode auto-indent to fixup the indentation within the block. This removes the leading whitespace, so each block is tangled without leading indentation. Thankfully, Nix doesn’t care, so this is purely for the aesthetics of the generated files.

Quickfix is to auto-indent the tangled files, then detangle.

Using noweb references seems to be the best option: https://orgmode.org/manual/Noweb-Reference-Syntax.html. See “This feature can also be used for management of indentation in exported code snippets”.

Autoloads

About

A neurotically minimal and reproducible computing environment.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published