Skip to content

nptd/dotfiles

 
 

Repository files navigation

Hi there! That’s my dotfiles. Most of config files are now generated by org-babel from this file (yes, from README.org). That’s literate programming applied to dotfiles. To generate all files you can open this file in emacs and press M-x org-babel-tangle. Or from command line with:

emacs README.org --batch -f org-babel-tangle

I keep this document in sync with generated config files just in case I won’t have access to my emacs. However, I recommend against looking at them—they’re just a generated mess; you’ll have much better time reading this doc instead—trust me.

Pieces not (yet) covered in this document are:

  • emacs configuration at .emacs.d/;
  • vim configuration at .vimrc and .vim/;
  • awesome wm configuration at .config/awesome/;
  • scripts at bin/;
  • irssi config at .irssi;

NixOS

I’m a NixOS user. What’s cool about it is that I can describe all my system configuration in one file (/etc/nixos/configuration.nix), so I can just copy it to other machine, call nixos-rebuild and have system with the same software, system settings, etc.

It looks like this:

{ config, pkgs, lib, ... }:
let
  meta = import ./meta.nix;
  machine-config = lib.getAttr meta.name {
    Larry = [
      <<machine-larry>>
    ];
    ashmalko = [
      <<machine-ashmalko>>
    ];
    omicron = [
      <<machine-omicron>>
    ];
  };

in
{
  imports = [
    {
      nixpkgs.config.allowUnfree = true;

      # The NixOS release to be compatible with for stateful data such as databases.
      system.stateVersion = "15.09";
    }

    <<nixos-section>>
  ] ++ machine-config;
}

This <<nixos-section>> is replaced by other parts of this doc.

Default locations

This moves nixos configuration from the default location to the dotfiles/nixos/configuration.nix.

This also disables channel mechanism and makes nixos use Nixpkgs in the dotfiles/channels directory. I usually follow nixpkgs-unstable, but that gives me more control.

{
  nix.nixPath =
    let dotfiles = "/home/rasen/dotfiles";
    in [
      "nixos-config=${dotfiles}/nixos/configuration.nix"
      "dotfiles=${dotfiles}"
      "${dotfiles}/channels"
    ];
}

If you want to override default configuration location, use -I flag:

sudo nixos-rebuild switch -I nixos-config=/etc/nixos/configuration.nix

Save config

Save nixos-config in the Nix store, so I can retrieve it later. The config for the currently running system is located at /var/run/current-system/configuration.nix.

{
  system.copySystemConfiguration = true;
}

This setting copies only the configuration.nix file, which works pretty nice as I have only one configuration file and don’t split it.

Users

I’m the only user of the system:

{
  users.extraUsers.rasen = {
    isNormalUser = true;
    uid = 1000;
    extraGroups = [ "users" "wheel" "input" ];
    initialPassword = "HelloWorld";
  };
}

initialPassword is used only first time when user is created. It must be changed as soon as possible with passwd.

Machines

I have three machines running NixOS.

Larry

This one is a laptop that needs the proprietary driver for Wi-Fi card (this wl and broadcom_sta).

{
  imports = [ <nixpkgs/nixos/modules/installer/scan/not-detected.nix> ];

  boot.initrd.availableKernelModules = [ "ahci" "xhci_hcd" ];
  boot.initrd.kernelModules = [ "wl" ];

  boot.kernelModules = [ "kvm-intel" "wl" ];
  boot.extraModulePackages = [ config.boot.kernelPackages.broadcom_sta ];
}

I have two partitions (usual “separate home” setup).

{
  fileSystems = {
    "/" = {
      device = "/dev/disk/by-uuid/ba82dd25-a9e5-436f-ae76-4ee44d53b2c6";
      fsType = "ext4";
    };
    "/home" = {
      device = "/dev/disk/by-uuid/b27c07d0-aaf7-44a1-87e1-5a2cb30954ec";
      fsType = "ext4";
    };
  };
}

There are also two swap partitions, but one of them is from my slow hdd, so I probably shouldn’t use it.

{
  swapDevices = [
    # TODO: set priority
    # { device = "/dev/disk/by-uuid/f0bd0438-3324-4295-9981-07015fa0af5e"; }
    { device = "/dev/disk/by-uuid/75822d9d-c5f0-495f-b089-f57d0de5246d"; }
  ];
}

There is also Gentoo on the second drive—it’s good to keep it bootable.

{
  boot.loader.grub = {
    enable = true;
    version = 2;
    device = "/dev/sda";
    extraEntries = ''
      menuentry 'Gentoo' {
        configfile (hd1,1)/grub2/grub.cfg
      }
    '';
  };
}

Boring stuff: 8 hyper-threads, synaptics (Larry is a laptop).

{
  nix.maxJobs = 8;
  nix.buildCores = 8;

  services.xserver.synaptics = {
    enable = true;
    twoFingerScroll = true;
    vertEdgeScroll = true;
  };
}

I have nvidia video card and integrated intel one. I don’t use nvidia one, so next the line disables it:

{
  hardware.nvidiaOptimus.disable = true;
}

Quantified Self

ELK stack to gather information about my computer activity.

