#+title: NixOS Configuration
#+AUTHOR: Preston Pan
#+DESCRIPTION: My NixOS system, written entirely in a literate configuration
#+html_head:
* Introduction
This is my NixOS configuration. It is a part of my monorepo, and this file automatically tangles
to all the under the nix/ directory in my monorepo [[https://ret2pop.net/gitweb/monorepo.git][git repository]]. My monorepo also stores my
website, as my website stores my [[file:elfeed.org][elfeed]] and [[file:emacs.org][emacs]] configurations. Additionally, I want to track
my emacs configuration with my Nix configuration. Having them in one repository means that my
emacs configuration is pinned to my flake.
Hence, my monorepo serves a dual purpose, as do many of the files within my monorepo. They are
often data files used in my configuration (i.e. emacs, elfeed, org-roam, agenda, journal, etc...)
and they are webpages as well. This page is one such example of this concept.
* Configurables
We start with some configurable variables (you can change these if you want to use this configuration yourself):
#+begin_src nix :tangle ../nix/flakevars.nix
let
# I'm ret2pop! What's your name?
internetName = "ret2pop";
in
{
# Name of spontaneity box
remoteHost = "${internetName}.net";
# Your internet name
internetName = internetName;
# Name of your organization
orgHost = "nullring.xyz";
# Hostnames of my systems
hostnames = [
"affinity"
"continuity"
"spontaneity"
"installer"
];
}
#+end_src
* Flake.nix
The flake is the entry point of the NixOS configuration. Here, I have a list of all the systems
that I use with all the modules that they use. My NixOS configuration is heavily modularized,
so that adding new configurations that add modifications is made simple.
and now for the main flake:
#+begin_src nix :tangle ../nix/flake.nix
{
description = "Emacs centric configurations for a complete networked system";
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
nur.url = "github:nix-community/NUR";
sops-nix.url = "github:Mic92/sops-nix";
scripts.url = "github:ret2pop/scripts";
wallpapers.url = "github:ret2pop/wallpapers";
sounds.url = "github:ret2pop/sounds";
deep-research.url = "github:ret2pop/ollama-deep-researcher";
impermanence.url = "github:nix-community/impermanence";
nix-topology = {
url = "github:oddlama/nix-topology";
inputs.nixpkgs.follows = "nixpkgs";
};
home-manager = {
url = "github:nix-community/home-manager/release-25.05";
inputs.nixpkgs.follows = "nixpkgs";
};
disko = {
url = "github:nix-community/disko";
inputs.nixpkgs.follows = "nixpkgs";
};
lanzaboote = {
url = "github:nix-community/lanzaboote/v0.4.1";
inputs.nixpkgs.follows = "nixpkgs";
};
nixos-dns = {
url = "github:Janik-Haag/nixos-dns";
inputs.nixpkgs.follows = "nixpkgs";
};
};
outputs = {
self,
nixpkgs,
home-manager,
nur,
disko,
lanzaboote,
sops-nix,
nix-topology,
nixos-dns,
deep-research,
impermanence,
...
}
@attrs:
let
vars = import ./flakevars.nix;
system = "x86_64-linux";
pkgs = import nixpkgs { inherit system; };
generate = nixos-dns.utils.generate nixpkgs.legacyPackages."${system}";
dnsConfig = {
inherit (self) nixosConfigurations;
extraConfig = import ./dns/default.nix;
};
# function that generates all systems from hostnames
mkConfigs = map (hostname: {name = "${hostname}";
value = nixpkgs.lib.nixosSystem {
inherit system;
specialArgs = attrs;
modules = if (hostname == "installer") then [
(./. + "/systems/${hostname}/default.nix")
{ networking.hostName = "${hostname}"; }
nix-topology.nixosModules.default
] else [
{
environment.systemPackages = with nixpkgs.lib; [
deep-research.packages."${system}".deep-research
];
}
impermanence.nixosModules.impermanence
nix-topology.nixosModules.default
lanzaboote.nixosModules.lanzaboote
disko.nixosModules.disko
home-manager.nixosModules.home-manager
sops-nix.nixosModules.sops
nixos-dns.nixosModules.dns
{
nixpkgs.overlays = [ nur.overlays.default ];
home-manager.extraSpecialArgs = attrs // { systemHostName = "${hostname}"; };
networking.hostName = "${hostname}";
}
(./. + "/systems/${hostname}/default.nix")
];
};
});
mkDiskoFiles = map (hostname: {
name = "${hostname}";
value = self.nixosConfigurations."${hostname}".config.monorepo.vars.diskoSpec;
});
in
{
nixosConfigurations = builtins.listToAttrs (mkConfigs vars.hostnames);
evalDisko = builtins.listToAttrs (mkDiskoFiles (builtins.filter (x: x != "installer") vars.hostnames));
topology."${system}" = import nix-topology {
pkgs = import nixpkgs {
inherit system;
overlays = [ nix-topology.overlays.default ];
};
modules = [
./topology/default.nix
{ nixosConfigurations = self.nixosConfigurations; }
];
};
devShell."${system}" = with pkgs; mkShell {
buildInputs = [
fira-code
python3
poetry
statix
deadnix
];
};
packages."${system}" = {
zoneFiles = generate.zoneFiles dnsConfig;
octodns = generate.octodnsConfig {
inherit dnsConfig;
config = {
providers = {
cloudflare = {
class = "octodns_cloudflare.CloudflareProvider";
token = "env/CLOUDFLARE_TOKEN";
};
config = {
check_origin = false;
};
};
};
zones = {
"${vars.remoteHost}." = nixos-dns.utils.octodns.generateZoneAttrs [ "cloudflare" ];
"${vars.orgHost}." = nixos-dns.utils.octodns.generateZoneAttrs [ "cloudflare" ];
};
};
};
};
}
#+end_src
Note that the configurations are automatically generated with he mkConfigs function, and the final disko output is automatically generated
with mkDiskoFiles.
* Sops Configuration
In order to use the sops configuration, you must change the age public key to the one that
you own:
#+begin_src yaml :tangle ../nix/.sops.yaml
keys:
- &primary age165ul43e8rc0qwzz2f2q9cw02psm2mkudsrwavq2e0pxs280p64yqy2z0dr
creation_rules:
- path_regex: secrets/secrets.yaml$
key_groups:
- age:
- *primary
#+end_src
also note that you will have to write your own secrets.yaml file, with an entry called ~mail~,
which is used for the imaps and smtps password.
* Nix DNS
#+begin_src nix :tangle ../nix/dns/default.nix
{
defaultTTL = 120;
}
#+end_src
* Nix Topology
Nix Topology generates a nice graph of all my hosts. You can view this
graph by running ~nix build .#topology.x86_64-linux.config.output~.
#+begin_src nix :tangle ../nix/topology/default.nix
{ config, ... }:
let
inherit
(config.lib.topology);
in
{
nodes = {
spontaneity = {
interfaces.wan.network = "remote";
};
installer = {
interfaces.lan.network = "home";
};
affinity = {
interfaces.lan = {
network = "home";
physicalConnections = [
{
node = "spontaneity";
interface = "wan";
}
{
node = "installer";
interface = "lan";
}
];
};
};
continuity = {
interfaces.lan = {
network = "home";
physicalConnections = [
{
node = "spontaneity";
interface = "wan";
}
{
node = "affinity";
interface = "lan";
}
];
};
};
};
networks = {
home = {
name = "Home Network";
cidrv4 = "192.168.1.1/24";
};
remote = {
name = "Remote Network";
cidrv4 = "144.202.27.169/32";
};
};
}
#+end_src
* Modules
** Vars
Variables used for regular configuration in your system ~default.nix~ file. The options are
largely self-documenting.
#+begin_src nix :tangle ../nix/modules/vars.nix
{ lib, ... }:
let
vars = import ../flakevars.nix;
in
{
options.monorepo.vars = {
device = lib.mkOption {
type = lib.types.str;
default = "/dev/sda";
example = "/dev/nvme0n1";
description = "device that NixOS is installed to";
};
internetName = lib.mkOption {
type = lib.types.str;
default = "${vars.internetName}";
example = "myinternetname";
description = "Internet name to be used for internet usernames";
};
repoName = lib.mkOption {
type = lib.types.str;
default = "monorepo";
example = "myreponame";
description = "Name of this repository";
};
fileSystem = lib.mkOption {
type = lib.types.str;
default = "ext4";
example = "btrfs";
description = "filesystem to install with disko";
};
diskoSpec = lib.mkOption {
type = lib.types.attrs;
description = "retains a copy of the disko spec for reflection";
};
userName = lib.mkOption {
type = lib.types.str;
default = "preston";
example = "myUser";
description = "system username";
};
fullName = lib.mkOption {
type = lib.types.str;
default = "Preston Pan";
example = "John Doe";
description = "Full Name";
};
gpgKey = lib.mkOption {
type = lib.types.str;
default = "AEC273BF75B6F54D81343A1AC1FE6CED393AE6C1";
example = "1234567890ABCDEF...";
description = "GPG key fingerprint";
};
remoteHost = lib.mkOption {
type = lib.types.str;
default = "${vars.remoteHost}";
example = "example.com";
description = "Address to push to and pull from for website and git repos";
};
orgHost = lib.mkOption {
type = lib.types.str;
default = "${vars.orgHost}";
example = "orgname.org";
description = "Domain name of your organization";
};
timeZone = lib.mkOption {
type = lib.types.str;
default = "America/Vancouver";
example = "America/Chicago";
description = "Linux timezone";
};
monitors = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [
"HDMI-A-1"
"eDP-1"
"DP-2"
"DP-3"
"DP-4"
"LVDS-1"
];
example = [];
description = "Monitors that waybar will use";
};
};
}
#+end_src
** Default Profile
Again, these are self documenting variables that you may see used below. These are to be used
under ~default.nix~ in the ~systems~ folder.
#+begin_src nix :tangle ../nix/modules/default.nix
{ lib, config, pkgs, ... }:
{
imports = [
./configuration.nix
./vars.nix
];
options = {
monorepo = {
profiles = {
cuda.enable = lib.mkEnableOption "Enables CUDA support";
documentation.enable = lib.mkEnableOption "Enables documentation on system.";
secureBoot.enable = lib.mkEnableOption "Enables secure boot. See sbctl.";
pipewire.enable = lib.mkEnableOption "Enables pipewire low latency audio setup";
tor.enable = lib.mkEnableOption "Enables tor along with torsocks";
home.enable = lib.mkEnableOption "Enables home user";
server.enable = lib.mkEnableOption "Enables server services";
ttyonly.enable = lib.mkEnableOption "TTY only, no xserver";
grub.enable = lib.mkEnableOption "Enables grub instead of systemd-boot";
workstation.enable = lib.mkEnableOption "Enables workstation services";
impermanence.enable = lib.mkEnableOption "Enables imperamanence";
};
};
};
config = {
environment.systemPackages = lib.mkIf config.monorepo.profiles.documentation.enable ((with pkgs; [
linux-manual
man-pages
man-pages-posix
iproute2
silver-searcher
ripgrep
]) ++
(if (config.monorepo.vars.fileSystem == "btrfs") then with pkgs; [
btrfs-progs
btrfs-snap
btrfs-list
btrfs-heatmap
] else []));
boot.loader.grub = lib.mkIf config.monorepo.profiles.grub.enable {
enable = true;
};
monorepo = {
profiles = {
documentation.enable = lib.mkDefault true;
pipewire.enable = lib.mkDefault true;
tor.enable = lib.mkDefault true;
home.enable = lib.mkDefault true;
impermanence.enable = lib.mkDefault false;
};
};
};
}
#+end_src
** X11
My Xorg configuration is used as a backup for when wayland applications don't work. Note that
using this configuration is extremely inefficient and my i3 configuration is unoptimized.
Still, it is suitable for using Krita.
#+begin_src nix :tangle ../nix/modules/xserver.nix
{ lib, config, pkgs, ... }:
{
services.xserver = {
enable = (! config.monorepo.profiles.ttyonly.enable);
displayManager = {
startx.enable = (! config.monorepo.profiles.ttyonly.enable);
};
windowManager = {
i3 = {
enable = ! config.monorepo.profiles.ttyonly.enable;
package = pkgs.i3-gaps;
};
};
desktopManager = {
runXdgAutostartIfNone = true;
};
xkb = {
layout = "us";
variant = "";
options = "caps:escape";
};
videoDrivers = (if config.monorepo.profiles.cuda.enable then [ "nvidia" ] else []);
};
}
#+end_src
You should add your own video drivers in a custom machine configuration.
** Containers
In order to run docker/podman containers, I need this file:
#+begin_src nix :tangle ../nix/modules/docker.nix
{ lib, config, vars, ... }:
{
virtualisation = {
oci-containers = {
backend = "podman";
containers = {};
};
containers.enable = true;
podman = {
enable = true;
dockerCompat = true;
defaultNetwork.settings.dns_enabled = true;
};
};
}
#+end_src
** Pipewire
My low latency pipewire configuration is used for music production, as well as for regular
desktop usage. Pipewire is much better than pulseaudio because it supports jack with the same
underlying interface and it breaks significantly less often.
#+begin_src nix :tangle ../nix/modules/pipewire.nix
{ lib, config, ... }:
{
services.pipewire = {
enable = lib.mkDefault config.monorepo.profiles.pipewire.enable;
alsa = {
enable = true;
support32Bit = true;
};
pulse.enable = true;
jack.enable = true;
wireplumber.enable = true;
extraConfig = {
pipewire."92-low-latency" = {
"context.properties" = {
"default.clock.rate" = 48000;
"default.clock.quantum" = 256;
"default.clock.min-quantum" = 32;
"default.clock.max-quantum" = 512;
};
pipewire-pulse."92-low-latency" = {
"context.properties" = [
{
name = "libpipewire-module-protocol-pulse";
args = { };
}
];
"pulse.properties" = {
"pulse.min.req" = "32/48000";
"pulse.default.req" = "32/48000";
"pulse.max.req" = "32/48000";
"pulse.min.quantum" = "32/48000";
"pulse.max.quantum" = "32/48000";
};
"stream.properties" = {
"node.latency" = "32/48000";
"resample.quality" = 1;
};
};
};
};
};
}
#+end_src
** SSH
My SSH daemon configuration.
#+begin_src nix :tangle ../nix/modules/ssh.nix
{ config, lib, ... }:
{
services.openssh = {
enable = true;
settings = {
PasswordAuthentication = lib.mkDefault (! config.monorepo.profiles.server.enable);
AllowUsers = [ config.monorepo.vars.userName "root" "git" ];
PermitRootLogin = "prohibit-password";
KbdInteractiveAuthentication = false;
};
};
}
#+end_src
** Tor
This is my tor configuration, used for my cryptocurrency wallets and whatever else I want
it to do.
#+begin_src nix :tangle ../nix/modules/tor.nix
{ config, lib, ... }:
{
services.tor = {
enable = lib.mkDefault config.monorepo.profiles.tor.enable;
openFirewall = true;
client = {
enable = lib.mkDefault config.monorepo.profiles.tor.enable;
socksListenAddress = {
IsolateDestAddr = true;
addr = "127.0.0.1";
port = 9050;
};
dns.enable = true;
};
torsocks = {
enable = lib.mkDefault config.monorepo.profiles.tor.enable;
server = "127.0.0.1:9050";
};
};
}
#+end_src
** Kubo IPFS
I use IPFS for my website and also for my ISOs for truly declarative and deterministic
configuration. NixOS might be moving to IPFS for binary cache distribution and package
distribution soon, and I'm waiting on that.
#+begin_src nix :tangle ../nix/modules/kubo.nix
{ config, pkgs, lib, ... }:
{
services.kubo = {
enable = lib.mkDefault config.monorepo.profiles.workstation.enable;
};
}
#+end_src
** TODO Murmur
#+begin_src nix :tangle ../nix/modules/murmur.nix
{ lib, config, ... }:
{
services.murmur = {
enable = lib.mkDefault config.monorepo.profiles.server.enable;
logFile = "/var/log/murmur.log";
openFirewall = true;
hostName = "0.0.0.0";
welcometext = "Wecome to the Null Murmur instance!";
registerName = "nullring";
registerHostname = "${config.monorepo.vars.orgHost}";
sslCert = "/var/lib/acme/${config.monorepo.vars.orgHost}/fullchain.pem";
sslKey = "/var/lib/acme/${config.monorepo.vars.orgHost}/sslKey.pem";
};
}
#+end_src
** TODO i2pd
I use i2p for some p2p connections. We enable it with the server profile:
#+begin_src nix :tangle ../nix/modules/i2pd.nix
{ config, lib, ... }:
{
services.i2pd = {
enable = lib.mkDefault config.monorepo.profiles.server.enable;
address = "0.0.0.0";
inTunnels = {
};
outTunnels = {
};
};
}
#+end_src
** TODO Icecast
This is an internet radio which will host a ton of music.
#+begin_src nix :tangle ../nix/modules/icecast.nix
{ lib, config, ... }:
{
services.icecast = {
enable = lib.mkDefault config.monorepo.profiles.server.enable;
listen.address = "0.0.0.0";
extraConfig = ''
0
/stream.m3u
3600
'';
};
admin.password = "changeme";
}
#+end_src
** IRC
A great protocol. It's the most widely usable by any netizen, as it is
just pure plaintext and the operating costs are trivial.
*** NgIRCD
I run my own IRC server to bridge with my Matrix server and my discord guild.
#+begin_src nix :tangle ../nix/modules/ngircd.nix
{ lib, config, ... }:
{
services.ngircd = {
enable = lib.mkDefault config.monorepo.profiles.server.enable;
config = ''
[Global]
Name = ${config.monorepo.vars.orgHost}
Info = NullRing IRC Instance
Listen = 0.0.0.0
MotdFile = /etc/motd.txt
Network = NullRing
Ports = 6667
[Options]
PAM = no
[SSL]
CertFile = /var/lib/acme/${config.monorepo.vars.orgHost}/fullchain.pem
CipherList = HIGH:!aNULL:@STRENGTH:!SSLv3
KeyFile = /var/lib/acme/${config.monorepo.vars.orgHost}/key.pem
Ports = 6697
'';
};
environment.etc."motd.txt" = {
source = ../data/motd.txt;
mode = "644";
user = "ngircd";
group = "ngircd";
};
}
#+end_src
*** MOTD
I also have a MOTD file that I want to add, which displays when users
connect to the server:
#+begin_src fundamental :tangle ../nix/data/motd.txt
Welcome to the NullRing experience!
The main channel is #nullring; we're glad to have you!
Rules:
1. Don't be annoying.
2. No illegal content.
And if you're here to have constructive, philisophical and theoretical
conversations, this is the place for you!
#+end_src
*** ZNC
I want to be able to create some sort of identity persistence on IRC
for users:
#+begin_src nix :tangle ../nix/modules/znc.nix
{ lib, config, ... }:
{
services.znc = {
enable = lib.mkDefault config.monorepo.profiles.server.enable;
openFirewall = true;
confOptions = {
useSSL = true;
passBlock = ''
Method = sha256
Hash = d4abdd69aa24de69693885c5bd83a4a0e9ee989e1a69a905041b0dad9abc06ea
Salt = sDY,?H5AxC-!gH3a.:)D
'';
modules = [
"partyline"
"webadmin"
"adminlog"
"log"
];
networks = {
"libera" = {
server = "irc.libera.chat";
port = 6697;
useSSL = true;
modules = [ "simple_away" ];
};
};
};
};
}
#+end_src
Note that the password hash and whatnot is completely random so there
is almost no point to cracking it with hashcat.
** Conduit
This is a modern matrix server that is meant to be lightweight while
still federating and hosting the same protocol.
#+begin_src nix :tangle ../nix/modules/conduit.nix
{ config, lib, ... }:
{
services.matrix-conduit = {
enable = lib.mkDefault config.monorepo.profiles.server.enable;
settings.global = {
server_name = "matrix.${config.monorepo.vars.orgHost}";
trusted_servers = [
"matrix.org"
"nixos.org"
"conduit.rs"
];
address = "0.0.0.0";
port = 6167;
allow_registration = false;
};
};
}
#+end_src
** Honk
In order to connect to activitypub:
#+begin_src nix :tangle ../nix/modules/honk.nix
{ lib, config, ... }:
{
services.honk = {
enable = config.monorepo.vars.ttyonly;
servername = "ret2pop.net";
username = "ret2pop";
};
}
#+end_src
** Matterbridge
I want to connect IRC to discord with matterbridge.
#+begin_src nix :tangle ../nix/modules/matterbridge.nix
{ lib, config, ... }:
{
services.matterbridge = {
enable = lib.mkDefault config.monorepo.profiles.server.enable;
configPath = "/etc/matterbridge.toml";
};
}
#+end_src
*** Mautrix
I use this bridge to bridge myself from Matrix to Discord and vise versa, because Matterbridge is not maintained very well and therefore
does not support conduit at the moment. Note that this is not fully declarative and requires that you add
~/var/lib/mautrix-discord/discord-registration.yaml~ as an appservice to conduit.
#+begin_src nix :tangle ../nix/modules/mautrix.nix
{ lib, config, ... }:
{
services.mautrix-discord = {
enable = lib.mkDefault config.monorepo.profiles.server.enable;
environmentFile = "/etc/mautrix";
settings = {
bridge = {
animated_sticker = {
args = {
fps = 25;
height = 320;
width = 320;
};
target = "webp";
};
autojoin_thread_on_open = true;
avatar_proxy_key = "generate";
backfill = {
forward_limits = {
initial = {
channel = 0;
dm = 0;
thread = 0;
};
max_guild_members = -1;
missed = {
channel = 0;
dm = 0;
thread = 0;
};
};
};
cache_media = "unencrypted";
channel_name_template = "{{if or (eq .Type 3) (eq .Type 4)}}{{.Name}}{{else}}#{{.Name}}{{end}}";
command_prefix = "!discord";
custom_emoji_reactions = true;
delete_guild_on_leave = true;
delete_portal_on_channel_delete = false;
delivery_receipts = false;
direct_media = {
allow_proxy = true;
enabled = false;
server_key = "generate";
};
displayname_template = "{{if .Webhook}}Webhook{{else}}{{or .GlobalName .Username}}{{if .Bot}} (bot){{end}}{{end}}";
double_puppet_allow_discovery = true;
double_puppet_server_map = { };
embed_fields_as_tables = true;
enable_webhook_avatars = true;
encryption = {
allow = false;
allow_key_sharing = false;
appservice = false;
default = false;
delete_keys = {
delete_fully_used_on_decrypt = false;
delete_on_device_delete = false;
delete_outbound_on_ack = false;
delete_outdated_inbound = false;
delete_prev_on_new_session = false;
dont_store_outbound = false;
periodically_delete_expired = false;
ratchet_on_decrypt = false;
};
msc4190 = false;
plaintext_mentions = false;
require = false;
rotation = {
disable_device_change_key_rotation = false;
enable_custom = false;
messages = 100;
milliseconds = 604800000;
};
verification_levels = {
receive = "unverified";
send = "unverified";
share = "cross-signed-tofu";
};
};
federate_rooms = true;
guild_name_template = "{{.Name}}";
login_shared_secret_map = { };
management_room_text = {
additional_help = "";
welcome = "Hello, I'm a Discord bridge bot.";
welcome_connected = "Use `help` for help.";
welcome_unconnected = "Use `help` for help or `login` to log in.";
};
message_error_notices = true;
message_status_events = false;
mute_channels_on_create = false;
permissions = {
"@${config.monorepo.vars.internetName}:matrix.${config.monorepo.vars.orgHost}" = "admin";
"*" = "user";
};
portal_message_buffer = 128;
prefix_webhook_messages = true;
private_chat_portal_meta = "default";
provisioning = {
debug_endpoints = false;
prefix = "/_matrix/provision";
shared_secret = "generate";
};
public_address = null;
resend_bridge_info = false;
restricted_rooms = false;
startup_private_channel_create_limit = 5;
sync_direct_chat_list = false;
use_discord_cdn_upload = true;
username_template = "discord_{{.}}";
};
appservice = {
address = "http://localhost:29334";
hostname = "0.0.0.0";
port = 29334;
id = "discord";
bot = {
username = "discordbot";
displayname = "Discord bridge bot";
avatar = "mxc://maunium.net/nIdEykemnwdisvHbpxflpDlC";
};
ephemeral_events = true;
async_transactions = false;
database = {
type = "sqlite3";
uri = "file:${config.services.mautrix-discord.dataDir}/mautrix-discord.db?_txlock=immediate";
max_open_conns = 20;
max_idle_conns = 2;
max_conn_idle_time = null;
max_conn_lifetime = null;
};
as_token = "$MAUTRIX_DISCORD_APPSERVICE_AS_TOKEN";
hs_token = "$MAUTRIX_DISCORD_APPSERVICE_HS_TOKEN";
};
dataDir = "/var/lib/mautrix-discord";
homeserver = {
async_media = false;
message_send_checkpoint_endpoint = null;
ping_interval_seconds = 0;
software = "standard";
status_endpoint = null;
websocket = false;
domain = "matrix.${config.monorepo.vars.orgHost}";
address = "http://localhost:6167";
};
};
};
}
#+end_src
** Ollama
Use ollama for serving large language models to my other computers.
#+begin_src nix :tangle ../nix/modules/ollama.nix
{ config, lib, ... }:
{
services.ollama = {
enable = lib.mkDefault (!config.monorepo.profiles.server.enable);
acceleration = if (config.monorepo.profiles.workstation.enable) then "cuda" else null;
loadModels = if (config.monorepo.profiles.workstation.enable) then [
"qwen3:30b"
"qwen3-coder:latest"
"qwen2.5-coder:latest"
"gemma3:12b-it-qat"
] else [
"qwen3:0.6b"
"qwen2.5-coder:0.5b"
];
host = "0.0.0.0";
openFirewall = true;
};
}
#+end_src
** Bitcoind
#+begin_src nix :tangle ../nix/modules/bitcoin.nix
{ config, lib, ... }:
{
services.bitcoind."${config.monorepo.vars.userName}" = {
enable = lib.mkDefault config.monorepo.profiles.workstation.enable;
prune = 10000;
};
}
#+end_src
** Git Server
I run my own git server in order to have a mirror in case github goes down.
#+begin_src nix :tangle ../nix/modules/git-daemon.nix
{ config, lib, ... }:
{
services.gitDaemon = {
enable = lib.mkDefault config.monorepo.profiles.server.enable;
exportAll = true;
basePath = "/srv/git";
};
}
#+end_src
** Nginx
These are all my virtual hosts. For many of these servers we have to
have a reverse proxy in order to expose the locally running instances
to the outside world under a domain.
#+begin_src nix :tangle ../nix/modules/nginx.nix
{ config, lib, services, ... }:
{
services.nginx = {
enable = lib.mkDefault config.monorepo.profiles.server.enable;
user = "nginx";
# Use recommended settings
recommendedGzipSettings = true;
recommendedOptimisation = true;
recommendedProxySettings = true;
recommendedTlsSettings = true;
appendHttpConfig = '''';
gitweb = {
enable = true;
virtualHost = "${config.monorepo.vars.remoteHost}";
};
virtualHosts = {
"matrix.${config.monorepo.vars.orgHost}" = {
enableACME = true;
forceSSL = true;
listen = [
{
addr = "0.0.0.0";
port = 443;
ssl = true;
}
{
addr = "[::]";
port = 443;
ssl = true;
} {
addr = "0.0.0.0";
port = 8448;
ssl = true;
}
{
addr = "[::]";
port = 8448;
ssl = true;
}
];
locations."/_matrix/" = {
proxyPass = "http://127.0.0.1:6167";
extraConfig = ''
proxy_set_header Host $host;
proxy_buffers 32 16k;
proxy_read_timeout 5m;
'';
};
extraConfig = ''
merge_slashes off;
'';
};
"${config.monorepo.vars.remoteHost}" = {
serverName = "${config.monorepo.vars.remoteHost}";
serverAliases = [ "${config.monorepo.vars.internetName}.${config.monorepo.vars.orgHost}" ];
root = "/var/www/${config.monorepo.vars.internetName}-website/";
addSSL = true;
enableACME = true;
};
"${config.monorepo.vars.orgHost}" = {
serverName = "${config.monorepo.vars.orgHost}";
root = "/var/www/nullring/";
addSSL = true;
enableACME = true;
};
"mail.${config.monorepo.vars.remoteHost}" = {
serverName = "mail.${config.monorepo.vars.remoteHost}";
root = "/var/www/dummy";
addSSL = true;
enableACME = true;
};
};
};
}
#+end_src
** Git Web Interface
I enable the git web interface to show off my git repos to the
world. This was the easiest frontend to set up on NixOS.
#+begin_src nix :tangle ../nix/modules/gitweb.nix
{ lib, config, ... }:
{
services.gitweb = {
gitwebTheme = true;
projectroot = "/srv/git/";
};
}
#+end_src
** Nvidia
I have an Nvidia GPU on my computer.
#+begin_src nix :tangle ../nix/modules/nvidia.nix
{ config, lib, pkgs, ... }:
{
hardware = {
graphics.extraPackages = (if config.monorepo.profiles.cuda.enable
then with pkgs; [
vaapiVdpau
libvdpau-va-gl
nvidia-vaapi-driver
] else []);
nvidia = {
modesetting.enable = lib.mkDefault config.monorepo.profiles.cuda.enable;
powerManagement = {
enable = lib.mkDefault config.monorepo.profiles.cuda.enable;
finegrained = false;
};
nvidiaSettings = lib.mkDefault config.monorepo.profiles.cuda.enable;
open = lib.mkDefault false;
package = config.boot.kernelPackages.nvidiaPackages.stable;
};
};
}
#+end_src
** CUDA
I need CUDA on some computers because I run local LLMs.
#+begin_src nix :tangle ../nix/modules/cuda.nix
{ config, lib, pkgs, ... }:
{
environment.systemPackages = (if config.monorepo.profiles.cuda.enable then with pkgs; [
cudatoolkit
cudaPackages.cudnn
cudaPackages.libcublas
linuxPackages.nvidia_x11
] else []);
}
#+end_src
** TODO Maddy
#+begin_src nix :tangle ../nix/modules/maddy.nix
{ lib, config, options, ... }:
{
services.maddy = {
enable = lib.mkDefault config.monorepo.profiles.server.enable;
openFirewall = true;
hostName = "${config.monorepo.vars.remoteHost}";
primaryDomain = "mail.${config.monorepo.vars.remoteHost}";
tls = {
loader = "acme";
};
config = builtins.replaceStrings [
"imap tcp://0.0.0.0:143"
"submission tcp://0.0.0.0:587"
] [
"imap tls://0.0.0.0:993 tcp://0.0.0.0:143"
"submission tls://0.0.0.0:465 tcp://0.0.0.0:587"
] options.services.maddy.config.default;
ensureCredentials = {
"${config.monorepo.vars.userName}@localhost" = {
passwordFile = "/secrets/${config.monorepo.vars.userName}-localhost";
};
};
};
}
#+end_src
** Impermanence
This is my impermanence profile, which removes all files on reboot except for the ones listed below.
#+begin_src nix :tangle ../nix/modules/impermanence.nix
{ lib, config, ... }:
{
assertions = [
{
assertion = (! (config.monorepo.profiles.impermanence.enable && (! (config.monorepo.vars.fileSystem == "btrfs"))));
message = "Impermanence requires btrfs filesystem.";
}
];
boot.initrd.postResumeCommands = (if config.monorepo.profiles.impermanence.enable then lib.mkAfter ''
mkdir /btrfs_tmp
mount /dev/root_vg/root /btrfs_tmp
if [[ -e /btrfs_tmp/root ]]; then
mkdir -p /btrfs_tmp/old_roots
timestamp=$(date --date="@$(stat -c %Y /btrfs_tmp/root)" "+%Y-%m-%-d_%H:%M:%S")
mv /btrfs_tmp/root "/btrfs_tmp/old_roots/$timestamp"
fi
delete_subvolume_recursively() {
IFS=$'\n'
for i in $(btrfs subvolume list -o "$1" | cut -f 9- -d ' '); do
delete_subvolume_recursively "/btrfs_tmp/$i"
done
btrfs subvolume delete "$1"
}
for i in $(find /btrfs_tmp/old_roots/ -maxdepth 1 -mtime +30); do
delete_subvolume_recursively "$i"
done
btrfs subvolume create /btrfs_tmp/root
umount /btrfs_tmp
'' else "");
fileSystems = if (config.monorepo.profiles.impermanence.enable) then {
"/persistent" = {
neededForBoot = true;
};
} else {};
environment.persistence."/persistent" = {
enable = config.monorepo.profiles.impermanence.enable;
hideMounts = true;
directories = [
"/var/log"
"/var/lib/bluetooth"
"/var/lib/nixos"
"/var/lib/systemd/coredump"
"/etc/NetworkManager/system-connections"
];
files = [
"/etc/machine-id"
"/etc/matterbridge.toml"
{ file = "/var/keys/secret_file"; parentDirectory = { mode = "u=rwx,g=,o="; }; }
];
users."${config.monorepo.vars.userName}" = {
directories = [
"Downloads"
"music"
"Pictures"
"Documents"
"Videos"
"Monero"
"org"
"monorepo"
"soundfont"
"website_html"
"ardour"
"audacity"
"img"
"email"
"projects"
"secrets"
".emacs.d"
".elfeed"
".electrum"
".mozilla"
".bitmonero"
".config"
{ directory = ".gnupg"; mode = "0700"; }
{ directory = ".ssh"; mode = "0700"; }
{ directory = ".local/share/keyrings"; mode = "0700"; }
".local/share/direnv"
];
files = [
".emacs"
];
};
};
}
#+end_src
** Main Configuration
This is the backbone of the all the NixOS configurations, with all these options being shared
because they enhance security.
#+begin_src nix :tangle ../nix/modules/configuration.nix
{ config, pkgs, lib, ... }:
{
imports = [
./matterbridge.nix
./mautrix.nix
./xserver.nix
./ssh.nix
./pipewire.nix
./tor.nix
./kubo.nix
./nvidia.nix
./cuda.nix
./nginx.nix
./git-daemon.nix
./ollama.nix
./i2pd.nix
./gitweb.nix
./conduit.nix
./bitcoin.nix
./murmur.nix
./ngircd.nix
./znc.nix
./docker.nix
./impermanence.nix
];
documentation = {
enable = lib.mkDefault config.monorepo.profiles.documentation.enable;
man.enable = lib.mkDefault config.monorepo.profiles.documentation.enable;
dev.enable = lib.mkDefault config.monorepo.profiles.documentation.enable;
};
environment = {
etc = {
securetty.text = ''
# /etc/securetty: list of terminals on which root is allowed to login.
# See securetty(5) and login(1).
'';
};
};
systemd = {
coredump.enable = false;
network.config.networkConfig.IPv6PrivacyExtensions = "kernel";
tmpfiles.settings = {
"restricthome"."/home/*".Z.mode = "~0700";
"restrictetcnixos"."/etc/nixos/*".Z = {
mode = "0000";
user = "root";
group = "root";
};
};
};
boot = {
extraModprobeConfig = ''
options snd-usb-audio vid=0x1235 pid=0x8200 device_setup=1
'';
extraModulePackages = [ ];
initrd = {
availableKernelModules = [
"xhci_pci"
"ahci"
"usb_storage"
"sd_mod"
"nvme"
"sd_mod"
"ehci_pci"
"rtsx_pci_sdmmc"
"usbhid"
];
kernelModules = [ ];
};
lanzaboote = {
enable = config.monorepo.profiles.secureBoot.enable;
pkiBundle = "/etc/secureboot";
};
loader = {
systemd-boot.enable = lib.mkForce (! config.monorepo.profiles.grub.enable);
efi.canTouchEfiVariables = lib.mkForce (! config.monorepo.profiles.grub.enable);
};
kernelModules = [
"snd-seq"
"snd-rawmidi"
"xhci_hcd"
"kvm_intel"
];
kernelParams = [
"usbcore.autosuspend=-1"
"debugfs=off"
"page_alloc.shuffle=1"
"slab_nomerge"
"page_poison=1"
# madaidan
"pti=on"
"randomize_kstack_offset=on"
"vsyscall=none"
"module.sig_enforce=1"
"lockdown=confidentiality"
# cpu
"spectre_v2=on"
"spec_store_bypass_disable=on"
"tsx=off"
"l1tf=full,force"
"kvm.nx_huge_pages=force"
# hardened
"extra_latent_entropy"
# mineral
"init_on_alloc=1"
"random.trust_cpu=off"
"random.trust_bootloader=off"
"intel_iommu=on"
"amd_iommu=force_isolation"
"iommu=force"
"iommu.strict=1"
"init_on_free=1"
"quiet"
"loglevel=0"
];
blacklistedKernelModules = [
"netrom"
"rose"
"adfs"
"affs"
"bfs"
"befs"
"cramfs"
"efs"
"erofs"
"exofs"
"freevxfs"
"f2fs"
"hfs"
"hpfs"
"jfs"
"minix"
"nilfs2"
"ntfs"
"omfs"
"qnx4"
"qnx6"
"sysv"
"ufs"
];
kernel.sysctl = {
"kernel.ftrace_enabled" = false;
"net.core.bpf_jit_enable" = false;
"kernel.kptr_restrict" = 2;
# madaidan
"kernel.smtcontrol" = "on";
"vm.swappiness" = 1;
"vm.unprivileged_userfaultfd" = 0;
"dev.tty.ldisc_autoload" = 0;
"kernel.kexec_load_disabled" = 1;
"kernel.sysrq" = 4;
"kernel.perf_event_paranoid" = 3;
# net
"net.ipv4.icmp_echo_ignore_broadcasts" = true;
"net.ipv4.conf.all.accept_redirects" = false;
"net.ipv4.conf.all.secure_redirects" = false;
"net.ipv4.conf.default.accept_redirects" = false;
"net.ipv4.conf.default.secure_redirects" = false;
"net.ipv6.conf.all.accept_redirects" = false;
"net.ipv6.conf.default.accept_redirects" = false;
};
};
networking = {
useDHCP = lib.mkDefault true;
networkmanager = {
enable = true;
};
firewall = {
allowedTCPPorts = [ 22 11434 ];
allowedUDPPorts = [ ];
};
};
hardware = {
enableAllFirmware = true;
cpu.intel.updateMicrocode = true;
graphics.enable = ! config.monorepo.profiles.ttyonly.enable;
bluetooth = {
enable = true;
powerOnBoot = true;
};
};
services = {
pulseaudio.enable = ! config.monorepo.profiles.pipewire.enable;
chrony = {
enable = true;
enableNTS = true;
servers = [ "time.cloudflare.com" "ptbtime1.ptb.de" "ptbtime2.ptb.de" ];
};
jitterentropy-rngd.enable = true;
resolved.dnssec = true;
# usbguard.enable = true;
usbguard.enable = false;
dbus.apparmor = "enabled";
kanata.enable = true;
# Misc.
udev = {
extraRules = '''';
packages = with pkgs; [
platformio-core
platformio-core.udev
openocd
];
};
printing.enable = true;
udisks2.enable = true;
};
programs = {
nix-ld.enable = true;
zsh.enable = true;
light.enable = true;
ssh.enableAskPassword = false;
};
nixpkgs = {
hostPlatform = lib.mkDefault "x86_64-linux";
config = {
allowUnfree = true;
cudaSupport = lib.mkDefault config.monorepo.profiles.cuda.enable;
};
};
security = {
acme = {
acceptTerms = true;
defaults.email = "ret2pop@gmail.com";
};
apparmor = {
enable = true;
killUnconfinedConfinables = true;
};
pam.loginLimits = [
{ domain = "*"; item = "nofile"; type = "-"; value = "32768"; }
{ domain = "*"; item = "memlock"; type = "-"; value = "32768"; }
];
rtkit.enable = true;
lockKernelModules = true;
protectKernelImage = true;
allowSimultaneousMultithreading = true;
forcePageTableIsolation = true;
tpm2 = {
enable = true;
pkcs11.enable = true;
tctiEnvironment.enable = true;
};
auditd.enable = true;
audit.enable = true;
chromiumSuidSandbox.enable = (! config.monorepo.profiles.ttyonly.enable);
sudo.enable = true;
};
xdg.portal = {
enable = (! config.monorepo.profiles.ttyonly.enable);
wlr.enable = (! config.monorepo.profiles.ttyonly.enable);
extraPortals = with pkgs; if (! config.monorepo.profiles.ttyonly.enable) then [
xdg-desktop-portal-gtk
xdg-desktop-portal
xdg-desktop-portal-hyprland
] else [];
config.common.default = "*";
};
environment.etc."gitconfig".text = ''
[init]
defaultBranch = main
'';
environment.extraInit = ''
umask 0022
'';
environment.systemPackages = with pkgs; [
restic
sbctl
git
vim
curl
nmap
(writeShellScriptBin "new-repo"
''
#!/bin/bash
cd /srv/git
git init --bare "$1"
vim "$1/description"
chown -R git:git "$1"
''
)
];
users.groups.nginx = lib.mkDefault {};
users.groups.git = lib.mkDefault {};
users.groups.ircd = lib.mkDefault {};
users.groups.ngircd = lib.mkDefault {};
users.users = {
ngircd = {
isSystemUser = lib.mkDefault true;
group = "ngircd";
extraGroups = [ "acme" "nginx" ];
};
ircd = {
isSystemUser = lib.mkDefault true;
group = "ircd";
home = "/home/ircd";
};
nginx = {
group = "nginx";
isSystemUser = lib.mkDefault true;
extraGroups = [
"acme"
];
};
root.openssh.authorizedKeys.keys = [
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICts6+MQiMwpA+DfFQxjIN214Jn0pCw/2BDvOzPhR/H2 preston@continuity-dell"
];
git = {
isSystemUser = true;
home = "/srv/git";
shell = "${pkgs.git}/bin/git-shell";
group = "git";
openssh.authorizedKeys.keys = [
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICts6+MQiMwpA+DfFQxjIN214Jn0pCw/2BDvOzPhR/H2 preston@continuity-dell"
];
};
"${config.monorepo.vars.userName}" = {
openssh.authorizedKeys.keys = [
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICts6+MQiMwpA+DfFQxjIN214Jn0pCw/2BDvOzPhR/H2 preston@continuity-dell"
];
initialPassword = "${config.monorepo.vars.userName}";
isNormalUser = true;
description = config.monorepo.vars.fullName;
extraGroups = [ "networkmanager" "wheel" "video" "docker" "jackaudio" "tss" "dialout" "docker" ];
shell = pkgs.zsh;
packages = [];
};
};
nixpkgs.config.permittedInsecurePackages = [
"olm-3.2.16"
];
nix = {
settings = {
experimental-features = "nix-command flakes";
trusted-users = [ "@wheel" ];
};
};
time.timeZone = config.monorepo.vars.timeZone;
i18n.defaultLocale = "en_CA.UTF-8";
system.stateVersion = "24.11";
}
#+end_src
** Disko
This is the disko configuration for my continuity system. It features a boot and ext4 partition,
with configurable disk.
*** Btrfs
#+begin_src nix :tangle ../nix/disko/btrfs-simple.nix
{ lib, config, ... }:
let
spec = {
disko.devices = {
disk = {
main = {
type = "disk";
device = config.monorepo.vars.device;
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";
passwordFile = "/tmp/secret.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"
];
};
"/persistent" = {
mountpoint = "/persistent";
mountOptions = [
"compress=zstd"
"noatime"
];
};
};
};
};
};
};
};
};
};
};
};
in
{
monorepo.vars.diskoSpec = spec;
disko.devices = spec.disko.devices;
}
#+end_src
*** Simple
This configuration is used for simple partitioning schemes with EFI.
#+begin_src nix :tangle ../nix/disko/drive-simple.nix
{ lib, config, ... }:
let
spec = {
disko.devices = {
disk = {
my-disk = {
device = config.monorepo.vars.device;
type = "disk";
content = {
type = "gpt";
partitions = {
ESP = {
type = "EF00";
size = "500M";
priority = 1;
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
mountOptions = [ "umask=0077" ];
};
};
root = {
size = "100%";
priority = 2;
content = {
type = "filesystem";
format = "ext4";
mountpoint = "/";
};
};
};
};
};
};
};
};
in
{
monorepo.vars.diskoSpec = spec;
disko.devices = spec.disko.devices;
}
#+end_src
*** BIOS
For machines that use BIOS instead of EFI.
#+begin_src nix :tangle ../nix/disko/drive-bios.nix
{ config, lib, ... }:
let
spec = {
disko.devices = {
disk = {
main = {
device = config.monorepo.vars.device;
type = "disk";
content = {
type = "gpt";
partitions = {
boot = {
size = "1M";
type = "EF02";
};
root = {
label = "disk-main-root";
size = "100%";
content = {
type = "filesystem";
format = "ext4";
mountpoint = "/";
};
};
};
};
};
};
};
};
in
{
monorepo.vars.diskoSpec = spec;
disko.devices = spec.disko.devices;
}
#+end_src
** Home
*** Default Home Profile
As you can see, I have my installed home packages installed based on the profiles enabled. Also,
I have many imports that we'll go through next.
#+begin_src nix :tangle ../nix/modules/home/default.nix
{ lib, config, pkgs, sops-nix, ... }:
{
imports = [
sops-nix.homeManagerModules.sops
../vars.nix
./fcitx.nix
./secrets.nix
./emacs.nix
./firefox.nix
./git.nix
./hyprland.nix
./mpv.nix
./yt-dlp.nix
./wofi.nix
./kitty.nix
./waybar.nix
./zsh.nix
./mbsync.nix
./msmtp.nix
./gammastep.nix
./mpd.nix
./mako.nix
./user.nix
];
options = {
monorepo.profiles = {
enable = lib.mkEnableOption "Enables home manager desktop configuration";
# Programs
graphics.enable = lib.mkEnableOption "Enables graphical programs for user";
lang-c.enable = lib.mkEnableOption "Enables C language support";
lang-sh.enable = lib.mkEnableOption "Enables sh language support";
lang-rust.enable = lib.mkEnableOption "Enables Rust language support";
lang-python.enable = lib.mkEnableOption "Enables python language support";
lang-sol.enable = lib.mkEnableOption "Enables solidity language support";
lang-openscad.enable = lib.mkEnableOption "Enables openscad language support";
lang-js.enable = lib.mkEnableOption "Enables javascript language support";
lang-nix.enable = lib.mkEnableOption "Enables nix language support";
lang-idris.enable = lib.mkEnableOption "Enables idris language support";
lang-agda.enable = lib.mkEnableOption "Enables agda language support";
lang-coq.enable = lib.mkEnableOption "Enables coq language support";
lang-lean.enable = lib.mkEnableOption "Enables lean language support";
lang-haskell.enable = lib.mkEnableOption "Enables haskell language support";
crypto.enable = lib.mkEnableOption "Enables various cryptocurrency wallets";
art.enable = lib.mkEnableOption "Enables various art programs";
music.enable = lib.mkEnableOption "Enables mpd";
workstation.enable = lib.mkEnableOption "Enables workstation packages (music production and others)";
cuda.enable = lib.mkEnableOption "Enables CUDA user package builds";
hyprland.enable = lib.mkEnableOption "Enables hyprland";
email = {
email = lib.mkOption {
type = lib.types.str;
default = "ret2pop@gmail.com";
example = "john@example.com";
description = "Email address and imaps/smtps account";
};
imapsServer = lib.mkOption {
type = lib.types.str;
default = "imap.gmail.com";
example = "imap.example.com";
description = "imaps server address";
};
smtpsServer = lib.mkOption {
type = lib.types.str;
default = "smtp.gmail.com";
example = "smtp.example.com";
description = "smtp server address";
};
enable = lib.mkEnableOption "Enables email";
};
};
};
config = {
home.packages = (if config.monorepo.profiles.email.enable then [ pkgs.mu ] else [])
++
(if config.monorepo.profiles.lang-c.enable then (with pkgs; [
autobuild
clang
gdb
gnumake
bear
clang-tools
autotools-language-server
]) else [])
++
(if config.monorepo.profiles.workstation.enable then (with pkgs; [
mumble
]) else [])
++
(if config.monorepo.profiles.lang-js.enable then (with pkgs; [
nodejs
bun
yarn
typescript
typescript-language-server
vscode-langservers-extracted
]) else [])
++
(if config.monorepo.profiles.lang-rust.enable then (with pkgs; [
cargo
rust-analyzer
rustfmt
]) else [])
++
(if config.monorepo.profiles.lang-python.enable then (with pkgs; [
poetry
python3
python312Packages.jedi
]) else [])
++
(if config.monorepo.profiles.lang-sol.enable then (with pkgs; [
solc
]) else [])
++
(if config.monorepo.profiles.lang-openscad.enable then (with pkgs; [
openscad
openscad-lsp
]) else [])
++
(if config.monorepo.profiles.lang-sh.enable then (with pkgs; [
bash-language-server
]) else [])
++
(if config.monorepo.profiles.lang-haskell.enable then (with pkgs; [
haskell-language-server
haskellPackages.hlint
ghc
]) else [])
++
(if config.monorepo.profiles.lang-coq.enable then (with pkgs; [
coq
]) else [])
++
(if config.monorepo.profiles.lang-lean.enable then (with pkgs; [
lean4
]) else [])
++
(if config.monorepo.profiles.lang-agda.enable then (with pkgs; [
agda
]) else [])
++
(if config.monorepo.profiles.lang-idris.enable then (with pkgs; [
idris
idris2Packages.idris2Lsp
]) else [])
++
(if config.monorepo.profiles.lang-nix.enable then (with pkgs; [
nil
nixd
nixfmt-rfc-style
nix-prefetch-scripts
]) else [])
++
(if config.monorepo.profiles.crypto.enable then (with pkgs; [
bitcoin
electrum
monero-cli
monero-gui
]) else [])
++
(if config.monorepo.profiles.art.enable then (with pkgs; [
inkscape
# krita
]) else [])
++
(if config.monorepo.profiles.music.enable then (with pkgs; [
mpc-cli
sox
]) else [])
++
(if config.monorepo.profiles.workstation.enable then (with pkgs; [
alsa-utils
alsa-scarlett-gui
ardour
audacity
# blender
foxdot
fluidsynth
qjackctl
qsynth
qpwgraph
imagemagick
supercollider
inkscape
kdePackages.kdenlive
# kicad
murmur
]) else []);
monorepo.profiles = {
enable = lib.mkDefault true;
music.enable = lib.mkDefault config.monorepo.profiles.enable;
hyprland.enable = lib.mkDefault config.monorepo.profiles.enable;
email.enable = lib.mkDefault config.monorepo.profiles.enable;
# Programming
graphics.enable = lib.mkDefault config.monorepo.profiles.enable;
lang-c.enable = lib.mkDefault config.monorepo.profiles.enable;
lang-rust.enable = lib.mkDefault config.monorepo.profiles.enable;
lang-python.enable = lib.mkDefault config.monorepo.profiles.enable;
lang-sol.enable = lib.mkDefault config.monorepo.profiles.enable;
lang-sh.enable = lib.mkDefault config.monorepo.profiles.enable;
lang-openscad.enable = lib.mkDefault config.monorepo.profiles.enable;
lang-js.enable = lib.mkDefault config.monorepo.profiles.enable;
lang-nix.enable = lib.mkDefault config.monorepo.profiles.enable;
lang-coq.enable = lib.mkDefault config.monorepo.profiles.enable;
lang-lean.enable = lib.mkDefault config.monorepo.profiles.enable;
lang-haskell.enable = lib.mkDefault config.monorepo.profiles.enable;
lang-idris.enable = lib.mkDefault config.monorepo.profiles.enable;
lang-agda.enable = lib.mkDefault config.monorepo.profiles.enable;
crypto.enable = lib.mkDefault config.monorepo.profiles.enable;
art.enable = lib.mkDefault config.monorepo.profiles.enable;
workstation.enable = lib.mkDefault config.monorepo.profiles.enable;
};
};
}
#+end_src
*** Firefox
I conditionally enable metamask based on the cryptocurrency option. Everything else here should
be straightforward.
#+begin_src nix :tangle ../nix/modules/home/firefox.nix
{ lib, config, pkgs, ... }:
{
programs.firefox = {
enable = lib.mkDefault config.monorepo.profiles.graphics.enable;
package = pkgs.firefox-bin;
policies = {
EnableTrackingProtection = true;
OfferToSaveLogins = false;
};
profiles = {
default = {
id = 0;
name = "default";
isDefault = true;
extensions.packages = with pkgs.nur.repos.rycee.firefox-addons; [
ublock-origin
tree-style-tab
firefox-color
vimium
]
++ (lib.optional
config.monorepo.profiles.crypto.enable pkgs.nur.repos.rycee.firefox-addons.metamask);
settings = {
media = {
memory_cache_max_size = 65536;
cache_readahead_limit = 7200;
cache_resume_threshold = 3600;
peerconnection.ice = {
proxy_only_if_behind_proxy = true;
default_address_only = true;
};
};
gfx = {
content.skia-font-cache-size = 20;
canvas.accelerated = {
cache-items = 4096;
cache-size = 512;
};
};
network = {
http = {
max-connections = 1800;
max-persistent-connections-per-server = 10;
max-urgent-start-excessive-connections-per-host = 5;
referer.XOriginTrimmingPolicy = 2;
};
buffer.cache = {
size = 262144;
count = 128;
};
dns = {
max_high_priority_threads = 8;
disablePrefetch = true;
};
pacing.requests.enabled = false;
dnsCacheExpiration = 3600;
ssl_tokens_cache_capacity = 10240;
prefetch-next = false;
predictor.enabled = false;
cookie.sameSite.noneRequiresSecure = true;
IDN_show_punycode = true;
auth.subresource-http-auth-allow = 1;
captive-portal-service.enabled = false;
connectivity-service.enabled = false;
};
browser = {
download = {
always_ask_before_handling_new_types = true;
manager.addToRecentDocs = false;
open_pdf_attachments_inline = true;
start_downloads_in_tmp_dir = true;
};
urlbar = {
suggest.quicksuggest.sponsored = false;
suggest.quicksuggest.nonsponsored = false;
suggest.calculator = true;
update2.engineAliasRefresh = true;
unitConversion.enabled = true;
trending.featureGate = false;
};
search = {
separatePrivateDefault.ui.enabled = true;
suggest.enabled = false;
};
newtabpage.activity-stream = {
feeds = {
topsites = false;
section.topstories = false;
telemetry = false;
};
asrouter.userprefs.cfr = {
addons = false;
features = false;
};
telemetry = false;
};
privatebrowsing = {
vpnpromourl = "";
forceMediaMemoryCache = true;
};
display = {
focus_ring_on_anything = true;
focus_ring_style = 0;
focus_ring_width = 0;
};
cache.jsbc_compression_level = 3;
helperApps.deleteTempFileOnExit = true;
uitour.enabled = false;
sessionstore.interval = 60000;
formfill.enable = false;
xul.error_pages.expert_bad_cert = true;
contentblocking.category = "strict";
ping-centre.telemetry = false;
discovery.enabled = false;
shell.checkDefaultBrowser = false;
preferences.moreFromMozilla = false;
tabs.tabmanager.enabled = false;
aboutConfig.showWarning = false;
aboutwelcome.enabled = false;
bookmarks.openInTabClosesMenu = false;
menu.showViewImageInfo = true;
compactmode.show = true;
safebrowsing.downloads.remote.enabled = false;
tabs.crashReporting.sendReport = false;
crashReports.unsubmittedCheck.autoSubmit2 = false;
privateWindowSeparation.enabled = false;
};
security = {
mixed_content = {
block_display_content = true;
upgrade_display_content = true;
};
insecure_connection_text = {
enabled = true;
pbmode.enabled = true;
};
OCSP.enabled = 0;
remote_settings.crlite_filters.enabled = true;
pki.crlite_mode = 2;
ssl.treat_unsafe_negotiation_as_broken = true;
tls.enable_0rtt_data = false;
};
toolkit = {
telemetry = {
unified = false;
enabled = false;
server = "data:,";
archive.enabled = false;
newProfilePing.enabled = false;
shutdownPingSender.enabled = false;
updatePing.enabled = false;
bhrPing.enabled = false;
firstShutdownPing.enabled = false;
coverage.opt-out = true;
};
coverage = {
opt-out = true;
endpoint.base = "";
};
legacyUserProfileCustomizations.stylesheets = true;
};
dom = {
security = {
https_first = true;
https_first_schemeless = true;
sanitizer.enabled = true;
};
enable_web_task_scheduling = true;
};
layout = {
css = {
grid-template-masonry-value.enabled = true;
has-selector.enabled = true;
prefers-color-scheme.content-override = 2;
};
word_select.eat_space_to_next_word = false;
};
urlclassifier = {
trackingSkipURLs = "*.reddit.com, *.twitter.com, *.twimg.com, *.tiktok.com";
features.socialtracking.skipURLs = "*.instagram.com, *.twitter.com, *.twimg.com";
};
privacy = {
globalprivacycontrol.enabled = true;
history.custom = true;
userContext.ui.enabled = true;
trackingprotection = {
enabled = true;
pbmode.enabled = true;
socialtracking.enabled = true;
};
};
full-screen-api = {
transition-duration = {
enter = "0 0";
leave = "0 0";
};
warning = {
delay = -1;
timeout = 0;
};
};
permissions.default = {
desktop-notification = 2;
geo = 2;
};
signon = {
formlessCapture.enabled = false;
privateBrowsingCapture.enabled = false;
};
datareporting = {
policy.dataSubmissionEnabled = false;
healthreport.uploadEnabled = false;
};
extensions = {
pocket.enabled = false;
getAddons.showPane = false;
htmlaboutaddons.recommendations.enabled = false;
postDownloadThirdPartyPrompt = false;
};
app = {
shield.optoutstudies.enabled = false;
normandy.enabled = false;
normandy.api_url = "";
};
image.mem.decode_bytes_at_a_time = 32768;
editor.truncate_user_pastes = false;
pdfjs.enableScripting = false;
geo.provider.network.url = "https://location.services.mozilla.com/v1/geolocate?key=%MOZILLA_API_KEY%";
permissions.manager.defaultsUrl = "";
webchannel.allowObject.urlWhitelist = "";
breakpad.reportURL = "";
captivedetect.canonicalURL = "";
cookiebanners.service.mode = 1;
findbar.highlightAll = true;
content.notify.interval = 100000;
};
};
};
};
}
#+end_src
*** Fcitx
This is a virtual keyboard program for writing in multiple languages. I use this sometimes.
#+begin_src nix :tangle ../nix/modules/home/fcitx.nix
{ config, pkgs, lib, ... }:
{
i18n.inputMethod = {
type = "fcitx5";
enable = lib.mkDefault config.monorepo.profiles.graphics.enable;
fcitx5.addons = with pkgs; [
fcitx5-gtk
fcitx5-chinese-addons
fcitx5-configtool
fcitx5-mozc
fcitx5-rime
];
};
}
#+end_src
Note that I configure fcitx with chinese and some japanese input enabled.
*** Emacs
I install all my emacs packages within Nix so that they build deterministically with native
compilation, and because I can fetch their exact versions. Note that I have a stub
configuration here that tells emacs to load my real configuration at ~~/monorepo/config/emacs.org~
as an org file which gets automatically tangled to an emacs-lisp file.
#+begin_src nix :tangle ../nix/modules/home/emacs.nix
{ lib, config, pkgs, ... }:
{
programs.emacs =
{
enable = lib.mkDefault config.monorepo.profiles.graphics.enable;
package = pkgs.emacs-pgtk;
extraConfig = ''
(setq debug-on-error t)
(setq system-email "${config.monorepo.profiles.email.email}")
(setq system-username "${config.monorepo.vars.internetName}")
(setq system-fullname "${config.monorepo.vars.fullName}")
(org-babel-load-file
(expand-file-name "~/${config.monorepo.vars.repoName}/config/emacs.org"))'';
extraPackages = epkgs: [
epkgs.agda2-mode
epkgs.all-the-icons
epkgs.auctex
epkgs.catppuccin-theme
epkgs.chatgpt-shell
epkgs.company
epkgs.company-solidity
epkgs.counsel
epkgs.dashboard
epkgs.doom-themes
epkgs.doom-modeline
epkgs.elfeed
epkgs.elfeed-org
epkgs.elfeed-tube
epkgs.elfeed-tube-mpv
epkgs.ellama
epkgs.elpher
epkgs.ement
epkgs.emmet-mode
epkgs.emms
epkgs.enwc
epkgs.evil
epkgs.evil-collection
epkgs.evil-commentary
epkgs.evil-org
epkgs.f
epkgs.flycheck
epkgs.general
epkgs.gptel
epkgs.gruvbox-theme
epkgs.haskell-mode
epkgs.htmlize
epkgs.idris-mode
epkgs.irony-eldoc
epkgs.ivy
epkgs.ivy-pass
epkgs.kiwix
epkgs.latex-preview-pane
epkgs.lsp-ivy
epkgs.lsp-mode
epkgs.lsp-haskell
epkgs.lyrics-fetcher
epkgs.mastodon
epkgs.magit
epkgs.magit-delta
epkgs.mu4e
epkgs.minuet
epkgs.nix-mode
epkgs.org-fragtog
epkgs.org-journal
epkgs.org-roam
epkgs.org-roam-ui
epkgs.org-superstar
epkgs.page-break-lines
epkgs.password-store
epkgs.pdf-tools
epkgs.pinentry
epkgs.platformio-mode
epkgs.projectile
epkgs.rustic
epkgs.scad-mode
epkgs.simple-httpd
epkgs.solidity-flycheck
epkgs.solidity-mode
epkgs.sudo-edit
epkgs.treemacs
epkgs.treemacs-evil
epkgs.treemacs-magit
epkgs.treemacs-projectile
epkgs.treesit-auto
epkgs.typescript-mode
epkgs.unicode-fonts
epkgs.use-package
epkgs.vterm
epkgs.wgrep
epkgs.web-mode
epkgs.websocket
epkgs.which-key
epkgs.writegood-mode
epkgs.writeroom-mode
epkgs.yaml-mode
epkgs.yasnippet
epkgs.yasnippet-snippets
];
};
}
#+end_src
*** Gammastep
This is a program like redshift for making your screen emit more red and less blue light. Here
I have the long and lat set for Vancouver, but you should replace it if you live outside
the timezone.
#+begin_src nix :tangle ../nix/modules/home/gammastep.nix
{ lib, config, ... }:
{
services.gammastep = {
enable = lib.mkDefault config.monorepo.profiles.graphics.enable;
provider = "manual";
latitude = 49.282730;
longitude = -123.120735;
temperature = {
day = 5000;
night = 3000;
};
settings = {
general = {
adjustment-method = "wayland";
};
};
};
}
#+end_src
*** Git
My git configuration uses information set in the ~vars.nix~ in order to set configuration options.
Make sure those are set correctly. I've set it to sign by default.
#+begin_src nix :tangle ../nix/modules/home/git.nix
{ lib, config, ... }:
{
programs.git = {
enable = lib.mkDefault config.monorepo.profiles.graphics.enable;
userName = config.monorepo.vars.fullName;
userEmail = config.monorepo.profiles.email.email;
signing = {
key = config.monorepo.vars.gpgKey;
signByDefault = true;
};
extraConfig = {
init.defaultBranch = "main";
};
aliases = {
pl = "pull";
ps = "push";
co = "checkout";
c = "commit";
a = "add";
st = "status";
sw = "switch";
b = "branch";
};
};
}
#+end_src
*** Hyprland
My compositor/window manager. This automatically starts on startup. Instructions on how
to use this component will come soon.
#+begin_src nix :tangle ../nix/modules/home/hyprland.nix
{ lib, config, wallpapers, pkgs, scripts, ... }:
{
wayland.windowManager.hyprland = {
enable = lib.mkDefault config.monorepo.profiles.hyprland.enable;
package = pkgs.hyprland;
xwayland.enable = true;
systemd.enable = true;
settings = {
"$mod" = "SUPER";
bezier = [
"overshot,0,1,0,0.95"
];
animation = [
"workspaces, 1, 10, overshot"
];
exec-once = [
"waybar"
"swww-daemon --format xrgb"
"swww img ${wallpapers}/imagination.png"
"fcitx5-remote -r"
"fcitx5 -d --replace"
"fcitx5-remote -r"
"emacs"
"firefox"
];
env = [
"LIBVA_DRIVER_NAME,nvidia"
"XDG_SESSION_TYPE,wayland"
"GBM_BACKEND,nvidia-drm"
"__GLX_VENDOR_LIBRARY_NAME,nvidia"
"ELECTRON_OZONE_PLATFORM_HINT,auto"
];
blurls = [
"waybar"
];
monitor = [
"Unknown-1,disable"
];
windowrulev2 = [
"workspace 1, class:^(emacs)$"
"workspace 2, class:^(firefox)$"
"workspace 2, title:^(.*Tor Browser.*)$"
"workspace 2, title:^(.*Chromium-browser.*)$"
"workspace 2, class:^(chromium)$"
"workspace 3, class:^(discord)$"
"workspace 3, class:^(vesktop)$"
"workspace 3, title:^(.*fluffychat.*)$"
"workspace 3, class:^(.*element-desktop.*)$"
"workspace 4, class:^(.*qpwgraph.*)$"
"workspace 4, class:^(.*mpv.*)$"
"workspace 5, title:^(.*Monero.*)$"
"workspace 5, title:^(.*org\.bitcoin\..*)$"
"workspace 5, title:^(.*Bitcoin Core - preston.*)$"
"workspace 5, title:^(.*org\.getmonero\..*)$"
"workspace 5, title:^(.*Monero - preston.*)$"
"workspace 5, title:^(.*electrum.*)$"
"pseudo,title:fcitx"
];
bind = [
"$mod, F, exec, firefox"
"$mod, T, exec, tor-browser"
"$mod, Return, exec, kitty"
"$mod, E, exec, emacs"
"$mod, B, exec, bitcoin-qt"
"$mod, M, exec, monero-wallet-gui"
"$mod, V, exec, vesktop"
"$mod, C, exec, fluffychat"
"$mod, D, exec, wofi --show run"
"$mod, P, exec, bash ${scripts}/powermenu.sh"
"$mod, Q, killactive"
"$mod SHIFT, H, movewindow, l"
"$mod SHIFT, L, movewindow, r"
"$mod SHIFT, K, movewindow, u"
"$mod SHIFT, J, movewindow, d"
"$mod, H, movefocus, l"
"$mod, L, movefocus, r"
"$mod, K, movefocus, u"
"$mod, J, movefocus, d"
", XF86AudioPlay, exec, mpc toggle"
", Print, exec, grim"
]
++ (
builtins.concatLists (builtins.genList
(
x:
let
ws =
let
c = (x + 1) / 10;
in
builtins.toString (x + 1 - (c * 10));
in
[
"$mod, ${ws}, workspace, ${toString (x + 1)}"
"$mod SHIFT, ${ws}, movetoworkspace, ${toString (x + 1)}"
]
)
10)
);
bindm = [
"$mod, mouse:272, movewindow"
"$mod, mouse:273, resizewindow"
"$mod ALT, mouse:272, resizewindow"
];
binde = [
", XF86AudioRaiseVolume, exec, wpctl set-volume -l 1.5 @DEFAULT_AUDIO_SINK@ 5%+"
", XF86AudioLowerVolume, exec, wpctl set-volume -l 1.5 @DEFAULT_AUDIO_SINK@ 5%-"
", XF86AudioNext, exec, mpc next"
", XF86AudioPrev, exec, mpc prev"
", XF86MonBrightnessUp , exec, xbacklight -inc 10"
", XF86MonBrightnessDown, exec, xbacklight -dec 10"
];
decoration = {
blur = {
enabled = true;
size = 5;
passes = 2;
};
rounding = 5;
};
device = {
name = "beken-usb-gaming-mouse-1";
sensitivity = -0.5;
};
input = {
kb_options = "caps:swapescape";
repeat_delay = 300;
repeat_rate = 50;
natural_scroll = true;
touchpad = {
natural_scroll = true;
disable_while_typing = true;
tap-to-click = true;
};
};
cursor = {
no_hardware_cursors = true;
};
misc = {
force_default_wallpaper = 0;
disable_hyprland_logo = true;
};
};
};
}
#+end_src
*** Kitty
I've set my terminal, kitty, to use catppuccin colors.
#+begin_src nix :tangle ../nix/modules/home/kitty.nix
{ lib, config, ... }:
{
programs.kitty = {
enable = lib.mkDefault (config.monorepo.profiles.hyprland.enable && config.monorepo.profiles.graphics.enable);
settings = {
enable_audio_bell = false;
font_family = "Iosevka Nerd Font";
font_size = 14;
confirm_os_window_close = 0;
background_opacity = "0.9";
# Catppuccin theme
foreground = "#cdd6f4";
background = "#1e1e2e";
selection_foreground = "#1e1e2e";
selection_background = "#f5e0dc";
cursor = "#f5e0dc";
cursor_text_color = "#1e1e2e";
url_color = "#f5e0dc";
active_border_color = "#B4BEFE";
inactive_border_color = "#6C7086";
bell_border_color = "#F9E2AF";
wayland_titlebar_color = "#1E1E2E";
macos_titlebar_color = "#1E1E2E";
active_tab_foreground = "#11111B";
active_tab_background = "#CBA6F7";
inactive_tab_foreground = "#CDD6F4";
inactive_tab_background = "#181825";
tab_bar_background = "#11111B";
mark1_foreground = "#1E1E2E";
mark1_background = "#B4BEFE";
mark2_foreground = "#1E1E2E";
mark2_background = "#CBA6F7";
mark3_foreground = "#1E1E2E";
mark3_background = "#74C7EC";
color0 = "#45475A";
color8 = "#585B70";
color1 = "#F38BA8";
color9 = "#F38BA8";
color2 = "#A6E3A1";
color10 = "#A6E3A1";
color3 = "#F9E2AF";
color11 = "#F9E2AF";
color4 = "#89B4FA";
color12 = "#89B4FA";
color5 = "#F5C2E7";
color13 = "#F5C2E7";
color6 = "#94E2D5";
color14 = "#94E2D5";
color7 = "#BAC2DE";
color15 = "#A6ADC8";
};
};
}
#+end_src
*** Mako
This is my notification system. My flake automatically fetches the notification sound, so you
are all set from the get-go!
#+begin_src nix :tangle ../nix/modules/home/mako.nix
{ lib, config, sounds, ... }:
{
services.mako = {
enable = lib.mkDefault config.monorepo.profiles.graphics.enable;
settings = {
on-notify = "exec mpv ${sounds}/polite.ogg --no-config --no-video";
background-color = "#11111bf8";
text-color = "#cdd6f4";
border-color = "#89b4faff";
border-radius = 1;
font = "Fira Code 10";
default-timeout = 3000;
};
};
}
#+end_src
*** Mbsync
Note that in order to use my email configuration, your imaps and smtps servers must be
encrypted. This module uses the ~vars.nix~ as well as the home ~default.nix~ options.
#+begin_src nix :tangle ../nix/modules/home/mbsync.nix
{ lib, config, ... }:
{
programs.mbsync = {
enable = lib.mkDefault config.monorepo.profiles.email.enable;
extraConfig = ''
IMAPAccount ${config.monorepo.vars.internetName}
Host ${config.monorepo.profiles.email.imapsServer}
User ${config.monorepo.profiles.email.email}
PassCmd "cat ${config.sops.secrets.mail.path}"
Port 993
TLSType IMAPS
AuthMechs *
CertificateFile /etc/ssl/certs/ca-certificates.crt
IMAPStore ${config.monorepo.vars.internetName}-remote
Account ${config.monorepo.vars.internetName}
MaildirStore ${config.monorepo.vars.internetName}-local
Path ~/email/${config.monorepo.vars.internetName}/
Inbox ~/email/${config.monorepo.vars.internetName}/INBOX
SubFolders Verbatim
Channel ${config.monorepo.vars.internetName}
Far :${config.monorepo.vars.internetName}-remote:
Near :${config.monorepo.vars.internetName}-local:
Patterns *
Create Near
Sync All
Expunge None
SyncState *
'';
};
}
#+end_src
*** MSMTP
This is the program I use to send email from emacs. It is really the same thing as above,
just set the options to the ones you want in your system ~default.nix~.
#+begin_src nix :tangle ../nix/modules/home/msmtp.nix
{ lib, config, ... }:
{
programs.msmtp = {
enable = lib.mkDefault config.monorepo.profiles.email.enable;
extraConfig = ''
# Set default values for all following accounts.
defaults
auth on
tls on
tls_trust_file /etc/ssl/certs/ca-certificates.crt
tls_certcheck off
logfile ~/.msmtp.log
# Gmail
account ${config.monorepo.vars.userName}
host ${config.monorepo.profiles.email.smtpsServer}
port 587
from ${config.monorepo.profiles.email.email}
user ${config.monorepo.profiles.email.email}
passwordeval "cat ${config.sops.secrets.mail.path}"
# Set a default account
account default : ${config.monorepo.vars.userName}
'';
};
}
#+end_src
*** Mpd
This mpd configuration uses pipewire by default, and it should just work if you place music
in the ~~/music~ directory and then run ~mpc add /~ afterwards.
#+begin_src nix :tangle ../nix/modules/home/mpd.nix
{ lib, config, ... }:
{
services.mpd = {
enable = lib.mkDefault config.monorepo.profiles.music.enable;
dbFile = "/home/${config.monorepo.vars.userName}/.config/mpd/db";
dataDir = "/home/${config.monorepo.vars.userName}/.config/mpd/";
network.port = 6600;
musicDirectory = "/home/${config.monorepo.vars.userName}/music";
playlistDirectory = "/home/${config.monorepo.vars.userName}/.config/mpd/playlists";
network.listenAddress = "0.0.0.0";
extraConfig = ''
audio_output {
type "pipewire"
name "pipewire output"
}
audio_output {
type "httpd"
name "My HTTP Stream"
encoder "opus" # optional
port "8000"
# quality "5.0" # do not define if bitrate is defined
bitrate "128000" # do not define if quality is defined
format "48000:16:1"
always_on "yes" # prevent MPD from disconnecting all listeners when playback is stopped.
tags "yes" # httpd supports sending tags to listening streams.
}
audio_output {
type "shout"
encoding "ogg"
name "my cool stream"
host "localhost"
port "8000"
mount "/example.ogg"
user "source"
password ""
bitrate "64"
format "44100:16:1"
description "Nullring public radio"
}
'';
};
}
#+end_src
*** MPV
I have some emacs + yt-dlp integrations with mpv with my rss feed, and therefore we need it
here:
#+begin_src nix :tangle ../nix/modules/home/mpv.nix
{ lib, config, ... }:
{
programs.mpv = {
enable = lib.mkDefault config.monorepo.profiles.graphics.enable;
config = {
profile = "gpu-hq";
force-window = true;
ytdl-format = "bestvideo+bestaudio";
cache-default = 4000000;
};
};
}
#+end_src
*** Secrets
This uses sops in order to declaratively create the secrets on my system by unencrypting
the yaml file specified. Yes, this is safe to include in the repo.
#+begin_src nix :tangle ../nix/modules/home/secrets.nix
{ config, ... }:
{
sops = {
defaultSopsFile = ../../secrets/secrets.yaml;
age = {
keyFile = "/home/${config.monorepo.vars.userName}/.ssh/keys.txt";
};
secrets = {
mail = {
format = "yaml";
path = "${config.sops.defaultSymlinkPath}/mail";
};
cloudflare-dns = {
format = "yaml";
path = "${config.sops.defaultSymlinkPath}/cloudflare-dns";
};
digikey = {
format = "yaml";
path = "${config.sops.defaultSymlinkPath}/digikey";
};
dn42 = {
format = "yaml";
path = "${config.sops.defaultSymlinkPath}/dn42";
};
znc = {
format = "yaml";
path = "${config.sops.defaultSymlinkPath}/znc";
};
znc_password_salt = {
format = "yaml";
path = "${config.sops.defaultSymlinkPath}/znc_password_salt";
};
znc_password_hash = {
format = "yaml";
path = "${config.sops.defaultSymlinkPath}/znc_password_hash";
};
matrix_bridge = {
format = "yaml";
path = "${config.sops.defaultSymlinkPath}/matrix_bridge";
};
};
defaultSymlinkPath = "/run/user/1000/secrets";
defaultSecretsMountPoint = "/run/user/1000/secrets.d";
};
}
#+end_src
*** Waybar
This is the bar I use for my hyprland configuration. You will need to adjust the monitors field
in the ~default.nix~ for it to really appear.
#+begin_src nix :tangle ../nix/modules/home/waybar.nix
{ lib, config, ... }:
{
programs.waybar = {
enable = lib.mkDefault config.monorepo.profiles.hyprland.enable;
style = ''
,* {
border: none;
border-radius: 0px;
font-family: Iosevka Nerd Font, FontAwesome, Noto Sans CJK;
font-size: 14px;
font-style: normal;
min-height: 0;
}
window#waybar {
background: rgba(30, 30, 46, 0.5);
border-bottom: 1px solid #45475a;
color: #cdd6f4;
}
#workspaces {
background: #45475a;
margin: 5px 5px 5px 5px;
padding: 0px 5px 0px 5px;
border-radius: 16px;
border: solid 0px #f4d9e1;
font-weight: normal;
font-style: normal;
}
#workspaces button {
padding: 0px 5px;
border-radius: 16px;
color: #a6adc8;
}
#workspaces button.active {
color: #f4d9e1;
background-color: transparent;
border-radius: 16px;
}
#workspaces button:hover {
background-color: #cdd6f4;
color: black;
border-radius: 16px;
}
#custom-date, #clock, #battery, #pulseaudio, #network, #custom-randwall, #custom-launcher {
background: transparent;
padding: 5px 5px 5px 5px;
margin: 5px 5px 5px 5px;
border-radius: 8px;
border: solid 0px #f4d9e1;
}
#custom-date {
color: #D3869B;
}
#custom-power {
color: #24283b;
background-color: #db4b4b;
border-radius: 5px;
margin-right: 10px;
margin-top: 5px;
margin-bottom: 5px;
margin-left: 0px;
padding: 5px 10px;
}
#tray {
background: #45475a;
margin: 5px 5px 5px 5px;
border-radius: 16px;
padding: 0px 5px;
/*border-right: solid 1px #282738;*/
}
#clock {
color: #cdd6f4;
background-color: #45475a;
border-radius: 0px 0px 0px 24px;
padding-left: 13px;
padding-right: 15px;
margin-right: 0px;
margin-left: 10px;
margin-top: 0px;
margin-bottom: 0px;
font-weight: bold;
/*border-left: solid 1px #282738;*/
}
#battery {
color: #89b4fa;
}
#battery.charging {
color: #a6e3a1;
}
#battery.warning:not(.charging) {
background-color: #f7768e;
color: #f38ba8;
border-radius: 5px 5px 5px 5px;
}
#backlight {
background-color: #24283b;
color: #db4b4b;
border-radius: 0px 0px 0px 0px;
margin: 5px;
margin-left: 0px;
margin-right: 0px;
padding: 0px 0px;
}
#network {
color: #f4d9e1;
border-radius: 8px;
margin-right: 5px;
}
#pulseaudio {
color: #f4d9e1;
border-radius: 8px;
margin-left: 0px;
}
#pulseaudio.muted {
background: transparent;
color: #928374;
border-radius: 8px;
margin-left: 0px;
}
#custom-randwall {
color: #f4d9e1;
border-radius: 8px;
margin-right: 0px;
}
#custom-launcher {
color: #e5809e;
background-color: #45475a;
border-radius: 0px 24px 0px 0px;
margin: 0px 0px 0px 0px;
padding: 0 20px 0 13px;
/*border-right: solid 1px #282738;*/
font-size: 20px;
}
#custom-launcher button:hover {
background-color: #FB4934;
color: transparent;
border-radius: 8px;
margin-right: -5px;
margin-left: 10px;
}
#custom-playerctl {
background: #45475a;
padding-left: 15px;
padding-right: 14px;
border-radius: 16px;
/*border-left: solid 1px #282738;*/
/*border-right: solid 1px #282738;*/
margin-top: 5px;
margin-bottom: 5px;
margin-left: 0px;
font-weight: normal;
font-style: normal;
font-size: 16px;
}
#custom-playerlabel {
background: transparent;
padding-left: 10px;
padding-right: 15px;
border-radius: 16px;
/*border-left: solid 1px #282738;*/
/*border-right: solid 1px #282738;*/
margin-top: 5px;
margin-bottom: 5px;
font-weight: normal;
font-style: normal;
}
#window {
background: #45475a;
padding-left: 15px;
padding-right: 15px;
border-radius: 16px;
/*border-left: solid 1px #282738;*/
/*border-right: solid 1px #282738;*/
margin-top: 5px;
margin-bottom: 5px;
font-weight: normal;
font-style: normal;
}
#custom-wf-recorder {
padding: 0 20px;
color: #e5809e;
background-color: #1E1E2E;
}
#cpu {
background-color: #45475a;
/*color: #FABD2D;*/
border-radius: 16px;
margin: 5px;
margin-left: 5px;
margin-right: 5px;
padding: 0px 10px 0px 10px;
font-weight: bold;
}
#memory {
background-color: #45475a;
/*color: #83A598;*/
border-radius: 16px;
margin: 5px;
margin-left: 5px;
margin-right: 5px;
padding: 0px 10px 0px 10px;
font-weight: bold;
}
#disk {
background-color: #45475a;
/*color: #8EC07C;*/
border-radius: 16px;
margin: 5px;
margin-left: 5px;
margin-right: 5px;
padding: 0px 10px 0px 10px;
font-weight: bold;
}
#custom-hyprpicker {
background-color: #45475a;
/*color: #8EC07C;*/
border-radius: 16px;
margin: 5px;
margin-left: 5px;
margin-right: 5px;
padding: 0px 11px 0px 9px;
font-weight: bold;
}
'';
settings = {
mainBar = {
layer = "top";
position = "top";
height = 50;
output = config.monorepo.vars.monitors;
modules-left = [ "hyprland/workspaces" ];
modules-center = [ "hyprland/window" ];
modules-right = [ "battery" "clock" ];
battery = {
format = "{icon} {capacity}%";
format-icons = ["" "" "" "" "" ];
};
clock = {
format = "⏰ {:%a %d, %b %H:%M}";
};
};
};
};
}
#+end_src
*** Wofi
This is a run launcher for wayland. I also use it for my powermenu.
#+begin_src nix :tangle ../nix/modules/home/wofi.nix
{ lib, config, ... }:
{
programs.wofi = {
enable = lib.mkDefault config.monorepo.profiles.graphics.enable;
settings = {
location = "bottom-right";
allow_markup = true;
show = "drun";
width = 750;
height = 400;
always_parse_args = true;
show_all = false;
term = "kitty";
hide_scroll = true;
print_command = true;
insensitive = true;
prompt = "Run what, Commander?";
columns = 2;
};
style = ''
@define-color rosewater #f5e0dc;
@define-color rosewater-rgb rgb(245, 224, 220);
@define-color flamingo #f2cdcd;
@define-color flamingo-rgb rgb(242, 205, 205);
@define-color pink #f5c2e7;
@define-color pink-rgb rgb(245, 194, 231);
@define-color mauve #cba6f7;
@define-color mauve-rgb rgb(203, 166, 247);
@define-color red #f38ba8;
@define-color red-rgb rgb(243, 139, 168);
@define-color maroon #eba0ac;
@define-color maroon-rgb rgb(235, 160, 172);
@define-color peach #fab387;
@define-color peach-rgb rgb(250, 179, 135);
@define-color yellow #f9e2af;
@define-color yellow-rgb rgb(249, 226, 175);
@define-color green #a6e3a1;
@define-color green-rgb rgb(166, 227, 161);
@define-color teal #94e2d5;
@define-color teal-rgb rgb(148, 226, 213);
@define-color sky #89dceb;
@define-color sky-rgb rgb(137, 220, 235);
@define-color sapphire #74c7ec;
@define-color sapphire-rgb rgb(116, 199, 236);
@define-color blue #89b4fa;
@define-color blue-rgb rgb(137, 180, 250);
@define-color lavender #b4befe;
@define-color lavender-rgb rgb(180, 190, 254);
@define-color text #cdd6f4;
@define-color text-rgb rgb(205, 214, 244);
@define-color subtext1 #bac2de;
@define-color subtext1-rgb rgb(186, 194, 222);
@define-color subtext0 #a6adc8;
@define-color subtext0-rgb rgb(166, 173, 200);
@define-color overlay2 #9399b2;
@define-color overlay2-rgb rgb(147, 153, 178);
@define-color overlay1 #7f849c;
@define-color overlay1-rgb rgb(127, 132, 156);
@define-color overlay0 #6c7086;
@define-color overlay0-rgb rgb(108, 112, 134);
@define-color surface2 #585b70;
@define-color surface2-rgb rgb(88, 91, 112);
@define-color surface1 #45475a;
@define-color surface1-rgb rgb(69, 71, 90);
@define-color surface0 #313244;
@define-color surface0-rgb rgb(49, 50, 68);
@define-color base #1e1e2e;
@define-color base-rgb rgb(30, 30, 46);
@define-color mantle #181825;
@define-color mantle-rgb rgb(24, 24, 37);
@define-color crust #11111b;
@define-color crust-rgb rgb(17, 17, 27);
* {
font-family: 'Iosevka Nerd Font', monospace;
font-size: 14px;
}
/* Window */
window {
margin: 0px;
padding: 10px;
border: 0.16em solid @lavender;
border-radius: 0.1em;
background-color: @base;
animation: slideIn 0.5s ease-in-out both;
}
/* Slide In */
@keyframes slideIn {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
/* Inner Box */
#inner-box {
margin: 5px;
padding: 10px;
border: none;
background-color: @base;
animation: fadeIn 0.5s ease-in-out both;
}
/* Fade In */
@keyframes fadeIn {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
/* Outer Box */
#outer-box {
margin: 5px;
padding: 10px;
border: none;
background-color: @base;
}
/* Scroll */
#scroll {
margin: 0px;
padding: 10px;
border: none;
background-color: @base;
}
/* Input */
#input {
margin: 5px 20px;
padding: 10px;
border: none;
border-radius: 0.1em;
color: @text;
background-color: @base;
animation: fadeIn 0.5s ease-in-out both;
}
#input image {
border: none;
color: @red;
}
#input * {
outline: 4px solid @red!important;
}
/* Text */
#text {
margin: 5px;
border: none;
color: @text;
animation: fadeIn 0.5s ease-in-out both;
}
#entry {
background-color: @base;
}
#entry arrow {
border: none;
color: @lavender;
}
/* Selected Entry */
#entry:selected {
border: 0.11em solid @lavender;
}
#entry:selected #text {
color: @mauve;
}
#entry:drop(active) {
background-color: @lavender!important;
}
'';
};
}
#+end_src
*** yt-dlp
A classic program that allows you to download from youtube. Also has integrations with mpv.
#+begin_src nix :tangle ../nix/modules/home/yt-dlp.nix
{ lib, config, ... }:
{
programs.yt-dlp = {
enable = lib.mkDefault config.monorepo.profiles.graphics.enable;
settings = {
embed-thumbnail = true;
embed-subs = true;
sub-langs = "all";
downloader = "aria2c";
downloader-args = "aria2c:'-c -x8 -s8 -k1M'";
};
};
}
#+end_src
*** Zsh
My zsh config has some useful aliases that one should read through. Otherwise it is pretty
standard.
#+begin_src nix :tangle ../nix/modules/home/zsh.nix
{ lib, config, pkgs, systemHostName, ... }:
{
programs.zsh = {
enable = true;
initContent = ''
umask 0022
export EXTRA_CCFLAGS="-I/usr/include"
source ${pkgs.zsh-vi-mode}/share/zsh-vi-mode/zsh-vi-mode.plugin.zsh
export QT_QPA_PLATFORM="wayland"
export OLLAMA_MODEL="qwen3:14b"
'';
localVariables = {
EDITOR = "emacsclient --create-frame --alternate-editor=vim";
INPUT_METHOD = "fcitx";
QT_IM_MODULE = "fcitx";
GTK_IM_MODULE = "fcitx";
XMODIFIERS = "@im=fcitx";
XIM_SERVERS = "fcitx";
WXSUPPRESS_SIZER_FLAGS_CHECK = "1";
};
shellAliases = {
get-channel-id = "yt-dlp --print \"%(channel_id)s\" --playlist-end 1 \"$1\"";
se = "sops edit";
f = "vim $(fzf)";
e = "cd $(find . -type d -print | fzf)";
c = "clear";
g = "git";
v = "vim";
py = "python3";
rb = "sudo nixos-rebuild switch --flake $HOME/monorepo/nix#${systemHostName}";
nfu = "cd ~/monorepo/nix && git add . && git commit -m \"new flake lock\" && nix flake update";
usync = "rsync -azvP --chmod=\"Du=rwx,Dg=rx,Do=rx,Fu=rw,Fg=r,Fo=r\" ~/website_html/ root@${config.monorepo.vars.remoteHost}:/var/www/${config.monorepo.vars.internetName}-website/";
usite
= "cd ~/src/publish-org-roam-ui && bash local.sh && rm -rf ~/website_html/graph_view; cp -r ~/src/publish-org-roam-ui/out ~/website_html/graph_view && rsync -azvP --chmod=\"Du=rwx,Dg=rx,Do=rx,Fu=rw,Fg=r,Fo=r\" ~/website_html/ root@${config.monorepo.vars.remoteHost}:/var/www/${config.monorepo.vars.internetName}-website/";
sai = "eval \"$(ssh-agent -s)\" && ssh-add ~/.ssh/id_ed25519 && ssh-add -l";
i3 = "exec ${pkgs.i3-gaps}/bin/i3";
};
loginExtra = ''
if [[ "$(tty)" = "/dev/tty1" ]]; then
exec Hyprland
fi
'';
};
}
#+end_src
*** Pantalaimon
This is used with ement as a proxy in order to connect to a remote
matrix server while having encryption.
#+begin_src nix :tangle ../nix/modules/home/pantalaimon.nix
{ lib, config, ... }:
{
services.pantalaimon = {
enable = lib.mkDefault config.monorepo.profiles.graphics.enable;
settings = {
Default = {
LogLevel = "Debug";
SSL = true;
};
local-matrix = {
Homeserver = "https://matrix.${config.monorepo.vars.orgHost}";
ListenAddress = "127.0.0.1";
ListenPort = "8008";
};
};
};
}
#+end_src
*** User
This configuration is the backbone configuration for the default user. It specifies some
generally useful packages and something every home should have, as well as some dependencies
for these configurations.
#+begin_src nix :tangle ../nix/modules/home/user.nix
{ lib, config, pkgs, ... }:
{
home = {
activation.startup-files = lib.hm.dag.entryAfter [ "installPackages" ] ''
if [ ! -d "/home/${config.monorepo.vars.userName}/email/${config.monorepo.vars.internetName}/" ]; then
mkdir -p /home/${config.monorepo.vars.userName}/email/${config.monorepo.vars.internetName}/
fi
if [ ! -d "/home/${config.monorepo.vars.userName}/music" ]; then
mkdir -p /home/${config.monorepo.vars.userName}/music
fi
if [ ! -d /home/${config.monorepo.vars.userName}/org ]; then
mkdir -p /home/${config.monorepo.vars.userName}/org
fi
if [ ! -d /home/${config.monorepo.vars.userName}/src ]; then
mkdir -p /home/${config.monorepo.vars.userName}/src
fi
touch /home/${config.monorepo.vars.userName}/org/agenda.org
touch /home/${config.monorepo.vars.userName}/org/notes.org
'';
enableNixpkgsReleaseCheck = false;
username = config.monorepo.vars.userName;
homeDirectory = "/home/${config.monorepo.vars.userName}";
stateVersion = "24.11";
packages = with pkgs; (if config.monorepo.profiles.graphics.enable then [
# wikipedia
# kiwix kiwix-tools
mupdf
zathura
fzf
# passwords
age sops
# formatting
ghostscript texliveFull pandoc
# Emacs Deps
graphviz jq
# Apps
# octaveFull
vesktop grim swww vim telegram-desktop qwen-code fluffychat
# Sound/media
pavucontrol alsa-utils imagemagick ffmpeg helvum
# Net
curl rsync git iamb
# Tor
torsocks tor-browser
# fonts
nerd-fonts.iosevka noto-fonts noto-fonts-cjk-sans noto-fonts-emoji fira-code font-awesome_6 victor-mono
(aspellWithDicts
(dicts: with dicts; [ en en-computers en-science ]))
# Misc.
pinentry
x11_ssh_askpass
xdg-utils
acpilight
pfetch
libnotify
htop
(pkgs.writeShellScriptBin "help"
''
#!/usr/bin/env sh
# Portable, colored, nicely aligned alias list
# Generate uncolored alias pairs
aliases=$(cat <<'EOF'
${let aliases = config.programs.zsh.shellAliases;
in lib.concatStringsSep "\n" (lib.mapAttrsToList (name: value:
"${name} -> ${value}"
) aliases)}
EOF
)
# Align and color using awk
echo "$aliases" | awk '
BEGIN {
GREEN="\033[0;32m";
YELLOW="\033[0;33m";
RESET="\033[0m";
maxlen=0;
}
{
# Split line on " -> "
split($0, parts, / -> /);
name[NR]=parts[1];
cmd[NR]=parts[2];
if(length(parts[1])>maxlen) maxlen=length(parts[1]);
}
END {
for(i=1;i<=NR;i++) {
# printf with fixed width for alias name
printf "%s%-*s%s -> %s%s%s\n", GREEN, maxlen, name[i], RESET, YELLOW, cmd[i], RESET;
}
}'
'')
(writeShellScriptBin "remote-build"
''
#!/bin/bash
nixos-rebuild --sudo --ask-sudo-password --target-host "$1" switch --flake $HOME/monorepo/nix#spontaneity
''
)
(writeShellScriptBin "install-vps"
''
#!/bin/bash
nix run github:nix-community/nixos-anywhere -- --generate-hardware-config nixos-generate-config $HOME/monorepo/nix/systems/spontaneity/hardware-configuration.nix --flake $HOME/monorepo/nix#spontaneity --target-host "$1"
'')
] else [
pfetch
# net
curl
torsocks
rsync
]);
};
services = {
gpg-agent = {
pinentry.package = pkgs.pinentry-emacs;
enable = true;
extraConfig = ''
allow-emacs-pinentry
allow-loopback-pinentry
'';
};
};
programs.bash.enable = true;
gtk = {
enable = lib.mkDefault config.monorepo.profiles.graphics.enable;
theme = null;
iconTheme = null;
};
fonts.fontconfig.enable = true;
}
#+end_src
* Systems
** Home
This module dynamically imports the correct corresponding home.nix at
the path.
#+begin_src nix :tangle ../nix/systems/home.nix
{ config, sops-nix, ... }:
{
home-manager = {
sharedModules = [
sops-nix.homeManagerModules.sops
];
useGlobalPkgs = true;
useUserPackages = true;
users."${config.monorepo.vars.userName}" = import (./. + "/${config.networking.hostName}/home.nix");
};
}
#+end_src
** Common
These are the common includes for each of my systems. This ensures that we don't have to duplicate includes every time we want to add a new
system. Also more common configuration can go here.
#+begin_src nix :tangle ../nix/systems/common.nix
{ config, lib, ... }:
{
imports = [
./home.nix
../modules/default.nix
];
# Put configuration (e.g. monorepo variable configuration) common to all configs here
}
#+end_src
** Home Manager Common
#+begin_src nix :tangle ../nix/systems/home-common.nix
{ lib, config, ... }:
{
imports = [
../modules/home/default.nix
];
# Put configuration (e.g. monorepo variable configuration) common to all configs here
}
#+end_src
** Continuity
This is pretty understandable, if you understand all the above.
#+begin_src nix :tangle ../nix/systems/continuity/default.nix
{ ... }:
{
imports = [
../../disko/btrfs-simple.nix
../common.nix
];
config = {
monorepo = {
profiles.impermanence.enable = true;
vars = {
device = "/dev/sda";
fileSystem = "btrfs";
};
};
};
}
#+end_src
*** Home
Each system has a corresponding home configuration in order to set
monorepo home options.
#+begin_src nix :tangle ../nix/systems/continuity/home.nix
{ lib, config, pkgs, ... }:
{
imports = [
../home-common.nix
];
config.monorepo.profiles.workstation.enable = false;
}
#+end_src
** Affinity
This is my configuration for my workstation. It runs ollama, as well
as several other useful services.
#+begin_src nix :tangle ../nix/systems/affinity/default.nix
{ config, lib, home-manager, ... }:
{
imports = [
../common.nix
../../disko/drive-simple.nix
];
config = {
monorepo = {
vars.device = "/dev/nvme0n1";
profiles = {
server.enable = false;
cuda.enable = true;
workstation.enable = true;
};
};
};
}
#+end_src
*** Home
I want cuda in home manager too.
#+begin_src nix :tangle ../nix/systems/affinity/home.nix
{ lib, config, pkgs, ... }:
{
imports = [
../home-common.nix
];
config.monorepo = {
profiles.cuda.enable = true;
};
}
#+end_src
** Spontaneity
Spontaneity is my VPS instance.
#+begin_src nix :tangle ../nix/systems/spontaneity/default.nix
{ config, lib, ... }:
let
ipv4addr = "66.42.84.130";
ipv6addr = "2001:19f0:5401:10d0:5400:5ff:fe4a:7794";
in
{
imports = [
../common.nix
../../disko/drive-bios.nix
# nixos-anywhere generates this file
./hardware-configuration.nix
];
config = {
monorepo = {
vars.device = "/dev/vda";
profiles = {
server.enable = true;
ttyonly.enable = true;
grub.enable = true;
};
};
boot.loader.grub.device = "nodev";
networking = {
interfaces.ens3.ipv6.addresses = [
{
address = ipv6addr;
prefixLength = 64;
}
];
firewall.allowedTCPPorts = [
80
143
443
465
587
993
6697
6667
8448
];
domains = {
enable = true;
baseDomains = {
"${config.monorepo.vars.remoteHost}" = {
a.data = ipv4addr;
aaaa.data = ipv6addr;
};
"${config.monorepo.vars.orgHost}" = {
a.data = ipv4addr;
aaaa.data = ipv6addr;
};
};
subDomains = {
"${config.monorepo.vars.remoteHost}" = {};
"matrix.${config.monorepo.vars.remoteHost}" = {};
"www.${config.monorepo.vars.remoteHost}" = {};
"mail.${config.monorepo.vars.remoteHost}" = {
mx.data = "10 mail.${config.monorepo.vars.remoteHost}.";
};
"${config.monorepo.vars.orgHost}" = {};
"git.${config.monorepo.vars.orgHost}" = {};
"matrix.${config.monorepo.vars.orgHost}" = {};
"talk.${config.monorepo.vars.orgHost}" = {};
"mail.${config.monorepo.vars.orgHost}" = {};
"${config.monorepo.vars.internetName}.${config.monorepo.vars.orgHost}" = {};
};
};
};
};
}
#+end_src
*** Home
#+begin_src nix :tangle ../nix/systems/spontaneity/home.nix
{ lib, config, pkgs, ... }:
{
imports = [
../home-common.nix
];
config.monorepo.profiles.enable = false;
}
#+end_src
** Installer
My installer installs my systems almost completely without interaction. You can also make them
install the exact version of the system that you want it to by pinning the commits to make it
always work in the exact same deterministic way.
*** Commit Hash Pinning
Modify this to pin the installer image hash to make the installer image always
work deterministically.
#+begin_src nix :tangle ../nix/systems/installer/commits.nix
{
diskoCommitHash = "c8a0e78d86b12ea67be6ed0f7cae7f9bfabae75a";
monorepoCommitHash = "e2fbfac7ad7c0b4d438179b31d2ed53d884313a0";
monorepoUrl = "https://github.com/ret2pop/monorepo";
}
#+end_src
*** ISO Default Profile
This contains the installation script I use to install my systems.
#+begin_src nix :tangle ../nix/systems/installer/default.nix
{ pkgs, config, lib, modulesPath, ... }:
let
commits = import ./commits.nix;
in
{
imports = [
(modulesPath + "/installer/cd-dvd/installation-cd-minimal.nix")
];
networking = {
networkmanager = {
enable = true;
};
firewall = {
allowedTCPPorts = [ 22 ];
allowedUDPPorts = [ ];
};
wireless.enable = false;
};
services.openssh = {
enable = true;
ports = [ 22 ];
settings = {
PasswordAuthentication = false;
AllowUsers = null;
UseDns = true;
PermitRootLogin = lib.mkForce "prohibit-password";
};
};
users.extraUsers.root.password = "nixos";
users.extraUsers.nixos.password = "nixos";
users.users = {
root.openssh.authorizedKeys.keys = [
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICts6+MQiMwpA+DfFQxjIN214Jn0pCw/2BDvOzPhR/H2 preston@continuity-dell"
];
nixos = {
packages = with pkgs; [
git
curl
gum
(writeShellScriptBin "nix_installer"
''
#!/usr/bin/env bash
set -euo pipefail
if [ "$(id -u)" -eq 0 ]; then
echo "ERROR! $(basename "$0") should be run as a regular user"
exit 1
fi
gum style --border normal --margin "1" --padding "1 2" "Notice: if using full disk encryption, write to /tmp/secret.key first with your password."
sleep 3
cd "$HOME"
ping -q -c1 google.com &>/dev/null && echo "online! Proceeding with the installation..." || nmtui
if [ ! -d "$HOME/monorepo/" ]; then
git clone ${commits.monorepoUrl}
cd "$HOME/monorepo"
git checkout "${commits.monorepoCommitHash}"
cd "$HOME"
fi
gum style --border normal --margin "1" --padding "1 2" "Choose a system to install or select `new` in order to create a new system."
SYSTEM="$(gum choose $(find "$HOME/monorepo/nix/systems" -mindepth 1 -maxdepth 1 -type d -printf "%f\n" | grep -v -E 'installer'; printf "New"))"
if [[ "$SYSTEM" == "New" ]]; then
gum style --border normal --margin "1" --padding "1 2" "Choose a system name"
SYSTEM="$(gum input --placeholder "system name")"
gum style --border normal --margin "1" --padding "1 2" "Select a drive file or create a new drive file."
DRIVE="$(gum choose $(find "$HOME/monorepo/nix/disko" -mindepth 1 -maxdepth 1 -type f -printf "%f\n"; printf "New"))"
if [[ "$DRIVE" == "New" ]]; then
gum style --border normal --margin "1" --padding "1 2" "Choose a name to call your drive file."
DRIVE="$(gum input --placeholder "drive file name (ex: partition_scheme.nix)")"
fi
fi
if [ ! -d "$HOME/monorepo/nix/systems/$SYSTEM" ]; then
mkdir -p "$HOME/monorepo/nix/systems/$SYSTEM"
cp "$HOME/monorepo/nix/systems/continuity/home.nix" "$HOME/monorepo/nix/systems/$SYSTEM/home.nix"
cat > "$HOME/monorepo/nix/systems/$SYSTEM/default.nix" </dev/null
vim "$HOME/monorepo/nix/systems/$SYSTEM/default.nix"
gum style --border normal --margin "1" --padding "1 2" "Edit the home default.nix with options."
gum input --placeholder "Press Enter to continue" >/dev/null
vim "$HOME/monorepo/nix/systems/$SYSTEM/home.nix"
sed -i "/hostnames = \[/,/];/ s/];/ \"$1\"\n ];/" "$HOME/monorepo/nix/flake.nix"
if [ ! -f "$HOME/monorepo/nix/disko/$DRIVE" ]; then
cp "$HOME/monorepo/nix/disko/drive-simple.nix" "$HOME/monorepo/nix/disko/$DRIVE"
gum style --border normal --margin "1" --padding "1 2" "Edit the drive file with your preferred partitioning scheme."
gum input --placeholder "Press Enter to continue" >/dev/null
vim "$HOME/monorepo/nix/disko/$DRIVE"
fi
cd "$HOME/monorepo" && git add . && cd "$HOME"
fi
nix --extra-experimental-features 'nix-command flakes' eval "$HOME/monorepo/nix#evalDisko.$SYSTEM" > "$HOME/drive.nix"
gum style --border normal --margin "1" --padding "1 2" "Formatting the drive is destructive!"
if gum confirm "Are you sure you want to continue?"; then
echo "Proceeding..."
else
echo "Aborting."
exit 1
fi
sudo nix --experimental-features "nix-command flakes" run "github:nix-community/disko/${commits.diskoCommitHash}" -- --mode destroy,format,mount "$HOME/drive.nix"
cd /mnt
sudo nixos-install --flake "$HOME/monorepo/nix#$SYSTEM"
target_user="$(ls /mnt/home | head -n1)"
if [ -z "$target_user" ]; then
echo "No user directories found in /mnt/home"
exit 1
fi
sudo cp -r "$HOME/monorepo" "/mnt/home/$target_user/"
echo "rebooting..."; sleep 3; reboot
'')
];
};
};
systemd = {
services.sshd.wantedBy = pkgs.lib.mkForce [ "multi-user.target" ];
targets = {
sleep.enable = false;
suspend.enable = false;
hibernate.enable = false;
hybrid-sleep.enable = false;
};
};
}
#+end_src