{
  services.logstash = {
    enable = true;
    inputConfig = ''
      file {
        path => "/home/rasen/log.txt.processed"
        sincedb_path => "/home/rasen/.log.txt.sincedb"
        codec => "json"
        start_position => "beginning"
        tags => [ "awesomewm" ]
        type => "awesomewm"
      }
      file {
        path => "/home/rasen/log.txt.ashmalko"
        sincedb_path => "/home/rasen/.log.txt.ashmalko.sincedb"
        codec => "json"
        start_position => "beginning"
        tags => [ "awesomewm" ]
        type => "awesomewm"
      }
      file {
        path => "/home/rasen/log.txt.omicron"
        sincedb_path => "/home/rasen/.log.txt.omicron.sincedb"
        codec => "json"
        start_position => "beginning"
        tags => [ "awesomewm" ]
        type => "awesomewm"
      }
    '';
    filterConfig = ''
      if [path] == "/home/rasen/log.txt.ashmalko" {
        mutate {
          replace => [ "host", "ashmalko" ]
        }
      }
      if [path] == "/home/rasen/log.txt.omicron" {
        mutate {
          replace => [ "host", "omicron" ]
        }
      }
    '';
    outputConfig = ''
      elasticsearch {
        index => "quantified-self"
        document_type => "awesomewm"
      }
    '';
  };

  services.elasticsearch = {
    enable = true;
    cluster_name = "ashmalko";
    extraConf = ''
      node.name: "${meta.name}"
    '';
  };

  services.kibana = {
    enable = true;
  };
}

ashmalko

This is my corporate desktop computer.

{
  nix.maxJobs = 4;
  nix.buildCores = 4;
}
{
  imports = [
    <nixpkgs/nixos/modules/installer/scan/not-detected.nix>
  ];

  boot.initrd.availableKernelModules = [ "xhci_pci" "ahci" "usb_storage" "usbhid" "sd_mod" ];
  boot.kernelModules = [ "kvm-intel" ];
  boot.extraModulePackages = [ ];

  boot.kernelParams = [ "intel_pstate=no_hwp" ];
  boot.loader.grub = {
    enable = true;
    version = 2;
    device = "/dev/sda";
    efiSupport = true;
  };
  boot.loader.efi.canTouchEfiVariables = true;
}

This is encrypted LVM on LUKS setup.

{
  boot.initrd.luks.devices = [
    {
      name = "root";
      device = "/dev/disk/by-uuid/a3eb801b-7771-4112-bb8d-42a9676e65de";
      preLVM = true;
      allowDiscards = true;
    }
  ];

  fileSystems."/boot" = {
    device = "/dev/disk/by-uuid/4184-7556";
    fsType = "vfat";
  };

  fileSystems."/" = {
    device = "/dev/disk/by-uuid/84d89f4b-7707-4580-8dbc-ec7e15e43b52";
    fsType = "ext4";
    options = [ "noatime" "nodiratime" "discard" ];
  };

  swapDevices = [
    { device = "/dev/disk/by-uuid/5a8086b0-627e-4775-ac07-b827ced6998b"; }
  ];
}

Gitolite

I host some git repos on my machine:

{
  services.gitolite = {
    enable = true;
    user = "git";
    adminPubkey = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDJhMhxIwZJgIY6CNSNEH+BetF/WCUtDFY2KTIl8LcvXNHZTh4ZMc5shTOS/ROT4aH8Awbm0NjMdW33J5tFMN8T7q89YZS8hbBjLEh8J04Y+kndjnllDXU6NnIr/AenMPIZxJZtSvWYx+f3oO6thvkZYcyzxvA5Vi6V1cGx6ni0Kizq/WV/mE/P1nNbwuN3C4lCtiBC9duvoNhp65PctQNohnKQs0vpQcqVlfqBsjQ7hhj2Fjg+Ofmt5NkL+NhKQNqfkYN5QyIAulucjmFAieKR4qQBABopl2F6f8D9IjY8yH46OCrgss4WTf+wxW4EBw/QEfNoKWkgVoZtxXP5pqAz rasen@Larry";
  };
}

I expose some repos for the folks here, so relax avahi rules for they to discover my machine by ashmalko.local rather than remember my IP address.

{
  services.avahi.interfaces = [ "enp0s31f6" ];
}

Zink

I’m running an MQTT implementation in there.

{
  networking.firewall.allowedTCPPorts = [
    1883 8883 # Zink
    3000      # Grafana
  ];

  systemd.services.zink = {
    description = "Zink service";
    wantedBy = [ "multi-user.target" ];
    after = [ "grafana.service" ];

    serviceConfig =
      let zink =
        pkgs.rustPlatform.buildRustPackage {
          name = "zink-0.0.3";

          src = pkgs.fetchFromGitHub {
            owner = "rasendubi";
            repo = "zink";
            rev = "influxdb-0.0.3";
            sha256 = "0sxw2jdabnw4q1kha176gz3glg4f1c6mag1i6242y0y579zf49lr";
          };

          depsSha256 = "1j7mipqd1n146xds8136c9dq87af821yfw4qk3m40531m9zw4pi4";
        };
      in {
        ExecStart = "${zink}/bin/zink timestamp,tagId,batteryLevel,temperature";
        Restart = "on-failure";
      };
  };

  services.influxdb.enable = true;

  services.grafana = {
    enable = true;
    addr = "0.0.0.0";
    port = 3000;

    domain = "ashmalko.local";
    auth.anonymous.enable = true;
  };
}

omicron

This is my small Dell XPS 13.

{
  imports = [
    <nixpkgs/nixos/modules/installer/scan/not-detected.nix>
  ];

  boot.initrd.availableKernelModules = [ "xhci_pci" "nvme" "usb_storage" "sd_mod" "rtsx_pci_sdmmc" ];
  boot.kernelModules = [ "kvm-intel" ];
  boot.extraModulePackages = [ ];

  nix.maxJobs = lib.mkDefault 4;

  powerManagement.cpuFreqGovernor = "powersave";

  boot.loader.systemd-boot.enable = true;
  boot.loader.efi.canTouchEfiVariables = true;
}

Same LVM on LUKS setup as ashmalko uses.

{
  boot.initrd.luks.devices = [
    {
      name = "root";
      device = "/dev/disk/by-uuid/8b591c68-48cb-49f0-b4b5-2cdf14d583dc";
      preLVM = true;
    }
  ];
  fileSystems."/boot" = {
    device = "/dev/disk/by-uuid/BA72-5382";
    fsType = "vfat";
  };
  fileSystems."/" = {
    device = "/dev/disk/by-uuid/434a4977-ea2c-44c0-b363-e7cf6e947f00";
    fsType = "ext4";
    options = [ "noatime" "nodiratime" "discard" ];
  };
  fileSystems."/home" = {
    device = "/dev/disk/by-uuid/8bfa73e5-c2f1-424e-9f5c-efb97090caf9";
    fsType = "ext4";
    options = [ "noatime" "nodiratime" "discard" ];
  };
  swapDevices = [
    { device = "/dev/disk/by-uuid/26a19f99-4f3a-4bd5-b2ed-359bed344b1e"; }
  ];
}

Clickpad:

{
  services.xserver.libinput = {
    enable = true;
    accelSpeed = "0.7";
  };
}

Networking tricks

I want to access my corporate network from my home laptop when I need to. I have a VPN up between my laptops and corporate desktop, so the solution is to use my corporate desktop as a router.

On Larry, route all corporate network traffic via ashmalko:

{
  networking.localCommands = ''
    ip route del 10.2.0.0/22 via 10.7.0.52 2> /dev/null || true
    ip route add 10.2.0.0/22 via 10.7.0.52
  '';
}

On ashmalko, rewrite packages from Larry into internal network. (Which is called “external” in the routing sense.)

{
  networking.nat = {
    enable = true;
    internalInterfaces = [ "tap0" ];
    externalInterface = "enp0s31f6";
  };
}

Local overlay

As a responsible NixOS user, I refuse to install software blindly with sudo make install. That’s why I must write my own nix-expressions. I keep them in my local overlay until they’re merged upstream.

Store separate overlays in a directory:

{
  nix.nixPath = [ "nixpkgs-overlays=/home/rasen/dotfiles/nixpkgs-overlays" ];
}

The entry point is just a set of all my packages in nixpkgs-local/default.nix:

{ pkgs ? import <nixpkgs> { } }:

let
  callPackage = pkgs.lib.callPackageWith (pkgs // pkgs.xlibs // self);

  pythonPackages = pkgs.pythonPackages // rec {
    <<nixpkgs-local-python-packages>>
  };

  self = rec {
    <<nixpkgs-local-packages>>
  };

in self

You can install all packages to current user with:

nix-env -f nixpkgs-local/default.nix -i

To make package results testing better, I build them in isolated environment (for more info, see nixos manual):

{
  nix.useSandbox = "relaxed";
}

Note that this is "relaxed" instead of true, because I have some packages that require a network to build and thus are __noChroot.

gitbook

gitbook-cli = (import ./gitbook { inherit pkgs; }).gitbook-cli;

To generate all Nix files use the following steps.

This goes to nixpkgs-local/gitbook/node-packages.json file:

["gitbook-cli"]

To generate the rest of Nix files, execute from nixpkgs-local/gitbook directory:

nix-shell -p nodePackages.node2nix --run 'node2nix -i node-packages.json 2>&1'

imapnotify

imapnotify is a nodejs package for listening to IMAP updates.

imapnotify = (import ./imapnotify { inherit pkgs; }).imapnotify;

To generate all Nix files use the following steps.

This goes to nixpkgs-local/imapnotify/node-packages.json file:

["imapnotify"]

To generate the rest of Nix files, execute from nixpkgs-local/imapnotify directory:

nix-shell -p nodePackages.node2nix --run 'node2nix -i node-packages.json 2>&1'

heroku

heroku = (import ./heroku { inherit pkgs; }).heroku-cli;

To generate all Nix files use the following steps.

This goes to nixpkgs-local/heroku/node-packages.json file:

["heroku-cli"]

To generate the rest of Nix files, execute the following command from nixpkgs-local/heroku directory (note that you need a fresh nixpkgs-unstable):

nix-shell -p nodePackages.node2nix --run 'node2nix -8 -i node-packages.json 2>&1'

Meltdown

{
  boot.kernelPackages = pkgs.linuxPackages_latest;
}

Services

NetworkManager

{
  networking = {
    hostName = meta.name;

    networkmanager.enable = true;

    # disable wpa_supplicant
    wireless.enable = false;
  };

  users.extraUsers.rasen.extraGroups = [ "networkmanager" ];

  environment.systemPackages = [
    pkgs.networkmanagerapplet
  ];
}

PulseAudio

Use pulseaudio (multiple sound sinks, skype calls). pavucontrol is PulseAudio Volume Control—a nice utility for controlling pulseaudio settings.

Also, Pulseaudio is a requirement for Firefox Quantum.

{
  hardware.pulseaudio = {
    enable = true;
    support32Bit = true;
  };

  environment.systemPackages = [ pkgs.pavucontrol ];
}

Locate

Update locate database daily.

{
  services.locate = {
    enable = true;
    localuser = "rasen";
  };
}

OpenVPN

All my computers are members of the VPN:

{
  services.openvpn.servers = {
    kaa.config = ''
      client
      dev tap
      port 22
      proto tcp
      tls-client
      persist-key
      persist-tun
      ns-cert-type server
      remote vpn.kaa.org.ua
      ca /root/.vpn/ca.crt
      key /root/.vpn/alexey.shmalko.key
      cert /root/.vpn/alexey.shmalko.crt
    '';
  };
}

Avahi

Avahi is needed to allow resolution of .local names. For example, you can access this computer by larry.local if we meet at the same local network.

{
  services.avahi = {
    enable = true;
    browseDomains = [ ];
    interfaces = [ "tap0" ];
    nssmdns = true;
    publish = {
      enable = true;
      addresses = true;
    };
  };
}

SSH

{
  services.openssh = {
    enable = true;
    passwordAuthentication = false;

    # Disable default firewall rules
    ports = [];
    listenAddresses = [
      { addr = "0.0.0.0"; port = 22; }
    ];
  };

  # allow ssh from VPN network only
  networking.firewall = {
    extraCommands = ''
      ip46tables -D INPUT -i tap0 -p tcp -m tcp --dport 22 -j ACCEPT 2> /dev/null || true
      ip46tables -A INPUT -i tap0 -p tcp -m tcp --dport 22 -j ACCEPT
    '';
  };
}

Mosh

Mosh (mobile shell) is a cool addition to ssh.

{
  programs.mosh.enable = true;
}

dnsmasq

Use dnsmasq as a DNS cache.

{
  services.dnsmasq = {
    enable = true;

    # These are used in addition to resolv.conf
    servers = [
      "/cybervisiontech.com/10.2.2.45"
      "/kaaiot.io/10.2.2.45"
      "8.8.8.8"
      "8.8.4.4"
    ];

    extraConfig = ''
      listen-address=127.0.0.1
      cache-size=1000

      no-negcache
    '';
  };
}

Syncthing

I use Syncthing to sync my org-mode files to my phone.

{
  services.syncthing = {
    enable = true;
    user = "rasen";
    dataDir = "/home/rasen/.config/syncthing";
    openDefaultPorts = true;
  };
}

Firewall

Enable firewall. This disables all ports and pings.

{
  networking.firewall = {
    enable = true;
    allowPing = false;

    connectionTrackingModules = [];
    autoLoadConntrackHelpers = false;
  };
}

Development

{
  services.postgresql.enable = true;
  services.redis.enable = true;
}
{
  virtualisation.docker.enable = true;
}

Mail setup

Mbsync

I use mbsync to sync my accounts and make them available offline.

{
  environment.systemPackages = [
    pkgs.isyncUnstable
  ];
}

Config file is .mbsyncrc.

MaildirStore local
Path ~/Mail/
Inbox ~/Mail/INBOX
SubFolders Verbatim

IMAPAccount gmail
Host imap.gmail.com
User [email protected]
PassCmd "pass imap.gmail.com/[email protected]"
SSLType IMAPS
CertificateFile /etc/ssl/certs/ca-certificates.crt

IMAPStore gmail-remote
Account gmail

Channel sync-gmail-all
Master :gmail-remote:"[Gmail]/All Mail"
Slave :local:Personal/all
Create Both
SyncState *

Channel sync-gmail-spam
Master :gmail-remote:"[Gmail]/Spam"
Slave :local:Personal/spam
Create Both
SyncState *

Channel sync-gmail-sent
Master :gmail-remote:"[Gmail]/Sent Mail"
Slave :local:Personal/sent
Create Both
SyncState *

Group sync-gmail
Channel sync-gmail-all
Channel sync-gmail-spam
Channel sync-gmail-sent

IMAPAccount kaaiot
Host imap.gmail.com
User [email protected]
PassCmd "pass imap.gmail.com/[email protected]"
SSLType IMAPS
CertificateFile /etc/ssl/certs/ca-certificates.crt

IMAPStore kaaiot-remote
Account kaaiot

Channel sync-kaaiot-all
Master :kaaiot-remote:"[Gmail]/All Mail"
Slave :local:KaaIoT/all
Create Both
SyncState *

Channel sync-kaaiot-spam
Master :kaaiot-remote:"[Gmail]/Spam"
Slave :local:KaaIoT/spam
Create Both
SyncState *

Channel sync-kaaiot-sent
Master :kaaiot-remote:"[Gmail]/Sent Mail"
Slave :local:KaaIoT/sent
Create Both
SyncState *

Group sync-kaaiot
Channel sync-kaaiot-all
Channel sync-kaaiot-spam
Channel sync-kaaiot-sent

imapnotify

var child_process = require('child_process');

function getStdout(cmd) {
  var stdout = child_process.execSync(cmd);
  return stdout.toString().trim();
}

exports.host = "imap.gmail.com";
exports.port = 993;
exports.tls = true;
exports.username = "[email protected]";
exports.password = getStdout("pass imap.gmail.com/[email protected]");
exports.onNotify = "mbsync sync-gmail";
// exports.onNotifyPost = {
//   "mail": "emacsclient  -e '(gnus-group-get-new-news)'",
//   "update": "emacsclient  -e '(gnus-group-get-new-news)'",
//   "expunge": "emacsclient  -e '(gnus-group-get-new-news)'",
// };
exports.boxes = [ "INBOX" ];
var child_process = require('child_process');

function getStdout(cmd) {
  var stdout = child_process.execSync(cmd);
  return stdout.toString().trim();
}

exports.host = "imap.gmail.com";
exports.port = 993;
exports.tls = true;
exports.username = "[email protected]";
exports.password = getStdout("pass imap.gmail.com/[email protected]");
exports.onNotify = "mbsync sync-kaaiot";
// exports.onNotifyPost = {
//   "mail": "emacsclient  -e '(gnus-group-get-new-news)'",
//   "update": "emacsclient  -e '(gnus-group-get-new-news)'",
//   "expunge": "emacsclient  -e '(gnus-group-get-new-news)'",
// };
exports.boxes = [ "INBOX" ];

Dovecot

Dovecot serves fetched mail to gnus.

{
  services.dovecot2 = {
    enable = true;
    enablePop3 = false;
    enableImap = true;
    mailLocation = "maildir:~/Mail:LAYOUT=fs";
  };

  # dovecot has some helpers in libexec (namely, imap).
  environment.pathsToLink = [ "/libexec/dovecot" ];
}

msmtp

Msmtp is used to send mail.

{
  environment.systemPackages = [
    pkgs.msmtp
  ];
}

Config file is .msmtprc.

defaults
auth on
tls on
tls_starttls off
tls_trust_file /etc/ssl/certs/ca-certificates.crt
logfile ~/.msmtp.log

# Gmail
account gmail
host smtp.gmail.com
port 465
from [email protected]
user [email protected]
passwordeval "pass imap.gmail.com/[email protected]"

# KaaIoT
account kaaiot
host smtp.gmail.com
port 465
from [email protected]
user [email protected]
passwordeval "pass imap.gmail.com/[email protected]"

# CyberVision
account cv
host mail.cybervisiontech.com
port 465
from [email protected]
user ashmalko
passwordeval "pass mail.cybervisiontech.com/[email protected]"

notmuch

Notmuch is used for tagging.

{
  environment.systemPackages = [
    pkgs.notmuch
  ];
}

Config file is .notmuch-config.

[user]
name=Alexey Shmalko
[email protected]
[email protected],[email protected]

[database]
path=/home/rasen/Mail

[new]
tags=new;
ignore=.mbsyncstate;.mbsyncstate.lock;.mbsyncstate.new;.mbsyncstate.journal;.uidvalidity;dovecot-uidlist;dovecot.index;dovecot.index.log;dovecot.index.log.2;dovecot.index.cache

[search]
exclude_tags=deleted;spam

[crypto]
gpg_path=gpg2

Environment

General

I definitely use X server:

{
  services.xserver.enable = true;
}

Use English as my only supported locale:

{
  i18n.supportedLocales = [ "en_US.UTF-8/UTF-8" ];
}

Setup timezone:

{
  time.timeZone = "Europe/Kiev";
}

Login manager

I use SLiM. It stands for Simple Login Manager. It’s fast and has little dependencies. The projects is dead since 2014, but still works fine, so I keep using it.

{
  services.xserver.displayManager.slim.enable = true;
}

Window manager

I use awesome wm:

{
  services.xserver.displayManager.slim.enable = true;
  services.xserver.windowManager = {
    default = "awesome";
    awesome = {
      enable = true;
      luaModules = [ pkgs.luaPackages.luafilesystem pkgs.luaPackages.cjson ];
    };
  };
}

Disabling xterm makes awesome wm a default choice in slim:

{
  services.xserver.desktopManager.xterm.enable = false;
}

These packages are used by my awesome wm setup:

{
  environment.systemPackages = [
    pkgs.wmname
    pkgs.xclip
    pkgs.escrotum

    # Control screen brightness
    pkgs.xorg.xbacklight
  ];
}

Keyboard

Layouts

I use English and Ukrainian layouts. I also use Russian symbols, but they are on the third level.

{
  services.xserver.layout = "us,ua";
  services.xserver.xkbVariant = "workman,";

  # Use same config for linux console
  i18n.consoleUseXkbConfig = true;
}

I toggle between them with either Caps Lock, or Menu key—I have two different keyboards, and one doesn’t have Menu when Caps Lock is too far on the second. I never use Caps Lock–the feature, so it’s nice to have Caps LED indicate alternate layouts.

{
  services.xserver.xkbOptions = "grp:caps_toggle,grp:menu_toggle,grp_led:caps";
}

Layout indicator

I use built-in awesome layout indicator. See .config/awesome/rc.lua for more details.

Custom keyboard modification

xkb_keymap {
  xkb_keycodes  { include "evdev+aliases(qwerty)"};
  xkb_types     { include "complete"};
  xkb_compat    { include "complete+ledcaps(group_lock)"};
  xkb_geometry  { include "pc(pc105)"};

  xkb_symbols "my" {
    include "pc+us+ru:2+inet(evdev)+group(menu_toggle)"
  };
};

(Not sure I actually use it.)

Redshift

Redshift adjusts the color temperature of the screen according to the position of the sun.

Blue light blocks melatonin (sleep harmone) secretion, so you feel less sleepy when you stare at computer screen. Redshift blocks some blue light (making screen more red), which should improve melatonin secretion and restore sleepiness (which is a good thing).

{
  services.redshift = {
    enable = true;
    latitude = "50.4500";
    longitude = "30.5233";
  };
}

Look and Feel

Qt theme

This makes apps look like in KDE:

{
  environment.systemPackages = [
    pkgs.oxygen-icons5
  ];
}

The next is a back-port of oxygen-gtk theme, which was removed with remove of KDE4 from nixpkgs.

(let
  oldpkgs = import (pkgs.fetchFromGitHub {
    owner = "NixOS";
    repo = "nixpkgs-channels";
    rev = "1aa77d0519ae23a0dbef6cab6f15393cfadcc454";
    sha256 = "1gcd8938n3z0a095b0203fhxp6lddaw1ic1rl33q441m1w0i19jv";
  }) { config = config.nixpkgs.config; };
in {
  environment.systemPackages = [ oldpkgs.oxygen-gtk2 oldpkgs.oxygen-gtk3 ];

  environment.shellInit = ''
    export GTK_PATH=$GTK_PATH:${oldpkgs.oxygen_gtk}/lib/gtk-2.0
    export GTK2_RC_FILES=$GTK2_RC_FILES:${oldpkgs.oxygen_gtk}/share/themes/oxygen-gtk/gtk-2.0/gtkrc
  '';
})

Find a way to make deadbeef use oxygen theme

The theme has some issues with deadbeef, so I install adwaita icons to make deadbeef usable.

{
  environment.systemPackages = [
    pkgs.gnome3.adwaita-icon-theme
  ];
}

Fonts

I’m not a font guru, so I just stuffed a bunch of random fonts in here.

{
  fonts = {
    enableCoreFonts = true;
    enableFontDir = true;
    enableGhostscriptFonts = false;

    fonts = with pkgs; [
      inconsolata
      corefonts
      dejavu_fonts
      source-code-pro
      ubuntu_font_family
      unifont
    ];
  };
}

HiDPI

These are for omicron-only.

Xft.dpi: 276
Xcursor.size: 64
{
  i18n = {
    consolePackages = [
      pkgs.terminus_font
    ];
    consoleFont = "ter-132n";
  };
}
{
  boot.loader.grub.gfxmodeEfi = "1024x768";
}
{
  services.xserver.dpi = 276;
}

Applications

Here go applications (almost) every normal user needs.

GPG

{
  environment.systemPackages = [
    pkgs.gnupg
  ];
  programs.gnupg.agent = {
    enable = true;
  };
}

password-store

Install password-store along with one-time password extension.

{
  environment.systemPackages = [
    pkgs.pass
    pkgs.pass-otp
  ];
}

The following section is required for pass to find the OTP extension.

{
  environment.pathsToLink = [ "/lib/password-store/extensions" ];
  environment.variables = {
    PASSWORD_STORE_EXTENSIONS_DIR = "/nix/var/nix/profiles/system/sw/lib/password-store/extensions";
    PASSWORD_STORE_ENABLE_EXTENSIONS = "true";
  };
}

KDE apps

I don’t use full KDE but some apps are definitely nice.

{
  environment.systemPackages = [
    pkgs.gwenview
    pkgs.dolphin
    pkgs.kdeFrameworks.kfilemetadata
    pkgs.filelight
    pkgs.shared_mime_info
  ];
}

KDE apps might have issues with mime types without this:

{
  environment.pathsToLink = [ "/share" ];
}

Browsers

Google Chrome

Google Chrome used to be my default browser and I still use it from time to time.

{
  environment.systemPackages = [
    pkgs.google-chrome
  ];
}

Firefox

I use Firefox Quantum as my default browser now.

{
  environment.systemPackages = [
    pkgs.firefox-devedition-bin
  ];
}

I also need an old Firefox with Java support. I use Firefox Extended Support Release for that. It clashes with firefox-devedition, so I do some renaming .

{
  nixpkgs.config.firefox = {
    icedtea = true;
  };

  environment.systemPackages = [
    (pkgs.runCommand "firefox-esr" { preferLocalBuild = true; } ''
      mkdir -p $out/bin
      ln -s ${pkgs.firefox-esr}/bin/firefox $out/bin/firefox-esr
    '')
  ];
}

Zathura

Zathura is a cool document viewer with Vim-like bindings.

{
  environment.systemPackages = [
    pkgs.zathura
  ];
}

Enable incremental search (Zathura’s config goes to ~/.config/zathura/zathurarc).

set incremental-search true

These are my rebinding for Workman layout (swap j/k):

map j scroll up
map k scroll down

Screen locking

Slock

Slock is a simple X display locker and should probably not crash as xscreensaver does.

Slock tries to disable OOM killer (so the locker is not killed when memory is low) and this requires a suid flag for executable. Otherwise, you get the following message:

slock: unable to disable OOM killer. Make sure to suid or sgid slock.
{
  security.wrappers = {
    slock = {
      source = "${pkgs.slock}/bin/slock";
    };
  };
}

xss-lock

xss-lock is a small utility to plug a screen locker into screen saver extension for X. This automatically activates selected screensaver after a period of user inactivity, or when system goes to sleep.

{
  environment.systemPackages = [
    pkgs.xss-lock
  ];
}

Other applications

Don’t require additional setup.

{
  environment.systemPackages = [
    pkgs.libreoffice
    pkgs.qbittorrent
    pkgs.calibre
    pkgs.mnemosyne
    pkgs.deadbeef

    pkgs.vlc
    pkgs.mplayer
    pkgs.smplayer

    pkgs.alarm-clock-applet

    # Used by naga setup
    pkgs.xdotool

    pkgs.hledger
    pkgs.drive
  ];
}

Development

Editors

I’m a seasoned Vim user, but I’ve switched to emacs.

{
  environment.systemPackages = [
    (pkgs.vim_configurable.override { python3 = true; })
  ];
}

Start emacs as a daemon:

{
  services.emacs = {
    enable = true;
    defaultEditor = true;
    package = (pkgs.emacsPackagesNgGen pkgs.emacs).emacsWithPackages (epkgs:
      [
        epkgs.orgPackages.org-plus-contrib

        epkgs.melpaStablePackages.use-package

        pkgs.ycmd
        pkgs.rustracer
      ]
    );
  };
}

rxvt-unicode

I use urxvt as my terminal emulator:

{
  environment.systemPackages = [
    pkgs.rxvt_unicode
  ];
}

Urxvt gets its setting from .Xresources file. If you ever want to reload it on-the-fly, type the following (or press C-c C-c if you’re reading this document in emacs now):

xrdb ~/.Xresources

General setup

See rxvt-unicode documentation for the full reference.

urxvt.loginShell:         true
urxvt.saveLines:         65535
urxvt.urgentOnBell:       true

urxvt.scrollBar:         false
urxvt.scrollTtyOutput:   false
urxvt.scrollTtyKeypress:  true
urxvt.secondaryScroll:    true

The next piece disables annoying message when pressing Ctrl+Shift:

urxvt.iso14755: False

Copy-paste with Ctrl+Shift+C, Ctrl+Shift+V:

From urxvt-perls:

Since version 9.20 rxvt-unicode natively supports copying to and pasting from the CLIPBOARD buffer with the Ctrl-Meta-c and Ctrl-Meta-v key bindings. The clipboard.autocopy setting is provided by the selection_to_clipboard extension shipped with rxvt-unicode.

That means, I don’t need perl extensions at all.

Font

I use Terminus font.

{
  fonts = {
    fonts = [
      pkgs.powerline-fonts
      pkgs.terminus_font
    ];
  };
}
URxvt.font: xft:Terminus:normal:size=12

Color theme

I like Molokai color theme.

URxvt*background: #101010
URxvt*foreground: #d0d0d0
URxvt*color0:     #101010
URxvt*color1:     #960050
URxvt*color2:     #66aa11
URxvt*color3:     #c47f2c
URxvt*color4:     #30309b
URxvt*color5:     #7e40a5
URxvt*color6:     #3579a8
URxvt*color7:     #9999aa
URxvt*color8:     #303030
URxvt*color9:     #ff0090
URxvt*color10:    #80ff00
URxvt*color11:    #ffba68
URxvt*color12:    #5f5fee
URxvt*color13:    #bb88dd
URxvt*color14:    #4eb4fa
URxvt*color15:    #d0d0d0

fish

fish is a cool shell, I use it as my default for day-to-day work.

{
  programs.fish.enable = true;
  users.defaultUserShell = pkgs.fish;
}

Show QR codes

The next section goes to .config/fish/functions/showqr.fish. That’s a function I use for displaying arbitrary text (mainly passwords stored with pass) as a QR code without any temporary files. (tr is used to drop trailing newline.)

function showqr
  tr -d '\n' | qrencode -t png -o - | feh -
end

It uses qrencode and feh packages:

{
  environment.systemPackages = [
    pkgs.qrencode
    pkgs.feh
  ];
}

Zsh

Zsh is my secondary shell. I use it when I need sh compatibility. (fish is not sh compliant.)

{
  programs.zsh.enable = true;
}

Prompt

source $HOME/.zsh/git-prompt/zshrc.sh

PROMPT='%B%F{green}%n@%m%k %B%F{blue}%1~%b$(git_super_status) %B%F{blue}%# %b%f%k'
RPROMPT="[%?] %T"

The ~/.zsh/git-prompt/ is a submodule, so don’t forget to initialize it!

git submodule update --init --recursive

Aliases

Nothing special, but g=git is a real timesaver.

alias ls='ls --color=auto'
alias grep='grep --color=auto'

alias g="git"

PATH

Install stuff in ~/.local/; ~/bin/ is for my helper scripts (linked to bin directory in dotfiles repo).

export PATH="${HOME}/bin:${PATH}"
export PATH="${HOME}/.local/bin:${PATH}"

export LD_LIBRARY_PATH="${HOME}/.local/lib:${LD_LIBRARY_PATH}"

Other

This part was written long time ago; I’m not sure I understand and use all of it:

autoload -U compinit promptinit
autoload -U colors
compinit
promptinit
colors

# Lines configured by zsh-newuser-install
HISTFILE=~/.histfile
HISTSIZE=1000
SAVEHIST=1000
setopt appendhistory autocd
unsetopt beep
bindkey -e
# End of lines configured by zsh-newuser-install
# The following lines were added by compinstall
zstyle :compinstall filename '/home/rasen/.zshrc'

zstyle ':completion:*:descriptions' format '%U%B%d%b%u'
zstyle ':completion:*:warnings' format '%BSorry, no matches for: %d%b'

setopt correct
setopt hist_ignore_space
setopt hist_ignore_all_dups
setopt extendedglob

setopt listpacked

zstyle ':completion:*' use-cache on
zstyle ':completion:*' cache-path ~/.zsh/cache

zstyle ':completion:*' completer _complete _match _approximate
zstyle ':completion:*:match:*' original only
zstyle ':completion:*:approximate:*' max-errors 1 numeric

zstyle ':completion:*:functions' ignored-patters '_*'

xdvi() { command xdvi ${*:-*.dvi(om[1])} }
zstyle ':completion:*:*:xdvi:*' menu yes select
zstyle ':completion:*:*:xdvi:*' file-sort time

zstyle ':completion:*' squeeze-slashes true

# End of lines added by compinstall
# create a zkbd compatible hash;
# to add other keys to this hash, see: man 5 terminfo
typeset -A key

key[Home]=${terminfo[khome]}

key[End]=${terminfo[kend]}
key[Insert]=${terminfo[kich1]}
key[Delete]=${terminfo[kdch1]}
key[Up]=${terminfo[kcuu1]}
key[Down]=${terminfo[kcud1]}
key[Left]=${terminfo[kcub1]}
key[Right]=${terminfo[kcuf1]}
key[PageUp]=${terminfo[kpp]}
key[PageDown]=${terminfo[knp]}

# setup key accordingly
[[ -n "${key[Home]}"    ]]  && bindkey  "${key[Home]}"    beginning-of-line
[[ -n "${key[End]}"     ]]  && bindkey  "${key[End]}"     end-of-line
[[ -n "${key[Insert]}"  ]]  && bindkey  "${key[Insert]}"  overwrite-mode
[[ -n "${key[Delete]}"  ]]  && bindkey  "${key[Delete]}"  delete-char
[[ -n "${key[Up]}"      ]]  && bindkey  "${key[Up]}"      up-line-or-history
[[ -n "${key[Down]}"    ]]  && bindkey  "${key[Down]}"    down-line-or-history
[[ -n "${key[Left]}"    ]]  && bindkey  "${key[Left]}"    backward-char
[[ -n "${key[Right]}"   ]]  && bindkey  "${key[Right]}"   forward-char

# Finally, make sure the terminal is in application mode, when zle is
# active. Only then are the values from $terminfo valid.
if (( ${+terminfo[smkx]} )) && (( ${+terminfo[rmkx]} )); then
    function zle-line-init () {
        printf '%s' "${terminfo[smkx]}"
    }
    function zle-line-finish () {
        printf '%s' "${terminfo[rmkx]}"
    }
    zle -N zle-line-init
    zle -N zle-line-finish
fi

TODO review this

git

{
  environment.systemPackages = [
    pkgs.gitFull
    pkgs.gitg
  ];
}

Basic info: my name, email, ui, editor, rerere.

[user]
    name = Alexey Shmalko
    email = [email protected]

[sendemail]
    smtpencryption = ssl
    smtpserver = smtp.gmail.com
    smtpuser = [email protected]
    smtpserverport = 465

[color]
    ui = true

[core]
    editor = vim

[push]
    default = simple

[pull]
    rebase = true

[rebase]
    autostash = true

[rerere]
    enabled = true

Configure signing with gpg.

[user]
    signingkey = EB3066C3

[gpg]
    program = gpg2

[push]
    gpgSign = if-asked

I have LOTS of aliases:

[alias]
    cl  = clone
    gh-cl = gh-clone
    cr  = cr-fix
    p   = push
    pl  = pull
    f   = fetch
    fa  = fetch --all
    a   = add
    ap  = add -p
    d   = diff
    dl  = diff HEAD~ HEAD
    ds  = diff --staged
    l   = log --show-signature
    l1  = log -1
    lp  = log -p
    c   = commit
    ca  = commit --amend
    co  = checkout
    cb  = checkout -b
    cm  = checkout origin/master
    de  = checkout --detach
    fco = fetch-checkout
    br  = branch
    s   = status
    re  = reset --hard
    r   = rebase
    rc  = rebase --continue
    ri  = rebase -i
    m   = merge
    t   = tag
    su  = submodule update --init --recursive
    bi  = bisect

Always push to github with ssh keys instead of login/password.

[url "[email protected]:"]
    pushInsteadOf = https://github.com/

tmux

{
  environment.systemPackages = [
    pkgs.tmux
  ];
}

Use C-a as a prefix.

set -g prefix C-a
unbind-key C-b
bind-key C-a send-prefix

TODO describe other settings

# To make vim work properly
set -g default-terminal "screen-256color"

set -g status-keys vi
setw -g mode-keys vi

set -g history-limit 10000

# Start numbering from 1
set -g base-index 1

# Allows for faster key repetition
set -s escape-time 0

bind h select-pane -L
bind j select-pane -D
bind k select-pane -U
bind l select-pane -R

bind-key s split-window
bind-key v split-window -h

bind r source-file ~/.tmux.conf \; display-message "Config reloaded..."

set-window-option -g automatic-rename

Embedded

The following packages provide a termiinal emulator, Open On-Chip Debugger, telnet, and logic analyzer.

{
  environment.systemPackages = [
    pkgs.minicom
    pkgs.openocd
    pkgs.telnet
    pkgs.saleae-logic
  ];
}

To allow user use openocd without sudo, we should add him to plugdev group and install openocd udev rules:

{
  users.extraGroups.plugdev = { };
  users.extraUsers.rasen.extraGroups = [ "plugdev" "dialout" ];

  services.udev.packages = [ pkgs.openocd pkgs.android-udev-rules ];
}

Other terminal goodies

{
  environment.systemPackages = [
    pkgs.wget
    pkgs.htop
    pkgs.psmisc
    pkgs.zip
    pkgs.unzip
    pkgs.unrar
    pkgs.p7zip
    pkgs.irssi
    pkgs.bind
    pkgs.file
    pkgs.which
    pkgs.whois
    pkgs.utillinuxCurses

    pkgs.patchelf

    pkgs.nix-repl
    pkgs.nox

    pkgs.python
    pkgs.python3
  ];
}

Man pages

This install a number of default man pages for the linux/posix system.

{
  environment.systemPackages = [
    pkgs.man-pages
    pkgs.stdman
    pkgs.posix_man_pages
    pkgs.stdmanpages
  ];
}

Games

Steam

We need the following package:

{
  environment.systemPackages = [
    pkgs.steam
  ];
}

It’s also required to enable 32-bit support for opengl and pulseaudio:

{
  hardware.opengl.driSupport32Bit = true;
  hardware.pulseaudio.support32Bit = true;
}

Nethack

I play nethack rarely, but still nice to have my setting in sync.

{
  environment.systemPackages = [
    pkgs.nethack
  ];
}

The following sets my default name, selects a dog, and disables auto-pickup; the last line makes interface a bit friendlier.

OPTIONS=name:rasen
OPTIONS=role:monk, gender:male
OPTIONS=statushilites
OPTIONS=pettype:dog, dogname:Fido
OPTIONS=!autopickup
OPTIONS=lit_corridor, DECgraphics, showscore, showexp, time, color, hilite_pet

Meta

Setup

There is a setup.sh script in this directory. It just links all files to $HOME:

FILES=".vimrc .vim .nvimrc .nvim .gitconfig .zshrc .zsh .tmux.conf .Xresources .config/awesome .config/nvim .nethackrc .emacs.d .ssh bin .config/zathura .irssi .config/xkb .config/fish .msmtprc .notmuch-config .mbsyncrc"

DEST=$1

if [ -z "$DEST" ]; then
    DEST="$HOME"
fi

BASE=$(cd "$(dirname "$0")" && pwd)

ask_install() {
    FILENAME=$1

    LINK="$DEST/$FILENAME"
    TARGET="$BASE/$FILENAME"

    if [ -e $LINK ]; then
        echo "$LINK exists. Skipping..."
    else
        read -r -p "Link $LINK to $TARGET? [y/N] " response
        case $response in
            [yY][eE][sS]|[yY])
                ln -v -s "$TARGET" "$LINK"
                ;;
        esac
    fi
}

for FILE in $FILES; do
    ask_install $FILE
done

Install fisherman

Fisherman is a plugin manager for fish.

if [ ! -e "$DEST/.config/fish/functions/fisher.fish" ]; then
    read -r -p "Install fisherman and all plugins? [y/N] " response
    case $response in
        [yY][eE][sS]|[yY])
            curl -Lo "$DEST/.config/fish/functions/fisher.fish" --create-dirs \
                https://raw.githubusercontent.com/fisherman/fisherman/master/fisher.fish
            fish -c fisher
            ;;
    esac
fi

Private

-----BEGIN PGP MESSAGE-----

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 c3WAQClCY48= =xEHG -----END PGP MESSAGE-----

Releases

No releases published

Packages

No packages published

Languages

  • Nix 62.2%
  • Emacs Lisp 13.6%
  • Lua 8.2%
  • Perl 6.2%
  • Python 5.2%
  • Vim Script 2.8%
  • Other 1.8%