Compare commits

..

No commits in common. "master" and "peertube" have entirely different histories.

92 changed files with 2343 additions and 5852 deletions

View file

@ -4,8 +4,6 @@ keys:
- &nki_pc age1tt0peqg8zdfh74m5sdgwsczcqh036nhgmwvkqnvywll88uvmm9xs433rhm
- &nkagami_main age1n8tnmmgredzltzwkspag7aufhrn6034ny8ysjeulhkwdnf7vqqaqec4mg5
- &nkagami_do age1z2h24mjt80fryqupajkh3kg5r4sjgw65uqy489xeqxhqj8u2a9fsm3ff36
- &nki_yoga age1vhjhmxura35apu5zdwg5ur5r40xay45ld9szh07dy0ph9chgsu7shfm4h9
- &nki_framework age188tgu3psvywk6shq85mk2q0jdjwd0tcswzwlwu5pa5n3pndx75dq090z59
creation_rules:
- path_regex: kagami-air-m1/secrets\.yaml$
key_groups:
@ -18,20 +16,9 @@ creation_rules:
- *nki_pc
- *nkagami_main
- *nkagami_do
- *nki_framework
- path_regex: nki-home/secrets\.yaml$
- path_regex: nki-home/secrets/secrets\.yaml$
key_groups:
- age:
- *nki_pc
- *nkagami_main
- *nkagami_do
- path_regex: nki-yoga-g8/secrets\.yaml$
key_groups:
- age:
- *nki_yoga
- age1axvjllyv2gutngwmp3pvp4xtq2gqneldaq2c4nrzmaye0uwmk9lqsealdv # The machine itself
- path_regex: nki-framework/secrets\.yaml$
key_groups:
- age:
- *nki_framework
- age1vgh6kvee8lvxylm7z86fpl3xzjyjs4u3zdfkyf064rjvxk9fpumsew7n27 # The machine itself

View file

@ -13,8 +13,6 @@ in
with lib; {
imports = [
# defaultShell
./modules/services/nix-cache
./modules/services/nix-build-farm
];
## Packages

File diff suppressed because it is too large Load diff

186
flake.nix
View file

@ -2,70 +2,66 @@
description = "nki's systems";
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-24.11";
nixpkgs.url = "github:nixos/nixpkgs/nixos-23.11";
nixpkgs-unstable.url = "github:nixos/nixpkgs/nixpkgs-unstable";
nixos-hardware.url = "github:nixos/nixos-hardware";
darwin.url = "github:lnl7/nix-darwin/master";
darwin.inputs.nixpkgs.follows = "nixpkgs-unstable";
home-manager.url = "github:nix-community/home-manager/release-24.11";
home-manager.url = "github:nix-community/home-manager/release-23.11";
home-manager.inputs.nixpkgs.follows = "nixpkgs";
home-manager-unstable.url = "github:nix-community/home-manager";
home-manager-unstable.inputs.nixpkgs.follows = "nixpkgs-unstable";
sops-nix.url = "github:Mic92/sops-nix";
sops-nix.inputs.nixpkgs.follows = "nixpkgs-unstable";
sops-nix.inputs.nixpkgs-stable.follows = "nixpkgs";
deploy-rs.url = "github:Serokell/deploy-rs";
nur.url = "github:nix-community/NUR";
# --- Secure boot
lanzaboote = {
url = github:nix-community/lanzaboote/v0.4.1;
url = github:nix-community/lanzaboote/v0.3.0;
inputs.nixpkgs.follows = "nixpkgs";
};
# --- Build tools
flake-utils.url = github:numtide/flake-utils;
crane.url = github:ipetkov/crane;
rust-overlay = {
url = "github:oxalica/rust-overlay";
inputs.nixpkgs.follows = "nixpkgs";
};
arion.url = github:hercules-ci/arion;
lix-module = {
url = "https://git.lix.systems/lix-project/nixos-module/archive/2.91.1-1.tar.gz";
inputs.nixpkgs.follows = "nixpkgs";
};
# ---
# Imported apps
youmubot.url = "github:natsukagami/youmubot/osu-commands";
# swayfx = {
# url = github:WillPower3309/swayfx;
# inputs.nixpkgs.follows = "nixpkgs";
# };
youmubot.url = "github:natsukagami/youmubot";
swayfx = {
url = github:WillPower3309/swayfx;
inputs.nixpkgs.follows = "nixpkgs";
};
mpd-mpris = {
url = github:natsukagami/mpd-mpris;
inputs.nixpkgs.follows = "nixpkgs";
};
dtth-phanpy.url = "git+ssh://gitea@git.dtth.ch/nki-dtth/phanpy?ref=dtth-fork";
conduit.url = "gitlab:famedly/conduit/v0.9.0";
nix-gaming.url = github:fufexan/nix-gaming;
zen-browser.url = "github:youwen5/zen-browser-flake";
niri.url = "github:sodiboo/niri-flake";
dtth-phanpy.url = "git+ssh://gitea@git.dtth.ch/nki/phanpy?branch=dtth-fork";
conduit = {
url = gitlab:famedly/conduit/next;
inputs.nixpkgs.follows = "nixpkgs-unstable";
};
eza.url = github:eza-community/eza/v0.12.0;
eza.inputs.nixpkgs.follows = "nixpkgs";
nix-gaming.url = github:fufexan/nix-gaming/22586cc4910284c9c7306f19dcd63ac0adbcbcc9; # until we jump to 24.05
# --- Sources
kakoune.url = github:mawww/kakoune;
kakoune.flake = false;
kak-lsp.url = github:kakoune-lsp/kakoune-lsp;
kak-lsp.url = github:natsukagami/kak-lsp/metals-support;
kak-lsp.flake = false;
nixos-m1.url = github:tpwrules/nixos-apple-silicon;
nixos-m1.inputs.nixpkgs.follows = "nixpkgs";
# Nixpkgs with new peertube, see #273769
nixpkgs-peertube.url = "github:Izorkin/nixpkgs/update-peertube";
# ---
# DEPLOYMENT ONLY! secrets
secrets.url = "git+ssh://git@github.com/natsukagami/nix-deploy-secrets";
};
outputs = { self, darwin, nixpkgs, nixpkgs-unstable, home-manager, deploy-rs, sops-nix, ... }@inputs:
outputs = { self, darwin, nixpkgs, nixpkgs-unstable, home-manager, deploy-rs, sops-nix, nur, ... }@inputs:
let
overlays = import ./overlay.nix inputs;
lib = nixpkgs.lib;
@ -77,12 +73,23 @@
nixpkgsAsRegistry_ = stable: { lib, ... }: {
imports = [ applyOverlays ];
nix.registry.current-system.flake = self;
nix.registry.nixpkgs.flake = stable;
nix.registry.nixpkgs-unstable.flake = nixpkgs-unstable;
nixpkgs.config.allowUnfree = true;
nix.nixPath = lib.mkDefault [
nix.nixPath = [
"nixpkgs=${nixpkgs}"
"nixpkgs-unstable=${nixpkgs-unstable}"
"/nix/var/nix/profiles/per-user/root/channels"
];
# Binary Cache for Haskell.nix
nix.settings.trusted-public-keys = [
# "hydra.iohk.io:f/Ea+s+dFdN+3Y/G+FDgSq+a5NEWhJGzdjvKNGv0/EQ="
];
nix.settings.substituters = [
# "https://cache.iog.io"
];
};
nixpkgsAsRegistry = nixpkgsAsRegistry_ nixpkgs;
osuStable = { pkgs, ... }: {
nix.settings = {
@ -90,20 +97,6 @@
trusted-public-keys = [ "nix-gaming.cachix.org-1:nbjlureqMbRAxR1gJ/f3hxemL9svXaZF/Ees8vCUUs4=" ];
};
environment.systemPackages = [ inputs.nix-gaming.packages.${pkgs.hostPlatform.system}.osu-stable ];
programs.gamemode = {
enable = true;
enableRenice = true;
settings = {
general = {
renice = 10;
};
custom = {
start = "${pkgs.libnotify}/bin/notify-send 'GameMode started'";
end = "${pkgs.libnotify}/bin/notify-send 'GameMode ended'";
};
};
};
};
# Common Nix modules
@ -112,7 +105,6 @@
(nixpkgsAsRegistry_ stable)
./common.nix
sops-nix.nixosModules.sops
inputs.lix-module.nixosModules.default
];
};
common-nixos = stable: { ... }: {
@ -122,39 +114,9 @@
(common-nix stable)
inputs.secrets.nixosModules.common
inputs.nix-gaming.nixosModules.pipewireLowLatency
inputs.niri.nixosModules.niri
];
};
mkPersonalSystem = nixpkgs-module: system: { configuration
, homeManagerUsers ? { }
, extraModules ? [ ]
, includeCommonModules ? true
,
}:
let
home-manager-module =
if nixpkgs-module == inputs.nixpkgs then inputs.home-manager
else if nixpkgs-module == inputs.nixpkgs-unstable then inputs.home-manager-unstable
else builtins.abort "Unknown nixpkgs module, use `nixpkgs` or `nixpkgs-unstable`";
in
nixpkgs-module.lib.nixosSystem {
inherit system;
modules =
(if includeCommonModules then [
(common-nixos nixpkgs-module)
] else [ ]) ++ [
configuration
# Home Manager
home-manager-module.nixosModules.home-manager
{
home-manager.useGlobalPkgs = true;
home-manager.useUserPackages = true;
home-manager.users = homeManagerUsers;
}
] ++ extraModules;
};
in
{
overlays.default = lib.composeManyExtensions overlays;
@ -178,16 +140,29 @@
};
# Home configuration
nixosConfigurations."kagamiPC" = mkPersonalSystem nixpkgs-unstable "x86_64-linux" {
configuration = ./nki-home/configuration.nix;
homeManagerUsers.nki = import ./home/kagami-pc-home.nix;
extraModules = [ osuStable ];
nixosConfigurations."kagamiPC" = nixpkgs.lib.nixosSystem rec {
system = "x86_64-linux";
modules = [
(common-nixos nixpkgs)
./nki-home/configuration.nix
osuStable
inputs.home-manager.nixosModules.home-manager
{
home-manager.useGlobalPkgs = true;
home-manager.useUserPackages = true;
home-manager.users.nki = { ... }: {
imports = [
./home/kagami-pc-home.nix
];
};
}
];
};
# yoga g8 configuration
nixosConfigurations."nki-yoga-g8" = mkPersonalSystem nixpkgs "x86_64-linux" {
configuration = ./nki-yoga-g8/configuration.nix;
homeManagerUsers.nki = import ./home/nki-x1c1.nix;
extraModules = [
nixosConfigurations."nki-yoga-g8" = nixpkgs.lib.nixosSystem rec {
system = "x86_64-linux";
modules = [
(common-nixos nixpkgs)
inputs.lanzaboote.nixosModules.lanzaboote
({ ... }: {
# Sets up secure boot
@ -197,15 +172,21 @@
pkiBundle = "/etc/secureboot";
};
})
./nki-yoga-g8/configuration.nix
home-manager.nixosModules.home-manager
{
home-manager.useGlobalPkgs = true;
home-manager.useUserPackages = true;
home-manager.users.nki = import ./home/nki-x1c1.nix;
}
];
};
# framework configuration
nixosConfigurations."nki-framework" = mkPersonalSystem nixpkgs-unstable "x86_64-linux" {
configuration = ./nki-framework/configuration.nix;
homeManagerUsers.nki = import ./home/nki-framework.nix;
extraModules = [
nixosConfigurations."nki-framework" = nixpkgs.lib.nixosSystem rec {
system = "x86_64-linux";
modules = [
(common-nixos nixpkgs)
inputs.lanzaboote.nixosModules.lanzaboote
inputs.nixos-hardware.nixosModules.framework-13-7040-amd
({ ... }: {
# Sets up secure boot
boot.loader.systemd-boot.enable = lib.mkForce false;
@ -214,21 +195,40 @@
pkiBundle = "/etc/secureboot";
};
})
./nki-framework/configuration.nix
home-manager.nixosModules.home-manager
{
home-manager.useGlobalPkgs = true;
home-manager.useUserPackages = true;
home-manager.users.nki = import ./home/nki-framework.nix;
}
];
};
# macbook nixos
nixosConfigurations."kagami-air-m1" = mkPersonalSystem nixpkgs "aarch64-linux" {
configuration = ./kagami-air-m1/configuration.nix;
homeManagerUsers.nki = import ./home/macbook-nixos.nix;
extraModules = [ inputs.nixos-m1.nixosModules.apple-silicon-support ];
nixosConfigurations."kagami-air-m1" = inputs.nixpkgs.lib.nixosSystem rec {
system = "aarch64-linux";
modules = [
(common-nixos inputs.nixpkgs)
inputs.nixos-m1.nixosModules.apple-silicon-support
./kagami-air-m1/configuration.nix
inputs.home-manager.nixosModules.home-manager
{
home-manager.useGlobalPkgs = true;
home-manager.useUserPackages = true;
home-manager.users.nki = import ./home/macbook-nixos.nix;
}
];
};
# DigitalOcean node
nixosConfigurations."nki-personal-do" = mkPersonalSystem nixpkgs "x86_64-linux" {
configuration = ./nki-personal-do/configuration.nix;
extraModules = [
nixosConfigurations."nki-personal-do" = nixpkgs.lib.nixosSystem rec {
system = "x86_64-linux";
modules = [
(common-nixos nixpkgs)
inputs.arion.nixosModules.arion
./modules/my-tinc
inputs.youmubot.nixosModules.default
./nki-personal-do/configuration.nix
inputs.secrets.nixosModules.nki-personal-do
];
};
@ -243,8 +243,8 @@
# This is highly advised, and will prevent many possible mistakes
checks = builtins.mapAttrs (system: deployLib: deployLib.deployChecks self.deploy) deploy-rs.lib;
} // (inputs.flake-utils.lib.eachDefaultSystem (system: {
formatter = nixpkgs.legacyPackages.${system}.nixpkgs-fmt;
}));
}

View file

@ -1,54 +1,48 @@
{ pkgs, config, lib, ... }:
with lib;
let
kwallet = { pkgs, lib, ... }: {
home.packages = with pkgs; [ kdePackages.kwallet kdePackages.ksshaskpass ];
home.sessionVariables = {
# https://wiki.archlinux.org/title/KDE_Wallet#Using_the_KDE_Wallet_to_store_ssh_key_passphrases
SSH_ASKPASS = lib.getExe pkgs.kdePackages.ksshaskpass;
SSH_ASKPASS_REQUIRE = "prefer";
};
# Enable this for sway
wayland.windowManager.sway.config.startup = [
{ command = "${pkgs.kdePackages.kwallet-pam}/libexec/pam_kwallet_init"; }
];
# Automatic dbus activation
xdg.dataFile."dbus-1/services/org.freedesktop.secrets.service".text = ''
[D-BUS Service]
Name=org.freedesktop.secrets
Exec=${pkgs.kdePackages.kwallet}/bin/kwalletd6
'';
};
in
{
with lib; {
imports = [
./modules/monitors.nix
./modules/linux/graphical
./modules/X11/xfce4-notifyd.nix
./modules/programs/discord.nix
kwallet
];
config = (mkIf pkgs.stdenv.isLinux {
home.packages = with pkgs; [
psmisc # killall and friends
file # Query file type
nix-output-monitor
pinentry-gnome3 # until pinentry-qt introduces caching
pinentry-gnome
# Java stuff
jdk21
sbt
];
## Gnome-keyring
services.gnome-keyring = {
enable = true;
components = [ "pkcs11" "secrets" "ssh" ];
};
# services.gpg-agent.enable = true;
# services.gpg-agent.pinentryFlavor = "curses";
# services.gpg-agent.enableSshSupport = true;
# Git "safe-directory"
programs.git.extraConfig.safe.directory = [
"${config.home.homeDirectory}/.config/nixpkgs"
];
systemd.user.startServices = "sd-switch";
# Audio stuff!
# services.easyeffects.enable = true;
services.easyeffects.enable = true;
# Bluetooth controls
# services.mpris-proxy.enable = true;
# Owncloud
services.owncloud-client.enable = true;
services.owncloud-client.package = pkgs.owncloud-client.overrideAttrs (attrs: {
buildInputs = attrs.buildInputs ++ [ pkgs.qt6.qtwayland ];
});
# UDisks automounter
services.udiskie.enable = true;

View file

@ -1,38 +1,72 @@
{ config, pkgs, ... }:
{ config, pkgs, lib, ... }:
let
in
{
imports = [
./kakoune/kak.nix
./fish/fish.nix
./modules/programs/my-broot.nix
./modules/programs/my-waybar.nix
./modules/programs/my-sway
./modules/programs/my-niri.nix
./modules/programs/my-kitty
./modules/programs/openconnect-epfl.nix
./common-linux.nix
# PATH Overrides
({ config, lib, ... }: {
home.sessionPath = lib.mkBefore [
"${config.home.homeDirectory}/.bin/overrides"
];
})
];
# Let Home Manager install and manage itself.
programs.home-manager.enable = true;
# Temporarily disable the manuals
manual.html.enable = false;
# manual.manpage.enable = false;
# Enable the manual so we don't have to load it
manual.html.enable = true;
# Packages that are not in programs section
home.packages = with pkgs; [
# Build Tools
## C++
autoconf
automake
## SQL
flyway
## Go
go # to be configured later
## Rust
rust-analyzer
## JavaScript
yarn
## Nix
cachix
## Latex
tectonic
texlab
## Typst
typst
## Python
python3
## Scala
scala-cli
# Fonts
fantasque-sans-mono
## Get the nerd font symbols
(nerdfonts.override { fonts = [ "NerdFontsSymbolsOnly" ]; })
# CLI tools
fd
sd
ripgrep
fossil
openssh
tea # gitea CLI (gh-like)
glab # gitlab CLI
fx # JSON viewer
glow # Markdown viewer
nix-output-monitor # Nice nix output formatting
unstable.scala-next
## File Manager
nnn
## PDF Processors
poppler_utils
## htop replacement
@ -44,6 +78,13 @@
unzip
zstd
atool
## To do tunneling with cloudflare
pkgs.cloudflared
# Databases
postgresql
mariadb
];
home.sessionVariables = {
@ -75,7 +116,7 @@
eza = {
enable = true;
enableFishIntegration = true;
enableAliases = true;
};
fzf = {
@ -107,8 +148,7 @@
.envrc
.kakrc
''}";
safe.directory = "*";
merge.conflictstyle = "zdiff3";
merge.conflictstyle = "diff3";
};
};

View file

@ -1,3 +1,8 @@
{
allowUnfree = true;
packageOverrides = pkgs: {
nur = import (builtins.fetchTarball "https://github.com/nix-community/NUR/archive/master.tar.gz") {
inherit pkgs;
};
};
}

View file

@ -1,39 +1,6 @@
{ config, options, pkgs, lib, ... }:
with lib;
let
bootDesktop = pkgs.writeScript "boot-desktop" ''
#!/usr/bin/env fish
set -a PATH ${pkgs.gum}/bin
set -x GUM_CHOOSE_HEADER "Select the Desktop to boot into:"
set CHOICES
if which sway &>/dev/null
set -a CHOICES "sway"
end
if which niri-session &>/dev/null
set -a CHOICES "Niri"
end
if which startplasma-wayland &>/dev/null
set -a CHOICES "KDE Plasma"
end
set -a CHOICES "None: continue to shell"
switch (gum choose $CHOICES)
case "sway"
systemctl --user unset-environment NIXOS_OZONE_WL
exec sway
case "Niri"
exec niri-session
case "KDE Plasma"
exec ${pkgs.kdePackages.plasma-workspace}/libexec/plasma-dbus-run-session-if-needed startplasma-wayland
case '*'
exec fish -i
end
'';
in
{
imports = [
./tide
@ -58,7 +25,8 @@ in
functions = {
rebuild = {
body = ''
pls nixos-rebuild --flake ~/.config/nixpkgs -L --log-format internal-json -v $argv \
command sudo -v && \
command sudo nixos-rebuild --flake ~/.config/nixpkgs -L --log-format internal-json -v $argv \
&| ${pkgs.nix-output-monitor}/bin/nom --json
'';
wraps = "nixos-rebuild";
@ -66,18 +34,19 @@ in
# Simplify nix usage!
nx = {
body = ''
argparse -s 'h/help' 'impure' 'u/unstable' 'g/git' -- $argv
if set -q _flag_help || test (count $argv) -eq 0
echo "nx [--impure] [-u/--unstable/-g/--git] {package} [args...]"
return 1
set impure
if test $argv[1] = "--impure"
set impure "--impure"
set argv $argv[2..]
end
if test (count $argv) -gt 0
nix run $impure nixpkgs#$argv[1] -- $argv[2..]
else
set -q _flag_impure && set impure "--impure"
set nixpkgs "nixpkgs"
set -q _flag_unstable && set nixpkgs "nixpkgs-unstable"
set -q _flag_git && set nixpkgs "github:nixOS/nixpkgs/nixpkgs-unstable"
nix run $impure $nixpkgs"#"$argv[1] -- $argv[2..]
echo "nx [--impure] {package} [args...]"
return 1
end
'';
wraps = "nix run";
description = "Runs an app from the nixpkgs store.";
};
@ -85,35 +54,25 @@ in
description = "Spawns a shell from the given nixpkgs packages";
wraps = "nix shell";
body = ''
function help
echo "nsh [--impure] [--impure] [-u/--unstable/-g/--git] {package}* [-c command args...]"
set impure
if test $argv[1] = "--impure"
set impure "--impure"
set argv $argv[2..]
end
argparse -s 'h/help' 'impure' 'u/unstable' 'g/git' -- $argv
if set -q _flag_help || test (count $argv) -eq 0
help
return 0
end
set packages $argv
set minusc (contains -i -- "-c" $argv)
if test -n "$minusc"
if test $minusc -eq 1
help
if test (count $argv) -gt 0
set minusc (contains -i -- "-c" $argv)
if test -z $minusc
nix shell $impure nixpkgs#$argv -c fish
else if test $minusc -eq (count $argv)
echo "nsh [--impure] {packages} [-c command args...]"
return 1
else
nix shell $impure nixpkgs#$argv[..(math $minusc - 1)] $argv[$minusc..]
end
set packages $argv[..(math $minusc - 1)]
set argv $argv[(math $minusc + 1)..]
else
set argv "fish" "-i"
end
if test (count $packages) -eq 0
help
echo "nsh [--impure] {packages} [-c command args...]"
return 1
end
set -q _flag_impure && set impure "--impure"
set nixpkgs "nixpkgs"
set -q _flag_unstable && set nixpkgs "nixpkgs-unstable"
set -q _flag_git && set nixpkgs "github:nixOS/nixpkgs/nixpkgs-unstable"
nix shell $impure $nixpkgs"#"$packages --command $argv
'';
};
# Grep stuff
@ -131,30 +90,6 @@ in
};
echo-today = "date +%F";
newfile = "mkdir -p (dirname $argv[-1]) && touch $argv";
# pls
pls = {
wraps = "sudo";
body = ''
set -l cmd "`"(string join " " -- $argv)"`"
echo "I-It's not like I'm gonna run "$cmd" for you or a-anything! Baka >:C" >&2
# Send a notification on password prompt
if command sudo -vn 2>/dev/null
# nothing to do, user already authenticated
else
# throw a notification
set notif_id (kitten notify -P \
-p ${./haruka.png} \
-a "pls" \
-u critical \
"A-a command requires your p-password" \
(printf "I-I need your p-password to r-run the following c-command:\n\n%s" $cmd))
command sudo -v -p "P-password please: "
kitten notify -i $notif_id ""
end
command sudo $argv
'';
};
};
@ -183,15 +118,12 @@ in
if test -e /opt/homebrew/bin/brew
/opt/homebrew/bin/brew shellenv | source
end
# Override PATH
set --export --prepend PATH ~/.bin/overrides ~/.local/bin
'';
interactiveShellInit = ''
# Sway!
if status --is-login; and test -z $DISPLAY; and test (tty) = "/dev/tty1"
exec ${bootDesktop}
if status --is-login; and which sway >/dev/null; and test -z $DISPLAY; and test (tty) = "/dev/tty1"
read -P "Press enter to start sway..."; and exec sway
end
function fish_greeting
@ -290,8 +222,8 @@ in
target = ".config/fish/conf.d/change_cmd.fish";
};
"fish/pls.fish" = {
source = ./pls_extra.fish;
target = ".config/fish/conf.d/pls_extra.fish";
source = ./. + "/pls.fish";
target = ".config/fish/conf.d/pls.fish";
};
};
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

155
home/fish/pls.fish Normal file
View file

@ -0,0 +1,155 @@
alias sue="pls -e"
function pls
set -l cmd "`"(string join " " -- $argv)"`"
echo "I-It's not like I'm gonna run "$cmd" for you or a-anything! Baka >:C" >&2
# Send a notification on password prompt
if command sudo -vn 2>/dev/null
# nothing to do, user already authenticated
else
# throw a notification
# notify-send -t 3000 -u critical -i ~/Downloads/harukablush.jpg -h "STRING:command:"$cmd "A-a command requires your p-password" (printf "I-I need your p-password to r-run the following c-command: %s" $cmd)
end
command sudo $argv
end
function sudo
echo "Not polite enough."
end
function __fish_prepend_pls -d "Prepend 'pls ' to the beginning of the current commandline"
# If there is no commandline, insert the last item from history
# and *then* toggle
if not commandline | string length -q
commandline -r "$history[1]"
end
set -l cmd (commandline -po)
set -l cursor (commandline -C)
if test "$cmd[1]" = e
commandline -C 0
commandline -i "su"
commandline -C (math $cursor + 2)
else if test "$cmd[1]" = sue
commandline -r (string sub --start=3 (commandline -p))
commandline -C -- (math $cursor - 2)
else if test "$cmd[1]" != pls
commandline -C 0
commandline -i "pls "
commandline -C (math $cursor + 4)
else
commandline -r (string sub --start=5 (commandline -p))
commandline -C -- (math $cursor - 4)
end
end
bind --preset -e -M insert \es
bind -M insert \es __fish_prepend_pls
function __fish_man_page
# Get all commandline tokens not starting with "-"
set -l args (commandline -po | string match -rv '^-')
# If commandline is empty, exit.
if not set -q args[1]
printf \a
return
end
#Skip `pls` and display then manpage of following command
while set -q args[2]
and string match -qr -- '^(pls|.*=.*)$' $args[1]
set -e args[1]
end
# If there are at least two tokens not starting with "-", the second one might be a subcommand.
# Try "man first-second" and fall back to "man first" if that doesn't work out.
set -l maincmd (basename $args[1])
if set -q args[2]
# HACK: If stderr is not attached to a terminal `less` (the default pager)
# wouldn't use the alternate screen.
# But since we don't know what pager it is, and because `man` is totally underspecified,
# the best we can do is to *try* the man page, and assume that `man` will return false if it fails.
# See #7863.
if man "$maincmd-$args[2]" &>/dev/null
man "$maincmd-$args[2]"
else if man "$maincmd" &>/dev/null
man "$maincmd"
else
printf \a
end
else
if man "$maincmd" &>/dev/null
man "$maincmd"
else
printf \a
end
end
commandline -f repaint
end
#
# Completion for pls
#
function __fish_pls_print_remaining_args
set -l tokens (commandline -opc) (commandline -ct)
set -e tokens[1]
# These are all the options mentioned in the man page for Todd Miller's "pls.ws" pls (in that order).
# If any other implementation has different options, this should be harmless, since they shouldn't be used anyway.
set -l opts A/askpass b/background C/close-from= E/preserve-env='?'
# Note that "-h" is both "--host" (which takes an option) and "--help" (which doesn't).
# But `-h` as `--help` only counts when it's the only argument (`pls -h`),
# so any argument completion after that should take it as "--host".
set -a opts e/edit g/group= H/set-home h/host= 1-help
set -a opts i/login K/remove-timestamp k/reset-timestamp l/list n/non-interactive
set -a opts P/preserve-groups p/prompt= S/stdin s/shell U/other-user=
set -a opts u/user= T/command-timeout= V/version v/validate
argparse -s $opts -- $tokens 2>/dev/null
# The remaining argv is the subcommand with all its options, which is what
# we want.
if test -n "$argv"
and not string match -qr '^-' $argv[1]
string join0 -- $argv
return 0
else
return 1
end
end
function __fish_pls_no_subcommand
not __fish_pls_print_remaining_args >/dev/null
end
function __fish_complete_pls_subcommand
set -l args (__fish_pls_print_remaining_args | string split0)
set -lx -a PATH /usr/local/sbin /sbin /usr/sbin
__fish_complete_subcommand --commandline $args
end
# All these options should be valid for GNU and OSX pls
complete -c pls -n __fish_no_arguments -s h -d "Display help and exit"
complete -c pls -n __fish_no_arguments -s V -d "Display version information and exit"
complete -c pls -n __fish_pls_no_subcommand -s A -d "Ask for password via the askpass or \$SSH_ASKPASS program"
complete -c pls -n __fish_pls_no_subcommand -s C -d "Close all file descriptors greater or equal to the given number" -xa "0 1 2 255"
complete -c pls -n __fish_pls_no_subcommand -s E -d "Preserve environment"
complete -c pls -n __fish_pls_no_subcommand -s H -d "Set home"
complete -c pls -n __fish_pls_no_subcommand -s K -d "Remove the credential timestamp entirely"
complete -c pls -n __fish_pls_no_subcommand -s P -d "Preserve group vector"
complete -c pls -n __fish_pls_no_subcommand -s S -d "Read password from stdin"
complete -c pls -n __fish_pls_no_subcommand -s b -d "Run command in the background"
complete -c pls -n __fish_pls_no_subcommand -s e -rF -d Edit
complete -c pls -n __fish_pls_no_subcommand -s g -a "(__fish_complete_groups)" -x -d "Run command as group"
complete -c pls -n __fish_pls_no_subcommand -s i -d "Run a login shell"
complete -c pls -n __fish_pls_no_subcommand -s k -d "Reset or ignore the credential timestamp"
complete -c pls -n __fish_pls_no_subcommand -s l -d "List the allowed and forbidden commands for the given user"
complete -c pls -n __fish_pls_no_subcommand -s n -d "Do not prompt for a password - if one is needed, fail"
complete -c pls -n __fish_pls_no_subcommand -s p -d "Specify a custom password prompt"
complete -c pls -n __fish_pls_no_subcommand -s s -d "Run the given command in a shell"
complete -c pls -n __fish_pls_no_subcommand -s u -a "(__fish_complete_users)" -x -d "Run command as user"
complete -c pls -n __fish_pls_no_subcommand -s v -n __fish_no_arguments -d "Validate the credentials, extending timeout"
# Complete the command we are executed under pls
complete -c pls -x -n 'not __fish_seen_argument -s e' -a "(__fish_complete_pls_subcommand)"

View file

@ -1,47 +0,0 @@
alias sue="pls -e"
function sudo
echo "Not polite enough."
end
bind --preset -M visual \es 'fish_commandline_prepend pls'
bind -M insert \es 'fish_commandline_prepend pls'
function __fish_man_page
# Get all commandline tokens not starting with "-", up to and including the cursor's
set -l args (string match -rv '^-|^$' -- (commandline -cpx && commandline -t))
# If commandline is empty, exit.
if not set -q args[1]
printf \a
return
end
# Skip leading commands and display the manpage of following command
while set -q args[2]
and string match -qr -- '^(and|begin|builtin|caffeinate|command|doas|entr|env|exec|if|mosh|nice|not|or|pipenv|prime-run|setsid|sudo|pls|systemd-nspawn|time|watch|while|xargs|.*=.*)$' $args[1]
set -e args[1]
end
# If there are at least two tokens not starting with "-", the second one might be a subcommand.
# Try "man first-second" and fall back to "man first" if that doesn't work out.
set -l maincmd (path basename $args[1])
# HACK: If stderr is not attached to a terminal `less` (the default pager)
# wouldn't use the alternate screen.
# But since we don't know what pager it is, and because `man` is totally underspecified,
# the best we can do is to *try* the man page, and assume that `man` will return false if it fails.
# See #7863.
if set -q args[2]
and not string match -q -- '*/*' $args[2]
and man "$maincmd-$args[2]" &>/dev/null
man "$maincmd-$args[2]"
else
if man "$maincmd" &>/dev/null
man "$maincmd"
else
printf \a
end
end
commandline -f repaint
end

View file

@ -37,24 +37,36 @@
# Enable X11 configuration
linux.graphical.type = "wayland";
linux.graphical.wallpaper = ./images/pixiv_18776904.png;
linux.graphical.defaults.webBrowser.package = pkgs.zen-browser-bin;
linux.graphical.defaults.webBrowser.desktopFile = "zen.desktop";
programs.my-niri.enable = true;
programs.my-sway.enable = true;
programs.my-sway.fontSize = 15.0;
programs.my-sway.enableLaptop = true;
programs.my-waybar.enableMpd = true;
programs.my-sway.enableLaptopBars = false;
programs.my-sway.enableMpd = true;
# Keyboard options
wayland.windowManager.sway.config.input."type:keyboard".xkb_layout = "jp";
wayland.windowManager.sway.config.input."type:pointer".accel_profile = "flat";
# 144hz adaptive refresh ON!
wayland.windowManager.sway.config.output =
let
scale = 1.5;
top_x = builtins.ceil (3840 / scale);
top_y = 0;
in
with config.common.monitors; {
${home_4k.name} = {
scale = toString scale;
position = "0 0";
};
${home_1440.name} = {
position = "${toString top_x} ${toString top_y}";
};
};
nki.programs.kitty.enable = true;
nki.programs.kitty.fontSize = 14;
programs.my-waybar.makeBars = with config.common.monitors; barWith: [
programs.my-sway.waybar.makeBars = with config.common.monitors; barWith: [
# For primary
(barWith { extraSettings = { output = [ home_4k.meta.connection ]; }; })
(barWith { extraSettings = { output = [ home_4k.name ]; }; })
# For secondary, hide mpd
(barWith { showMedia = false; showConnectivity = false; extraSettings = { output = [ home_1440.meta.connection ]; }; })
(barWith { showMedia = false; showConnectivity = false; extraSettings = { output = [ home_1440.name ]; }; })
];
# Yellow light!

View file

@ -1,79 +0,0 @@
# Catppuccin theme for Kakoune
declare-option str rosewater "rgb:dc8a78"
declare-option str flamingo "rgb:dd7878"
declare-option str pink "rgb:ea76cb"
declare-option str mauve "rgb:8839ef"
declare-option str red "rgb:d20f39"
declare-option str maroon "rgb:e64553"
declare-option str peach "rgb:fe640b"
declare-option str yellow "rgb:df8e1d"
declare-option str green "rgb:40a02b"
declare-option str teal "rgb:179299"
declare-option str sky "rgb:04a5e5"
declare-option str sapphire "rgb:209fb5"
declare-option str blue "rgb:1e66f5"
declare-option str lavender "rgb:7287fd"
declare-option str text "rgb:4c4f69"
declare-option str subtext1 "rgb:5c5f77"
declare-option str subtext0 "rgb:6c6f85"
declare-option str overlay2 "rgb:7c7f93"
declare-option str overlay1 "rgb:8c8fa1"
declare-option str overlay0 "rgb:9ca0b0"
declare-option str surface2 "rgb:acb0be"
declare-option str surface1 "rgb:bcc0cc"
declare-option str surface0 "rgb:ccd0da"
declare-option str base "rgb:eff1f5"
declare-option str mantle "rgb:e6e9ef"
declare-option str crust "rgb:dce0e8"
set-face global title "%opt{text}+b"
set-face global header "%opt{subtext0}+b"
set-face global bold "%opt{maroon}+b"
set-face global italic "%opt{maroon}+i"
set-face global mono "%opt{green}"
set-face global block "%opt{sapphire}"
set-face global link "%opt{blue}"
set-face global bullet "%opt{peach}"
set-face global list "%opt{peach}"
set-face global Default "%opt{text},%opt{base}"
set-face global PrimarySelection "%opt{text},%opt{surface2}"
set-face global SecondarySelection "%opt{text},%opt{surface2}"
set-face global PrimaryCursor "%opt{crust},%opt{rosewater}"
set-face global SecondaryCursor "%opt{text},%opt{overlay0}"
set-face global PrimaryCursorEol "%opt{surface2},%opt{lavender}"
set-face global SecondaryCursorEol "%opt{surface2},%opt{overlay1}"
set-face global LineNumbers "%opt{overlay1},%opt{base}"
set-face global LineNumberCursor "%opt{rosewater},%opt{surface2}+b"
set-face global LineNumbersWrapped "%opt{rosewater},%opt{surface2}+i"
set-face global MenuForeground "%opt{text},%opt{surface1}+b"
set-face global MenuBackground "%opt{text},%opt{surface0}"
set-face global MenuInfo "%opt{crust},%opt{teal}"
set-face global Information "%opt{crust},%opt{teal}"
set-face global Error "%opt{crust},%opt{red}"
set-face global StatusLine "%opt{text},%opt{mantle}"
set-face global StatusLineMode "%opt{crust},%opt{yellow}"
set-face global StatusLineInfo "%opt{crust},%opt{teal}"
set-face global StatusLineValue "%opt{crust},%opt{yellow}"
set-face global StatusCursor "%opt{crust},%opt{rosewater}"
set-face global Prompt "%opt{teal},%opt{base}+b"
set-face global MatchingChar "%opt{maroon},%opt{base}"
set-face global Whitespace "%opt{overlay1},%opt{base}+f"
set-face global WrapMarker "Whitespace"
set-face global BufferPadding "%opt{base},%opt{base}"
set-face global value "%opt{peach}"
set-face global type "%opt{blue}"
set-face global variable "%opt{text}"
set-face global module "%opt{maroon}"
set-face global function "%opt{blue}"
set-face global string "%opt{green}"
set-face global keyword "%opt{mauve}"
set-face global operator "%opt{sky}"
set-face global attribute "%opt{green}"
set-face global comment "%opt{overlay0}"
set-face global documentation "comment"
set-face global meta "%opt{yellow}"
set-face global builtin "%opt{red}"

View file

@ -1,67 +1,29 @@
{ pkgs, lib, ... }:
{ config, pkgs, lib, ... }:
let
kak-lsp = pkgs.libs.crane.buildPackage {
src = pkgs.sources.kak-lsp;
buildInputs = (with pkgs;
lib.optionals stdenv.isDarwin (with darwin.apple_sdk.frameworks; [ Security SystemConfiguration CoreServices ])
) ++ (with pkgs; [ libiconv ]);
};
kak-lsp-frontend = { pkgs, lib, ... }:
let
langserver = name: {
name = "vscode-${name}-language-server";
value = {
args = [ "--stdio" ];
command = "vscode-${name}-language-server";
filetypes = [ name ];
roots = [ "package.json" ".git" ];
};
package = pkgs.nodePackages.vscode-langservers-extracted;
};
tailwind = {
command = "tailwindcss-language-server";
args = [ "--stdio" ];
filetypes = [ "html" "css" "javascript" "typescript" "templ" ];
roots = [ "tailwind.config.{js,cjs,mjs,ts}" "package.json" ".git" ];
settings_section = "tailwindCSS";
settings.tailwindCSS = {
validate = "warning";
userLanguages.templ = "html";
};
package = pkgs.tailwindcss-language-server;
command = "${pkgs.nodePackages.vscode-langservers-extracted}/bin/vscode-${name}-language-server";
filetypes = [ name ];
roots = [ "package.json" ".git" ];
};
templModule = { pkgs, lib, ... }: {
programs.kak-lsp.languageServers."vscode-html-language-server".filetypes = [ "templ" ];
programs.kak-lsp.languageServers."tailwindcss-language-server".filetypes = [ "templ" ];
programs.kak-lsp.languageServers.templ = {
command = "templ";
args = [ "lsp" ];
filetypes = [ "templ" ];
roots = [ "go.mod" ".git" ];
package = pkgs.unstable.templ;
};
};
in
{
imports = [ templModule ];
programs.kak-lsp.languageServers = (builtins.listToAttrs (map langserver [ "html" "css" "json" ])) // {
tailwindcss-language-server = tailwind;
};
programs.kak-lsp.languages = lib.attrsets.genAttrs [ "html" "css" "json" ] langserver;
};
ltexLsp = { pkgs, lib, ... }: {
programs.kak-lsp.languageServers.ltex-ls = {
command = "ltex-ls";
args = [ "--log-file=/tmp" ];
filetypes = [ "latex" "typst" ];
roots = [ "main.tex" "main.typ" ".git" ];
package = pkgs.ltex-ls;
};
};
in
{
imports = [ ../modules/programs/my-kakoune ./kaktex.nix kak-lsp-frontend ltexLsp ];
imports = [ ../modules/programs/my-kakoune ./kaktex.nix kak-lsp-frontend ];
home.packages = with pkgs; [
# ctags for peneira
@ -76,6 +38,7 @@ in
programs.my-kakoune.enable = true;
programs.my-kakoune.enable-fish-session = true;
programs.kak-lsp.enable = true;
programs.kak-lsp.package = kak-lsp;
programs.kak-lsp.semanticTokens.additionalFaces = [
# Typst
{ face = "header"; token = "heading"; }
@ -91,20 +54,13 @@ in
{ face = "ts_markup_italic"; token = "text"; modifiers = [ "emph" ]; }
];
programs.kak-lsp.languageServers.elixir-ls = {
args = [ ];
command = "elixir-ls";
filetypes = [ "elixir" ];
roots = [ "mix.exs" ];
};
programs.kak-lsp.languageServers.typescript-language-server = {
programs.kak-lsp.languages.typescript = {
args = [ "--stdio" ];
command = "typescript-language-server";
filetypes = [ "typescript" "javascript" ];
filetypes = [ "typescript" ];
roots = [ "package.json" ];
package = pkgs.nodePackages.typescript-language-server;
};
programs.kak-lsp.languageServers.fsautocomplete = {
programs.kak-lsp.languages.fsharp = {
args = [ "--adaptive-lsp-server-enabled" "--project-graph-enabled" "--source-text-factory" "RoslynSourceText" ];
command = "fsautocomplete";
filetypes = [ "fsharp" ];
@ -114,25 +70,19 @@ in
AutomaticWorkspaceInit = true;
};
};
programs.kak-lsp.languageServers.metals = {
programs.kak-lsp.languages.scala = {
command = "metals";
filetypes = [ "scala" ];
roots = [ "build.sbt" "build.sc" "build.mill" ];
roots = [ "build.sbt" "build.sc" ];
settings_section = "metals";
settings.metals = {
enableSemanticHighlighting = true;
showInferredType = true;
decorationProvider = true;
inlineDecorationProvider = true;
# From kakoune-lsp's own options
icons = "unicode";
isHttpEnabled = true;
statusBarProvider = "log-message";
compilerOptions = { overrideDefFormat = "unicode"; };
};
package = pkgs.metals;
};
programs.kak-lsp.languageServers.texlab = {
programs.kak-lsp.languages.latex = {
command = "texlab";
filetypes = [ "latex" ];
roots = [ "main.tex" "all.tex" ".git" ];
@ -151,33 +101,23 @@ in
} else
{
executable = "${pkgs.zathura}/bin/zathura";
args = [ "--synctex-forward" "%l:1:%f" "%p" "-x" "${./kaktex} jump %%{input} %%{line} %%{column}" ];
args = [ "--synctex-forward" "%l:1:%f" "%p" "-x" "/home/nki/.bin/kaktex jump %{input} %{line}" ];
});
};
package = pkgs.texlab;
};
programs.kak-lsp.languageServers.tinymist = {
command = "tinymist";
programs.kak-lsp.languages.typst = {
command = "typst-lsp";
filetypes = [ "typst" ];
roots = [ "main.typ" ".git" ];
settings_section = "tinymist";
settings.tinymist = {
exportPdf = "onSave";
formatterMode = "typstfmt";
settings_section = "typst-lsp";
settings.typst-lsp = {
experimentalFormatterMode = "on";
};
};
programs.kak-lsp.languageServers.marksman = {
programs.kak-lsp.languages.markdown = {
command = "marksman";
filetypes = [ "markdown" ];
roots = [ ".marksman.toml" ".git" ];
package = pkgs.marksman;
};
programs.kak-lsp.languageServers.rust-analyzer = {
args = [ ];
command = "rust-analyzer";
filetypes = [ "rust" ];
roots = [ "Cargo.toml" ];
package = pkgs.rust-analyzer;
};
programs.my-kakoune.tree-sitter.extraAliases = {
@ -196,111 +136,66 @@ in
conditional = "keyword_conditional";
include = "keyword_control_import";
};
programs.my-kakoune.tree-sitter.languages =
let
tree-sitter-go = pkgs.fetchFromGitHub {
owner = "tree-sitter";
repo = "tree-sitter-go";
rev = "v0.20.0";
hash = "sha256-G7d8CHCyKDAb9j6ijRfHk/HlgPqSI+uvkuRIRRvjkHI=";
programs.my-kakoune.tree-sitter.languages = {
scala =
let
src = pkgs.fetchFromGitHub {
owner = "tree-sitter";
repo = "tree-sitter-scala";
rev = "70afdd5632d57dd63a960972ab25945e353a52f6";
hash =
if pkgs.stdenv.isDarwin
then lib.fakeHash
else "sha256-Q8KSI8H7+d/sUdSlcNAiOPn2THKk7SFqC3U7rLqSqtE=";
leaveDotGit = true;
};
in
{
grammar.src = src;
queries.src = src;
queries.path = "queries/scala";
};
in
{
scala =
let
src = pkgs.fetchFromGitHub {
owner = "tree-sitter";
repo = "tree-sitter-scala";
rev = "70afdd5632d57dd63a960972ab25945e353a52f6";
hash = "sha256-bi0Lqo/Zs2Uaz1efuKAARpEDg5Hm59oUe7eSXgL1Wow=";
};
in
{
grammar.src = src;
queries.src = src;
queries.path = "queries/scala";
haskell =
let
src = pkgs.fetchFromGitHub {
owner = "tree-sitter";
repo = "tree-sitter-haskell";
rev = "ba0bfb0e5d8e9e31c160d287878c6f26add3ec08";
sha256 =
if pkgs.stdenv.isDarwin
then "sha256-lW3E4gSZV/m2RfofUqeiCu8KDz06YEvXbYKs8smXFi4="
else "sha256-H5Z4vPTZCFxDCWguOB8oVSGPJyQonrD8FWAQZTYPG2U=";
leaveDotGit = true;
};
haskell =
let
src = pkgs.fetchFromGitHub {
owner = "tree-sitter";
repo = "tree-sitter-haskell";
rev = "ba0bfb0e5d8e9e31c160d287878c6f26add3ec08";
hash = "sha256-ZSOF0CLOn82GwU3xgvFefmh/AD2j5zz8I0t5YPwfan0=";
};
in
{
grammar.src = src;
grammar.compile.args = [ "-c" "-fpic" "../parser.c" "../scanner.c" "../unicode.h" "-I" ".." ];
queries.src = src;
queries.path = "queries";
};
yaml = {
grammar.src = pkgs.fetchFromGitHub {
owner = "ikatyang";
repo = "tree-sitter-yaml";
rev = "0e36bed171768908f331ff7dff9d956bae016efb";
hash = "sha256-bpiT3FraOZhJaoiFWAoVJX1O+plnIi8aXOW2LwyU23M=";
};
grammar.compile.args = [ "-c" "-fpic" "../scanner.cc" "../parser.c" "-I" ".." ];
grammar.link.args = [ "-shared" "-fpic" "scanner.o" "parser.o" ];
grammar.link.flags = [ "-O3" "-lstdc++" ];
queries.src = pkgs.fetchFromGitHub {
owner = "helix-editor";
repo = "helix";
rev = "dbd248fdfa680373d94fbc10094a160aafa0f7a7";
hash = "sha256-wk8qVUDFXhAOi1Ibc6iBMzDCXb6t+YiWZcTd0IJybqc=";
};
queries.path = "runtime/queries/yaml";
};
templ =
let
src = pkgs.fetchFromGitHub {
owner = "vrischmann";
repo = "tree-sitter-templ";
rev = "044ad200092170727650fa6d368df66a8da98f9d";
hash = "sha256-hJuB3h5pp+LLfP0/7bAYH0uLVo+OQk5jpzJb3J9BNkY=";
};
in
{
grammar.src = src;
queries.src = pkgs.runCommandLocal "templ-tree-sitter-queries" { } ''
mkdir -p $out/queries
# copy most stuff from tree-sitter-templ
install -m644 ${src}/queries/templ/* $out/queries
# override inherited files
cat ${tree-sitter-go}/queries/highlights.scm ${src}/queries/templ/highlights.scm > $out/queries/highlights.scm
'';
queries.path = "queries";
};
go = {
grammar.src = tree-sitter-go;
grammar.compile.args = [ "-c" "-fpic" "../parser.c" "-I" ".." ];
grammar.link.args = [ "-shared" "-fpic" "parser.o" ];
queries.src = tree-sitter-go;
in
{
grammar.src = src;
grammar.compile.args = [ "-c" "-fpic" "../parser.c" "../scanner.c" "../unicode.h" "-I" ".." ];
queries.src = src;
queries.path = "queries";
};
yaml = {
grammar.src = pkgs.fetchFromGitHub {
owner = "ikatyang";
repo = "tree-sitter-yaml";
rev = "0e36bed171768908f331ff7dff9d956bae016efb";
hash = "sha256-rN/a8dYffDQNuvnhNp/nfu0AzhYrR1ESACQbQWb/n5w=";
leaveDotGit = true;
};
grammar.compile.args = [ "-c" "-fpic" "../scanner.cc" "../parser.c" "-I" ".." ];
grammar.link.args = [ "-shared" "-fpic" "scanner.o" "parser.o" ];
grammar.link.flags = [ "-O3" "-lstdc++" ];
hylo =
let
src = pkgs.fetchFromGitHub {
owner = "natsukagami";
repo = "tree-sitter-hylo";
rev = "494cbdff0d13cbc67348316af2efa0286dbddf6f";
hash = "sha256-R5UeoglCTl0do3VDJ/liCTeqbxU9slvmVKNRA/el2VY=";
};
in
{
grammar.src = src;
grammar.compile.args = [ "-c" "-fpic" "../parser.c" "-I" ".." ];
grammar.link.args = [ "-shared" "-fpic" "parser.o" ];
queries.src = src;
queries.path = "queries";
};
queries.src = pkgs.fetchFromGitHub {
owner = "helix-editor";
repo = "helix";
rev = "dbd248fdfa680373d94fbc10094a160aafa0f7a7";
hash = "sha256-dv/T8ROXmwEdjM71gza1RzF2HoINA7Zl2jmz63kCZyQ=";
leaveDotGit = true;
};
queries.path = "runtime/queries/yaml";
};
};
programs.my-kakoune.package = pkgs.kakoune;
programs.my-kakoune.rc =
@ -314,11 +209,11 @@ in
'';
programs.my-kakoune.extraFaces = {
Default = "%opt{text},%opt{base}";
BufferPadding = "%opt{base},%opt{base}";
MenuForeground = "%opt{blue},white+bF";
MenuBackground = "%opt{sky},white+F";
Information = "%opt{sky},white";
Default = "%opt{white},%opt{background}";
BufferPadding = "%opt{background},%opt{background}";
MenuForeground = "blue,white+bF";
MenuBackground = "bright-blue,white+F";
Information = "bright-blue,white";
# Markdown help color scheme
InfoDefault = "Information";
InfoBlock = "@block";
@ -510,7 +405,10 @@ in
}
];
programs.my-kakoune.themes = {
catppuccin-latte = ./catppuccin-latte.kak;
catppuccin-latte = builtins.fetchurl {
url = "https://raw.githubusercontent.com/catppuccin/kakoune/f6d43770609433c45046632f1bb68d1395305dbb/colors/catppuccin_latte.kak";
sha256 = "sha256:0ycvxs8hmsvd0zrpxiby16wzmapvmz6p34b6j343pc1girw6fi4i";
};
};
}

View file

@ -1,27 +1,20 @@
# Enable kak-tree-sitter
eval %sh{test -z "$WE_STARTED_KAK" && kak-tree-sitter --kakoune -d --server --init $kak_session}
eval %sh{ kak-tree-sitter --kakoune -d --server --with-text-objects }
map global normal <c-t> ": enter-user-mode tree-sitter<ret>"
# ## Set some color overrides
# set global kts_yellow "rgb:e2b75e"
# set global kts_teal "rgb:008080"
# set global kts_mauve "rgb:c264ff"
# set global kts_sky "rgb:6aa622"
## Set some color overrides
set global kts_yellow "rgb:e2b75e"
set global kts_teal "rgb:008080"
set global kts_mauve "rgb:c264ff"
set global kts_sky "rgb:6aa622"
# Color scheme
colorscheme catppuccin-latte
set global base "default"
# Set indentation guides
add-highlighter global/indent-guides show-whitespaces -tab " " -spc " " -lf " " -nbsp " "
set-face global Whitespace default,default
set-face global WhitespaceIndent +d@comment
set global background default
# Assistant
set global ui_options terminal_assistant=cat
# Enable line numbers
hook global WinCreate .* %{
addhl window/number-lines number-lines
}
addhl global/ number-lines
set global grepcmd "rg --line-number --no-column --no-heading --color=never "
@ -41,9 +34,9 @@ map global goto f -docstring "current grep-jump match" '<esc>: grep-jump<ret>'
hook global RegisterModified '"' %{ nop %sh{
printf "%s" "$kak_main_reg_dquote" | pbcopy >/dev/null 2>/dev/null &
}}
map global user P -docstring "Paste before cursor from clipboard" '! pbpaste -n | cat<ret>'
map global user p -docstring "Paste after cursor from clipboard" '<a-!> pbpaste -n | cat<ret>'
map global user R -docstring "Replace selection with text from clipboard" '<a-d>! pbpaste -n | cat<ret>'
map global user P -docstring "Paste before cursor from clipboard" '! pbpaste<ret>'
map global user p -docstring "Paste after cursor from clipboard" '<a-!> pbpaste<ret>'
map global user R -docstring "Replace selection with text from clipboard" '<a-d>! pbpaste<ret>'
define-command -params 0 -docstring "Copy line down" copyline %{
execute-keys -draft 'xy'%val{count}'P'
}
@ -61,13 +54,11 @@ map global normal D ": delete-current-brackets<ret>"
# Tab sizes
hook global InsertChar \t %{ exec -draft -itersel h@ }
set global tabstop 2
set global indentwidth 2
# Language-specific tabstop with override
hook global WinSetOption filetype=(rust) %{
set window tabstop 4
set window indentwidth 4
set global tabstop 4
set global indentwidth 4
hook global WinSetOption filetype=(c|cpp|haskell|nix|yaml) %{
set global tabstop 2
set global indentwidth 2
}
# Ctrl + a in insert mode = esc
@ -93,9 +84,8 @@ hook global InsertCompletionHide .* %{
# Enable LSP
try %{
eval %sh{test -z "$WE_STARTED_KAK" && kak-lsp --kakoune -s $kak_session}
set-option global lsp_cmd "kak-lsp -s %val{session}"
}
hook global WinSetOption filetype=(racket|rust|python|go|javascript|typescript|c|cpp|tex|latex|fsharp|ocaml|haskell|nix|scala|typst|html|css|json|markdown|templ|elixir) %{
hook global WinSetOption filetype=(racket|rust|python|go|javascript|typescript|c|cpp|tex|latex|fsharp|ocaml|haskell|nix|scala|typst|html|css|json|markdown) %{
lsp-enable-window
map window lsp N -docstring "Display the next message request" ": lsp-show-message-request-next<ret>"
map window normal <c-l> ": enter-user-mode lsp<ret>"
@ -103,16 +93,9 @@ hook global WinSetOption filetype=(racket|rust|python|go|javascript|typescript|c
map window normal <c-s-h> ": lsp-hover-buffer<ret>"
# lsp-auto-hover-insert-mode-enable
set window lsp_hover_anchor true
map global insert <tab> '<a-;>:try lsp-snippets-select-next-placeholders catch %{ execute-keys -with-hooks <lt>tab> }<ret>' -docstring 'Select next snippet placeholder'
map global object a '<a-semicolon>lsp-object<ret>' -docstring 'LSP any symbol'
map global object <a-a> '<a-semicolon>lsp-object<ret>' -docstring 'LSP any symbol'
map global object f '<a-semicolon>lsp-object Function Method<ret>' -docstring 'LSP function or method'
map global object t '<a-semicolon>lsp-object Class Interface Struct<ret>' -docstring 'LSP class interface or struct'
map global object d '<a-semicolon>lsp-diagnostic-object --include-warnings<ret>' -docstring 'LSP errors and warnings'
map global object D '<a-semicolon>lsp-diagnostic-object<ret>' -docstring 'LSP errors'
}
hook global WinSetOption filetype=(racket|rust|python|go|javascript|typescript|c|cpp|tex|latex|haskell|nix|fsharp|templ) %{
hook global WinSetOption filetype=(racket|rust|python|go|javascript|typescript|c|cpp|tex|latex|haskell|nix|fsharp) %{
# Format the document if possible
hook window BufWritePre .* %{ lsp-formatting-sync }
}
@ -158,6 +141,9 @@ hook global WinSetOption filetype=(rust) %{
hook global WinSetOption filetype=(scala) %{
# Format the document if possible
hook -group scala-fmt window BufWritePre .* %{ lsp-formatting-sync }
set window tabstop 2
set window indentwidth 2
}
hook global WinSetOption filetype=(typst) %{
@ -206,7 +192,7 @@ hook global BufCreate .*[.]md %{
add-highlighter buffer/ wrap
}
hook global BufCreate .*[.](sc|sbt|mill) %{
hook global BufCreate .*[.](sc|sbt) %{
set-option buffer filetype scala
}
@ -215,16 +201,6 @@ hook global BufCreate .*[.]typ %{
add-highlighter buffer/ wrap
}
hook global BufCreate .*[.]templ %{
set-option buffer filetype templ
set-option buffer comment_line "//"
}
hook global BufCreate .*[.]hylo %{
set-option buffer filetype hylo
set-option buffer comment_line "//"
}
hook global BufOpenFile .* %{
modeline-parse
}

View file

@ -3,11 +3,11 @@
function usage
echo "Usage: "
echo " kaktex set [client] [session]"
echo " kaktex jump [file] [line] [column]"
echo " kaktex jump [file] [line]"
exit 1
end
if test (count $argv) -lt 3
if test (count $argv) -ne 3
usage
end
@ -19,7 +19,7 @@ switch $argv[1]
echo "
evaluate-commands -client $_kaktex_client %{
evaluate-commands -try-client $_kaktex_client %{
edit -existing -- $argv[2] $(math $argv[3] + 1) $(math $argv[4] + 1)
edit -- $argv[2] $argv[3]
}
}
" | kak -p $_kaktex_session

View file

@ -1,12 +1,22 @@
{ config, pkgs, lib, ... }:
let
kaktexScript = ./kaktex;
in
{
# Create kak-tex executable
home.file.kaktex = {
source = kaktexScript;
executable = true;
target = ".bin/kaktex";
};
# Source kaktex whenever we have a tex file
programs.my-kakoune.rc = ''
hook global WinSetOption filetype=(tex|latex) %{
hook window WinDisplay '.*' %{
eval %sh{
${./kaktex} set $kak_client $kak_session
${kaktexScript} set $kak_client $kak_session
}
}
}

View file

@ -50,8 +50,8 @@ in
# Enable sway
programs.my-sway.enable = true;
programs.my-sway.fontSize = 14.0;
programs.my-sway.enableLaptop = true;
programs.my-waybar.enableMpd = false;
programs.my-sway.enableLaptopBars = true;
programs.my-sway.enableMpd = false;
programs.my-sway.discord = "${discord}/bin/armcord";
# Keyboard options
wayland.windowManager.sway.config.input."type:keyboard".xkb_layout = "jp";
@ -97,7 +97,7 @@ in
home.file.".gnupg/gpg-agent.conf" = {
text = ''
pinentry-program ${pkgs.pinentry-gnome3}/bin/pinentry-gnome3
pinentry-program ${pkgs.pinentry-gnome}/bin/pinentry-gnome3
'';
onChange = ''
echo "Reloading gpg-agent"
@ -106,6 +106,10 @@ in
};
# Autostart
xdg.configFile."autostart/polkit.desktop".text = ''
${builtins.readFile "${pkgs.pantheon.pantheon-agent-polkit}/etc/xdg/autostart/io.elementary.desktop.agent-polkit.desktop"}
OnlyShowIn=sway;
'';
xdg.configFile."autostart/input-remapper-autoload.desktop".source =
"${pkgs.input-remapper}/share/applications/input-remapper-autoload.desktop";

View file

@ -3,38 +3,9 @@ with lib;
let
cfg = config.linux.graphical;
thunderbird = pkgs.thunderbird-128;
vscode = with pkgs; if stdenv.isAarch64 then unstable.vscode else unstable.vscode-fhs;
wifi-indicator = pkgs.writeScriptBin "wifi-indicator" ''
#!/usr/bin/env fish
set wifi_output (${lib.getExe pkgs.iw} wlan0 link | rg "SSID: (.+)" --replace '🛜 $1' | string trim)
if test -z $wifi_output
echo " not connected"
else
echo $wifi_output
end
'';
mkPackageWithDesktopOption = opts: mkOption ({
type = types.submodule {
options = {
package = mkOption {
type = types.package;
description = "The package for " + description;
};
desktopFile = mkOption {
type = types.nullOr types.str;
default = null;
description = "The desktop file name for " + description + ", defaults to [packagename].desktop";
};
};
};
} // opts);
desktopFileOf = cfg: if cfg.desktopFile == null then "${cfg.package}/share/applications/${cfg.package.pname}.desktop" else cfg.desktopFile;
alwaysStartup = with pkgs; [ ];
in
{
imports = [ ./x11.nix ./wayland.nix ./alacritty.nix ];
@ -52,24 +23,18 @@ in
startup = mkOption {
type = types.listOf types.package;
description = "List of packages to include in ~/.config/autostart";
default = with pkgs; [
cfg.defaults.webBrowser.package
thunderbird
vesktop
];
default = [ ];
};
defaults = {
webBrowser = mkPackageWithDesktopOption { description = "default web browser"; };
terminal = mkPackageWithDesktopOption { description = "default terminal"; default.package = pkgs.kitty; };
defaults.webBrowser = mkOption {
type = types.str;
default = "firefox.desktop";
description = "Desktop file of the default web browser";
};
};
config = mkIf (cfg.type != null) {
# Packages
home.packages = (with pkgs; [
cfg.defaults.webBrowser.package
cfg.defaults.terminal.package
## GUI stuff
evince # PDF reader
gparted
@ -77,44 +42,54 @@ in
feh # For images?
deluge # Torrent client
pavucontrol # PulseAudio control panel
firefox
librewolf
cinnamon.nemo # File manager
thunderbird # Email
sublime-music # For navidrome
# cinny-desktop
gajim
pkgs.unstable.cinny-desktop
pkgs.unstable.gajim
vivaldi
# Note taking
pkgs.unstable.logseq
# Audio
qpwgraph # Pipewire graph
unstable.zotero
# (if stdenv.isAarch64 then zotero else pkgs.unstable.zotero) // kinda fucked for now from CVE
libreoffice
mpv # for anki
anki-bin
pkgs.unstable.anki-bin
# Chat stuff
tdesktop
whatsapp-for-linux
slack
zoom-us
obs-studio
## CLI stuff
dex # .desktop file management, startup
# sct # Display color temperature
xdg-utils # Open stuff
wifi-indicator
] ++ cfg.startup);
] ++ (if pkgs.stdenv.isAarch64 then [ ] else [
gnome.cheese # Webcam check, expensive
# Chat stuff
unstable.slack
]));
nki.programs.discord.enable = pkgs.stdenv.isx86_64;
nki.programs.discord.package = pkgs.vesktop;
nki.programs.discord.package = (pkgs.callPackage pkgs.unstable.vesktop.override { }).overrideAttrs (attrs: {
nativeBuildInputs = attrs.nativeBuildInputs ++ [ pkgs.nss_latest pkgs.makeWrapper ];
postInstall = ''
makeWrapper $out/bin/vesktop $out/bin/discord
'';
});
# OBS
programs.obs-studio = {
# Yellow light!
services.wlsunset = {
enable = true;
plugins = with pkgs.obs-studio-plugins; [
wlrobs
input-overlay
obs-pipewire-audio-capture
];
# Lausanne
latitude = "46.31";
longitude = "6.38";
};
# Cursor
@ -130,45 +105,23 @@ in
xdg.mimeApps.enable = true;
xdg.mimeApps.associations.added = {
"x-scheme-handler/mailto" = [ "thunderbird.desktop" "org.gnome.Evolution.desktop" ];
"x-scheme-handler/mailto" = [ "org.gnome.Evolution.desktop" ];
"application/pdf" = [ "org.gnome.Evince.desktop" ];
"text/plain" = [ "kakoune.desktop" ];
# Other Thunderbird stuff
"x-scheme-handler/mid" = [ "thunderbird.desktop" ];
"x-scheme-handler/news" = [ "thunderbird.desktop" ];
"x-scheme-handler/snews" = [ "thunderbird.desktop" ];
"x-scheme-handler/nntp" = [ "thunderbird.desktop" ];
"x-scheme-handler/feed" = [ "thunderbird.desktop" ];
"application/rss+xml" = [ "thunderbird.desktop" ];
"application/x-extension-rss" = [ "thunderbird.desktop" ];
"x-scheme-handler/tg2" = [ "org.telegram.desktop.desktop" ];
"x-scheme-handler/tonsite2" = [ "org.telegram.desktop.desktop" ];
};
xdg.mimeApps.defaultApplications = {
# Email
"x-scheme-handler/mailto" = [ "thunderbird.desktop" "org.gnome.Evolution.desktop" ];
"x-scheme-handler/webcal" = [ "thunderbird.desktop" ];
"x-scheme-handler/webcals" = [ "thunderbird.desktop" ];
# Other Thunderbird stuff
"x-scheme-handler/mid" = [ "thunderbird.desktop" ];
"x-scheme-handler/news" = [ "thunderbird.desktop" ];
"x-scheme-handler/snews" = [ "thunderbird.desktop" ];
"x-scheme-handler/nntp" = [ "thunderbird.desktop" ];
"x-scheme-handler/feed" = [ "thunderbird.desktop" ];
"application/rss+xml" = [ "thunderbird.desktop" ];
"application/x-extension-rss" = [ "thunderbird.desktop" ];
"x-scheme-handler/mailto" = [ "org.gnome.Evolution.desktop" ];
# Default web browser stuff
"text/html" = [ (desktopFileOf cfg.defaults.webBrowser) ];
"x-scheme-handler/about" = [ (desktopFileOf cfg.defaults.webBrowser) ];
"x-scheme-handler/unknown" = [ (desktopFileOf cfg.defaults.webBrowser) ];
"x-scheme-handler/http" = [ (desktopFileOf cfg.defaults.webBrowser) ];
"x-scheme-handler/https" = [ (desktopFileOf cfg.defaults.webBrowser) ];
"x-scheme-handler/ftp" = [ (desktopFileOf cfg.defaults.webBrowser) ];
"x-scheme-handler/ftps" = [ (desktopFileOf cfg.defaults.webBrowser) ];
"x-scheme-handler/file" = [ (desktopFileOf cfg.defaults.webBrowser) ];
"text/html" = [ cfg.defaults.webBrowser ];
"x-scheme-handler/about" = [ cfg.defaults.webBrowser ];
"x-scheme-handler/unknown" = [ cfg.defaults.webBrowser ];
"x-scheme-handler/http" = [ cfg.defaults.webBrowser ];
"x-scheme-handler/https" = [ cfg.defaults.webBrowser ];
"x-scheme-handler/ftp" = [ cfg.defaults.webBrowser ];
"x-scheme-handler/ftps" = [ cfg.defaults.webBrowser ];
"x-scheme-handler/file" = [ cfg.defaults.webBrowser ];
# Torrent
"application/x-bittorrent" = [ "deluge.desktop" ];
@ -176,14 +129,10 @@ in
# Text
"text/plain" = [ "kakoune.desktop" ];
"application/pdf" = [ "okularApplication_pdf.desktop" ];
"application/pdf" = [ "org.gnome.Evince.desktop" ];
# Files
"inode/directory" = [ "dolphin.desktop" ];
# Telegram
"x-scheme-handler/tg2" = "org.telegram.desktop.desktop";
"x-scheme-handler/tonsite2" = "org.telegram.desktop.desktop";
"inode/directory" = [ "nemo.desktop" ];
};
# Add one for kakoune
@ -202,36 +151,21 @@ in
## GTK
gtk.enable = true;
gtk.cursorTheme = { inherit (config.home.pointerCursor) package name size; };
gtk.font.name = "IBM Plex Sans JP";
gtk.font.name = "system-ui";
gtk.font.size = 10;
gtk.iconTheme = {
package = pkgs.kdePackages.breeze-icons;
name = "breeze";
package = pkgs.numix-icon-theme;
name = "Numix";
};
gtk.theme = {
package = pkgs.kdePackages.breeze-gtk;
name = "Breeze";
package = pkgs.numix-gtk-theme;
name = "Numix";
};
gtk.gtk2.configLocation = "${config.xdg.configHome}/gtk-2.0/gtkrc";
gtk.gtk2.extraConfig = ''
gtk-enable-animations=1
gtk-im-module="fcitx"
gtk-theme-name="Numix"
gtk-primary-button-warps-slider=1
gtk-toolbar-style=3
gtk-menu-images=1
gtk-button-images=1
gtk-sound-theme-name="ocean"
gtk-icon-theme-name="breeze"
'';
gtk.gtk3.extraConfig.gtk-im-module = "fcitx";
gtk.gtk4.extraConfig.gtk-im-module = "fcitx";
## Qt
qt.enable = true;
qt.platformTheme.name = "kde";
qt.platformTheme.package = with pkgs.kdePackages; [ plasma-integration systemsettings ];
qt.style.package = [ pkgs.kdePackages.breeze ];
qt.style.name = "Breeze";
qt.platformTheme = "gnome";
qt.style.package = pkgs.adwaita-qt;
qt.style.name = "adwaita";
xdg.configFile =
let
@ -241,22 +175,17 @@ in
source =
let
srcFile = pkgs.runCommand "${pkg.name}-startup" { } ''
mkdir -p $out
cp $(ls -d ${pkg}/share/applications/*.desktop | head -n 1) $out/${pkg.name}.desktop
mkdir - p $out
cp $
(ls - d ${
pkg}/share/applications/*.desktop | head -n 1) $out/${pkg.name}.desktop
'';
in
"${srcFile}/${pkg.name}.desktop";
};
};
autoStartup = listToAttrs (map f cfg.startup);
in
autoStartup // {
## Polkit UI
"autostart/polkit.desktop".text = ''
${builtins.readFile "${pkgs.pantheon.pantheon-agent-polkit}/etc/xdg/autostart/io.elementary.desktop.agent-polkit.desktop"}
OnlyShowIn=sway;
'';
};
listToAttrs (map f (cfg.startup ++ alwaysStartup));
# IBus configuration
# dconf.settings."desktop/ibus/general" = {
# engines-order = hm.gvariant.mkArray hm.gvariant.type.string [ "xkb:jp::jpn" "mozc-jp" "Bamboo" ];
@ -265,26 +194,6 @@ in
# dconf.settings."desktop/ibus/general/hotkey" = {
# triggers = hm.gvariant.mkArray hm.gvariant.type.string [ "<Super>z" ];
# };
# Some graphical targets
systemd.user.targets = {
# For system trays, usually after graphical-session and graphical-session-pre
tray = {
Unit.Description = lib.mkDefault "System tray";
Unit.After = [ "graphical-session-pre.target" ];
Unit.Before = [ "graphical-session.target" ];
Unit.BindsTo = [ "graphical-session.target" ];
Install.WantedBy = [ "graphical-session.target" ];
};
# XWayland target
xwayland = {
Unit.Description = "XWayland support";
Unit.After = [ "graphical-session-pre.target" ];
Unit.Before = [ "graphical-session.target" ];
Unit.BindsTo = [ "graphical-session.target" ];
Install.WantedBy = [ "graphical-session.target" ];
};
};
};
}

View file

@ -5,16 +5,28 @@ let
swaync = pkgs.swaynotificationcenter;
in
with lib; mkIf (config.linux.graphical.type == "wayland") {
services.swaync = {
enable = true;
settings.widgets = [ "inhibitors" "title" "dnd" "mpris" "notifications" ];
style = ./swaync.css;
home.packages = [ swaync ];
wayland.windowManager.sway.config = {
startup = [
{ command = "swaync"; }
];
};
xdg.configFile = {
"swaync/config.json" = {
text = builtins.toJSON {
widgets = [ "inhibitors" "title" "dnd" "mpris" "notifications" ];
scripts = { };
};
onChange = "swaync-client -R";
};
"swaync/style.css" = {
source = ./swaync.css;
onChange = "swaync-client -rs";
};
};
systemd.user.services.swaync.Install.WantedBy = lib.mkForce [ "sway-session.target" ];
systemd.user.services.swaync.Unit.PartOf = lib.mkForce [ "graphical-session.target" ];
programs.my-waybar = {
extraSettings = [{
programs.my-sway.waybar = {
extraSettings = {
modules-right = mkAfter [ "custom/swaync" ];
modules."custom/swaync" = {
tooltip = false;
@ -36,7 +48,7 @@ let
on-click-right = "${swaync}/bin/swaync-client -d -sw";
escape = true;
};
}];
};
extraStyle = mkAfter ''
#custom-swaync {
background: #F0FFFF;
@ -45,72 +57,24 @@ let
'';
};
};
plasmaModule = { pkgs, ... }: {
home.packages = with pkgs.kdePackages; [
discover
kmail
kontact
akonadi
kdepim-runtime
kmail-account-wizard
akonadi-import-wizard
];
xdg.configFile."plasma-workspace/env/wayland.sh".source = pkgs.writeScript "plasma-wayland-env.sh" ''
export NIXOS_OZONE_WL=1
'';
xdg.dataFile."dbus-1/services/org.freedesktop.Notifications.service".source = "${pkgs.kdePackages.plasma-workspace}/share/dbus-1/services/org.kde.plasma.Notifications.service";
};
rofi-rbw-script = pkgs.writeShellApplication {
name = "rofi-rbw-script";
runtimeInputs = with pkgs; [ rofi wtype rofi-rbw ];
text = "rofi-rbw";
meta.mainProgram = "rofi-rbw-script";
};
in
with lib;
{
imports = [ notificationModule plasmaModule ];
imports = [ notificationModule ];
config = mkIf (config.linux.graphical.type == "wayland") {
# Additional packages
home.packages = with pkgs; [
wl-clipboard # Clipboard management
rofi-rbw-script
# Mimic the clipboard stuff in MacOS
(pkgs.writeShellScriptBin "pbcopy" ''
exec ${pkgs.wl-clipboard}/bin/wl-copy "$@"
exec ${pkgs.wl-clipboard}/bin/wl-copy
'')
(pkgs.writeShellScriptBin "pbpaste" ''
exec ${pkgs.wl-clipboard}/bin/wl-paste "$@"
exec ${pkgs.wl-clipboard}/bin/wl-paste -n
'')
];
programs.rofi = {
enable = true;
package = pkgs.rofi-wayland;
cycle = true;
font = "monospace";
terminal = "${lib.getExe config.programs.kitty.package}";
theme = "Paper";
plugins = with pkgs; [ rofi-bluetooth rofi-calc rofi-rbw rofi-power-menu ];
};
home.sessionVariables = {
ANKI_WAYLAND = "1";
};
# Yellow light!
services.wlsunset = {
enable = true;
# Lausanne
latitude = "46.31";
longitude = "6.38";
};
# Notification system
# services.dunst = {
# enable = true;
@ -130,6 +94,9 @@ with lib;
# settings.experimental.per_monitor_dpi = "true";
# };
# Forward wallpaper settings to sway
programs.my-sway.wallpaper = config.linux.graphical.wallpaper;
};
}

View file

@ -5,47 +5,32 @@ let
# Internal
"framework" = {
name = "BOE 0x0BCA Unknown";
meta.mode = { width = 2256; height = 1504; refresh = 60.0; };
mode = "2256x1504@60Hz";
scale = 1.25;
};
"yoga" = {
name = "AU Optronics 0xD291 Unknown";
meta.connection = "eDP-1";
meta.mode = { width = 1920; height = 1080; refresh = 60.026; };
scale = 1;
};
# External
## Work @ EPFL
"work" = {
name = "LG Electronics LG ULTRAFINE 301MAXSGHD10";
meta.mode = { width = 3840; height = 2160; refresh = 60.0; };
mode = "3840x2160@60Hz";
scale = 1.25;
};
"home_4k" = {
name = "AOC U28G2G6B PPYP2JA000013";
mode = "3840x2160@60Hz";
scale = 1.5;
adaptive_sync = "on";
meta = {
connection = "DP-2";
mode = { width = 3840; height = 2160; refresh = 60.0; };
fixedPosition = { x = 0; y = 0; };
niriName = "PNP(AOC) U28G2G6B PPYP2JA000013";
};
# render_bit_depth = "10";
};
"home_1440" = {
name = "AOC Q27G2G3R3B VXJP6HA000442";
mode = "2560x1440@165Hz";
adaptive_sync = "on";
meta = {
connection = "DP-3";
mode = { width = 2560; height = 1440; refresh = 165.0; };
fixedPosition = { x = 2560; y = 0; };
niriName = "PNP(AOC) Q27G2G3R3B VXJP6HA000442";
};
};
"viewsonic_1080" = {
name = "ViewSonic Corporation XG2402 SERIES V4K182501054";
meta.mode = { width = 1920; height = 1080; refresh = 144.0; };
mode = "1920x1080@144Hz";
adaptive_sync = "on";
};
@ -53,13 +38,9 @@ let
eachMonitor = _name: monitor: {
name = monitor.name;
value = builtins.removeAttrs monitor [ "scale" "name" "meta" ] // (lib.optionalAttrs (monitor ? scale) {
value = builtins.removeAttrs monitor [ "scale" "name" ] // (if monitor ? scale then {
scale = toString monitor.scale;
}) // {
mode = with monitor.meta.mode; "${toString width}x${toString height}@${toString refresh}Hz";
} // (lib.optionalAttrs (monitor.meta ? fixedPosition) {
position = with monitor.meta.fixedPosition; "${toString x} ${toString y}";
});
} else { });
};
in
{

View file

@ -4,23 +4,52 @@ with lib;
let
lspConfig =
{
language_ids = {
c = "c_cpp";
cpp = "c_cpp";
javascript = "javascriptreact";
typescript = "typescriptreact";
protobuf = "proto";
sh = "shellscript";
};
language_servers = {
ccls = {
languages = {
bash = {
args = [ "start" ];
command = "bash-language-server";
filetypes = [ "sh" ];
roots = [ ".git" ".hg" ];
};
c_cpp = {
args = [ "-v=2" "-log-file=/tmp/ccls.log" ];
command = "ccls";
filetypes = [ "c" "cpp" ];
roots = [ "compile_commands.json" ".cquery" ".git" ];
};
gopls = {
crystal = {
command = "scry";
filetypes = [ "crystal" ];
roots = [ "shard.yml" ];
};
css = {
args = [ "--stdio" ];
command = "css-languageserver";
filetypes = [ "css" ];
roots = [ "package.json" ];
};
d = {
command = "dls";
filetypes = [ "d" "di" ];
roots = [ ".git" "dub.sdl" "dub.json" ];
};
dart = {
command = "dart_language_server";
filetypes = [ "dart" ];
roots = [ "pubspec.yaml" ".git" ];
};
elm = {
args = [ "--stdio" ];
command = "elm-language-server";
filetypes = [ "elm" ];
roots = [ "elm.json" ];
};
fsharp = {
command = "FSharpLanguageServer";
filetypes = [ "fsharp" ];
roots = [ ".git" "*.fsx" ];
};
go = {
command = "gopls";
filetypes = [ "go" ];
offset_encoding = "utf-8";
@ -28,14 +57,55 @@ let
settings = { gopls = { hoverKind = "SynopsisDocumentation"; semanticTokens = true; }; };
settings_section = "gopls";
};
haskell-language-server = {
haskell = {
args = [ "--lsp" ];
command = "haskell-language-server-wrapper";
filetypes = [ "haskell" ];
roots = [ "Setup.hs" "stack.yaml" "*.cabal" "package.yaml" ];
settings_section = "haskell";
};
nil = {
html = {
args = [ "--stdio" ];
command = "html-languageserver";
filetypes = [ "html" ];
roots = [ "package.json" ];
};
javascript = {
args = [ "lsp" ];
command = "flow";
filetypes = [ "javascript" ];
roots = [ ".flowconfig" ];
};
json = {
args = [ "--stdio" ];
command = "json-languageserver";
filetypes = [ "json" ];
roots = [ "package.json" ];
};
latex = {
command = "texlab";
filetypes = [ "latex" ];
roots = [ ".git" "main.tex" "all.tex" ];
settings_section = "texlab";
settings.texlab = {
build.executable = "latexmk";
build.args = [ "-pdf" "-shell-escape" "-interaction=nonstopmode" "-synctex=1" "%f" ];
build.forwardSearchAfter = true;
build.onSave = true;
forwardSearch = {
executable = "/Applications/Skim.app/Contents/SharedSupport/displayline";
args = [ "-r" "-g" "%l" "%p" "%f" ];
};
};
};
nim = {
command = "nimlsp";
filetypes = [ "nim" ];
roots = [ "*.nimble" ".git" ];
};
nix = {
command = "${pkgs.nil}/bin/nil";
filetypes = [ "nix" ];
roots = [ "flake.nix" "shell.nix" ".git" ];
@ -43,12 +113,48 @@ let
formatting.command = [ "${getExe pkgs.nixpkgs-fmt}" ];
};
};
pyls = {
ocaml = {
args = [ ];
command = "ocamllsp";
filetypes = [ "ocaml" ];
roots = [ "Makefile" "opam" "*.opam" "dune" ".merlin" ".ocamlformat" ];
};
php = {
args = [ "--stdio" ];
command = "intelephense";
filetypes = [ "php" ];
roots = [ ".htaccess" "composer.json" ];
};
python = {
command = "pyls";
filetypes = [ "python" ];
offset_encoding = "utf-8";
roots = [ "requirements.txt" "setup.py" ".git" ".hg" ];
};
racket = {
args = [ "-l" "racket-langserver" ];
command = "racket";
filetypes = [ "racket" ];
roots = [ ".git" ];
};
reason = {
args = [ "--stdio" ];
command = "ocaml-language-server";
filetypes = [ "reason" ];
roots = [ "package.json" "Makefile" ".git" ".hg" ];
};
ruby = {
args = [ "stdio" ];
command = "solargraph";
filetypes = [ "ruby" ];
roots = [ "Gemfile" ];
};
rust = {
args = [ ];
command = "rust-analyzer";
filetypes = [ "rust" ];
roots = [ "Cargo.toml" ];
};
};
semantic_tokens.faces = [
## Items
@ -85,7 +191,7 @@ let
verbosity = 255;
};
languageServerOption = types.submodule {
languageOption = types.submodule {
options = {
filetypes = mkOption {
type = types.listOf types.str;
@ -119,28 +225,10 @@ let
default = null;
description = "Additional settings to be passed to the LSP server.";
};
package = mkOption {
type = types.nullOr types.package;
default = null;
description = "The default package of the language server. Will be appended as the ending segments of the PATH to kak-lsp";
};
};
};
cfg = config.programs.kak-lsp;
serverPackages =
filter (v: v != null)
(lib.mapAttrsToList (_: serv: serv.package) cfg.languageServers);
wrappedPackage = pkgs.symlinkJoin {
name = "kak-lsp-wrapped";
nativeBuildInputs = [ pkgs.makeWrapper ];
paths = [ cfg.package ];
postBuild = ''
wrapProgram $out/bin/kak-lsp --suffix PATH ":" ${lib.makeBinPath serverPackages}
'';
};
in
{
options.programs.kak-lsp = {
@ -174,40 +262,35 @@ in
description = "Server timeout";
};
languageServers = mkOption {
type = types.attrsOf languageServerOption;
languages = mkOption {
type = types.attrsOf languageOption;
default = { };
description = "The language options";
};
languageIds = mkOption {
type = types.attrsOf types.str;
default = { };
description = "Language IDs to be sent to the LSP";
};
};
config = mkIf cfg.enable
{
home.packages = [ wrappedPackage ];
config = mkIf cfg.enable {
home.packages = [ cfg.package ];
# Configurations
xdg.configFile."kak-lsp/kak-lsp.toml" =
let
toml = pkgs.formats.toml { };
toLspConfig = lib.filterAttrsRecursive (n: v: n != "package" && v != null);
in
# Configurations
xdg.configFile."kak-lsp/kak-lsp.toml" = {
source = pkgs.runCommand "config.toml"
{
source = toml.generate "config.toml"
{
buildInputs = [ pkgs.yj ];
preferLocalBuild = true;
} ''
yj -jt -i \
< ${
pkgs.writeText "config.json" (builtins.toJSON {
semantic_tokens.faces = cfg.semanticTokens.faces ++ cfg.semanticTokens.additionalFaces;
server.timeout = cfg.serverTimeout;
snippet_support = cfg.enableSnippets;
verbosity = 255;
language_server = toLspConfig (lspConfig.language_servers // cfg.languageServers);
language_ids = lspConfig.language_ids // cfg.languageIds;
};
};
language = lspConfig.languages // cfg.languages;
})
} \
> $out
'';
};
};
}

View file

@ -43,96 +43,83 @@ in
};
package = mkPackageOption pkgs "kak-tree-sitter" { };
features = {
highlighting = mkOption {
type = types.bool;
description = "Enable highlighting";
default = true;
};
text_objects = mkOption {
type = types.bool;
description = "Enable text objects";
default = true;
};
};
highlighterGroups = mkOption {
type = types.attrsOf types.str;
default = {
attribute = "@attribute";
comment = "@comment";
conceal = "%opt{mauve}+i";
constant = "%opt{peach}";
constant_builtin_boolean = "%opt{sky}";
constant_character = "%opt{yellow}";
constant_macro = "%opt{mauve}";
constant_numeric = "%opt{peach}";
constructor = "%opt{sapphire}";
diff_plus = "%opt{green}";
diff_minus = "%opt{red}";
diff_delta = "%opt{blue}";
diff_delta_moved = "%opt{mauve}";
error = "%opt{red}+b";
conceal = "%opt{kts_mauve}+i";
constant = "%opt{kts_peach}";
constant_builtin_boolean = "%opt{kts_sky}";
constant_character = "%opt{kts_yellow}";
constant_macro = "%opt{kts_mauve}";
constant_numeric = "%opt{kts_peach}";
constructor = "%opt{kts_sapphire}";
diff_plus = "%opt{kts_green}";
diff_minus = "%opt{kts_red}";
diff_delta = "%opt{kts_blue}";
diff_delta_moved = "%opt{kts_mauve}";
error = "%opt{kts_red}+b";
function = "@function";
function_builtin = "@builtin";
function_macro = "+i@ts_function";
hint = "%opt{blue}+b";
info = "%opt{green}+b";
hint = "%opt{kts_blue}+b";
info = "%opt{kts_green}+b";
keyword = "keyword";
keyword_conditional = "+i@ts_keyword";
keyword_control_conditional = "+i@ts_keyword";
keyword_control_directive = "+i@ts_keyword";
keyword_control_import = "+i@ts_keyword";
keyword_directive = "+i@ts_keyword";
label = "%opt{sapphire}+i";
markup_bold = "%opt{peach}+b";
markup_heading = "%opt{red}";
markup_heading_1 = "%opt{red}";
markup_heading_2 = "%opt{mauve}";
markup_heading_3 = "%opt{green}";
markup_heading_4 = "%opt{yellow}";
markup_heading_5 = "%opt{pink}";
markup_heading_6 = "%opt{teal}";
markup_heading_marker = "%opt{peach}+b";
markup_italic = "%opt{pink}+i";
markup_list_checked = "%opt{green}";
markup_list_numbered = "%opt{blue}+i";
markup_list_unchecked = "%opt{teal}";
markup_list_unnumbered = "%opt{mauve}";
markup_link_label = "%opt{blue}";
markup_link_url = "%opt{teal}+u";
markup_link_uri = "%opt{teal}+u";
markup_link_text = "%opt{blue}";
markup_quote = "%opt{crust}";
markup_raw = "%opt{sky}";
markup_raw_block = "%opt{sky}";
markup_raw_inline = "%opt{green}";
markup_strikethrough = "%opt{crust}+s";
label = "%opt{kts_sapphire}+i";
markup_bold = "%opt{kts_peach}+b";
markup_heading = "%opt{kts_red}";
markup_heading_1 = "%opt{kts_red}";
markup_heading_2 = "%opt{kts_mauve}";
markup_heading_3 = "%opt{kts_green}";
markup_heading_4 = "%opt{kts_yellow}";
markup_heading_5 = "%opt{kts_pink}";
markup_heading_6 = "%opt{kts_teal}";
markup_heading_marker = "%opt{kts_peach}+b";
markup_italic = "%opt{kts_pink}+i";
markup_list_checked = "%opt{kts_green}";
markup_list_numbered = "%opt{kts_blue}+i";
markup_list_unchecked = "%opt{kts_teal}";
markup_list_unnumbered = "%opt{kts_mauve}";
markup_link_label = "%opt{kts_blue}";
markup_link_url = "%opt{kts_teal}+u";
markup_link_uri = "%opt{kts_teal}+u";
markup_link_text = "%opt{kts_blue}";
markup_quote = "%opt{kts_gray1}";
markup_raw = "%opt{kts_sky}";
markup_raw_block = "%opt{kts_sky}";
markup_raw_inline = "%opt{kts_green}";
markup_strikethrough = "%opt{kts_gray1}+s";
namespace = "@module";
operator = "@operator";
property = "%opt{sky}";
punctuation = "%opt{overlay2}";
punctuation_special = "%opt{sky}";
special = "%opt{blue}";
spell = "%opt{mauve}";
string = "%opt{green}";
string_regex = "%opt{peach}";
string_regexp = "%opt{peach}";
string_escape = "%opt{mauve}";
string_special = "%opt{blue}";
string_special_path = "%opt{green}";
string_special_symbol = "%opt{mauve}";
string_symbol = "%opt{red}";
tag = "%opt{teal}";
tag_error = "%opt{red}";
text_title = "%opt{mauve}";
type = "@type";
type_enum_variant = "+i@ts_type";
property = "%opt{kts_sky}";
punctuation = "%opt{kts_overlay2}";
punctuation_special = "%opt{kts_sky}";
special = "%opt{kts_blue}";
spell = "%opt{kts_mauve}";
string = "%opt{kts_green}";
string_regex = "%opt{kts_peach}";
string_regexp = "%opt{kts_peach}";
string_escape = "%opt{kts_mauve}";
string_special = "%opt{kts_blue}";
string_special_path = "%opt{kts_green}";
string_special_symbol = "%opt{kts_mauve}";
string_symbol = "%opt{kts_red}";
tag = "%opt{kts_teal}";
tag_error = "%opt{kts_red}";
text_title = "%opt{kts_mauve}";
type = "%opt{kts_yellow}";
type_enum_variant = "%opt{kts_flamingo}";
variable = "@variable";
variable_builtin = "@builtin";
variable_other_member = "%opt{teal}";
variable_parameter = "+i@variable";
warning = "%opt{peach}+b";
variable_other_member = "%opt{kts_teal}";
variable_parameter = "%opt{kts_maroon}+i";
warning = "%opt{kts_peach}+b";
};
};
@ -187,6 +174,8 @@ in
config =
let
aliasedOnce = name: values: if asserts.assertMsg (builtins.length values 1) "face ${name} was aliased more than once: ${toString values}" then (builtins.head values) else [ ];
allGroups = attrsets.recursiveUpdate cfg.highlighterGroups cfg.extraHighlighterGroups;
aliases = attrsets.recursiveUpdate cfg.aliases cfg.extraAliases;
@ -200,24 +189,10 @@ in
toml = pkgs.formats.toml { };
srcName = src: lib.removePrefix "/nix/store/" src.outPath;
mkGitRepo = src: pkgs.runCommandLocal "${src.name}-git" { } ''
cp -r --no-preserve=all ${src} $out
cd $out
if ! test -d $out/.git; then
${lib.getExe pkgs.git} init -b ${srcName src}
${lib.getExe pkgs.git} config user.email "a@b.com"
${lib.getExe pkgs.git} config user.name "a"
${lib.getExe pkgs.git} add .
${lib.getExe pkgs.git} commit -m "Just making a git commit"
fi
'';
toLanguageConf = name: lang: with lang; {
grammar = {
inherit (grammar) path;
source.git.url = "${mkGitRepo grammar.src}";
source.git.pin = "${srcName grammar.src}";
url = "${grammar.src}";
compile = grammar.compile.command;
compile_args = grammar.compile.args;
compile_flags = grammar.compile.flags;
@ -226,8 +201,7 @@ in
link_flags = grammar.link.flags;
};
queries = {
source.git.url = "${mkGitRepo queries.src}";
source.git.pin = "${srcName queries.src}";
url = "${queries.src}";
path = if queries.path == null then "runtime/queries/${name}" else queries.path;
};
};
@ -246,14 +220,19 @@ in
xdg.configFile."kak-tree-sitter/config.toml" = {
source = toml.generate "config.toml" {
highlight.groups = builtins.map toScm (builtins.attrNames allGroups ++ builtins.attrNames aliases);
features = cfg.features;
language = builtins.mapAttrs toLanguageConf cfg.languages;
};
onChange = ''
export PATH=$PATH:${lib.getBin pkgs.gcc}
${cfg.package}/bin/ktsctl sync -a
'';
onChange =
let
buildCmd = lang: "ktsctl -fci ${lang}";
buildAll = strings.concatMapStringsSep "\n" buildCmd (builtins.attrNames cfg.languages);
in
''
# Rebuild languages
${buildAll}
'';
};
programs.my-kakoune.extraFaces = faces;

View file

@ -2,28 +2,14 @@
let
cfg = config.nki.programs.kitty;
theme = { lib, options, config, ... }: {
programs.kitty = lib.mkIf config.nki.programs.kitty.enable (
if builtins.hasAttr "themeFile" options.programs.kitty then {
themeFile = "ayu_light";
} else {
theme = "Ayu Light";
}
);
};
cmd = if pkgs.stdenv.isDarwin then "cmd" else "ctrl";
in
with lib;
{
imports = [ theme ./darwin.nix ./linux.nix ./tabs.nix ];
imports = [ ./darwin.nix ./linux.nix ./tabs.nix ];
options.nki.programs.kitty = {
enable = mkEnableOption "Enable kitty";
setDefault = mkOption {
type = types.bool;
description = "Set kitty as default terminal";
default = true;
};
package = mkOption {
type = types.package;
@ -56,10 +42,6 @@ with lib;
};
};
config.linux.graphical = mkIf (cfg.enable && cfg.setDefault) {
defaults.terminal.package = cfg.package;
};
config.programs.kitty = mkIf cfg.enable {
enable = true;
@ -69,12 +51,14 @@ with lib;
font.name = "Fantasque Sans Mono";
font.size = cfg.fontSize;
theme = "Ayu Light";
settings =
let
# Background color and transparency
background =
if isNull cfg.background then {
background_opacity = "0.93";
background_opacity = "0.85";
dynamic_background_opacity = true;
} else {
background_image = "${cfg.background}";
@ -109,18 +93,6 @@ with lib;
## Hints
"${cfg.cmd}+shift+p>n" = "kitten hints --type=linenum --linenum-action=tab kak {path} +{line}";
};
extraConfig =
let
# Nerd Fonts glyph map
glyphMap = pkgs.fetchurl {
url = "https://raw.githubusercontent.com/Sharparam/dotfiles/main/kitty/.config/kitty/font-nerd-symbols.conf";
hash = "sha256-1OaDWLC3y8ASD2ttRWWgPEpRnfKXu6H6vS3cFVpzT0o=";
};
in
''
include ${glyphMap}
'';
};
}

View file

@ -1,388 +0,0 @@
{ config, osConfig, lib, pkgs, ... }:
let
cfg = config.programs.my-niri;
sh = config.lib.niri.actions.spawn "sh" "-c";
playerctl = lib.getExe pkgs.playerctl;
amixer = lib.getExe' pkgs.alsa-utils "amixer";
brightnessctl = lib.getExe pkgs.brightnessctl;
app-menu = "${pkgs.dmenu}/bin/dmenu_path | ${pkgs.bemenu}/bin/bemenu | ${pkgs.findutils}/bin/xargs swaymsg exec --";
wallpaper = config.linux.graphical.wallpaper;
xwayland-display = ":0";
in
{
options.programs.my-niri = {
enable = lib.mkEnableOption "My own niri configuration";
enableLaptop = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Enable laptop options";
};
lock-command = lib.mkOption {
type = lib.types.listOf lib.types.str;
description = "The command to lock the screen";
default = [ "${pkgs.swaylock}/bin/swaylock" ]
++ (if wallpaper == "" then [ "" ] else [ "-i" "${wallpaper}" "-s" "fill" ])
++ [ "-l" "-k" ];
};
workspaces = lib.mkOption {
type = lib.types.attrsOf
(lib.types.submodule {
options = {
name = lib.mkOption { type = lib.types.str; description = "workspace name"; };
fixed = lib.mkOption {
type = lib.types.bool;
default = true;
description = "whether workspace always exists";
};
monitor = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = null;
description = "Default monitor to spawn workspace in";
};
};
});
description = "A mapping of ordering to workspace names, for fixed workspaces";
};
};
config = lib.mkIf cfg.enable {
programs.my-niri.workspaces = {
# Default workspaces, always there
"01" = { name = "🌏 web"; };
"02" = { name = "💬 chat"; };
"03" = { name = " code"; };
"04" = { name = "🎶 music"; };
"05" = { name = "🔧 extra"; };
"06" = { name = "🧰 6"; };
"07" = { name = "🔩 7"; };
"08" = { name = "🛠 8"; };
"09" = { name = "🔨 9"; };
"10" = { name = "🎲 misc"; };
"99" = { name = "📧 Email"; };
};
systemd.user.services.swaync.Install.WantedBy = [ "niri.service" ];
systemd.user.services.swaync.Unit.After = [ "niri.service" ];
systemd.user.targets.tray.Unit.After = [ "niri.service" ];
systemd.user.services.waybar.Unit.After = [ "niri.service" ];
systemd.user.services.waybar.Install.WantedBy = [ "niri.service" ];
systemd.user.targets.xwayland.Unit.After = [ "niri.service" ];
# xwayland-satellite
systemd.user.services.niri-xwayland-satellite = lib.mkIf cfg.enable {
Unit = {
Description = "XWayland Client for niri";
PartOf = [ "xwayland.target" ];
Before = [ "xwayland.target" "xdg-desktop-autostart.target" ];
After = [ "niri.service" ];
};
Install.UpheldBy = [ "niri.service" ];
Service.Slice = "session.slice";
Service.Type = "notify";
Service.ExecStart = "${lib.getExe pkgs.xwayland-satellite} ${xwayland-display}";
};
programs.niri.settings = {
environment = {
QT_QPA_PLATFORM = "wayland";
QT_WAYLAND_DISABLE_WINDOWDECORATION = "1";
QT_IM_MODULE = "fcitx";
GTK_IM_MODULE = "fcitx"; # Til text-input is merged
# export NIXOS_OZONE_WL=1 # Until text-input is merged
DISPLAY = xwayland-display;
} // lib.optionalAttrs osConfig.services.desktopManager.plasma6.enable {
XDG_MENU_PREFIX = "plasma-";
};
input.keyboard.xkb = { layout = "jp"; };
input.touchpad = lib.mkIf cfg.enableLaptop {
tap = true;
dwt = true;
natural-scroll = true;
middle-emulation = true;
};
input.mouse = {
accel-profile = "flat";
};
input.warp-mouse-to-focus = true;
input.focus-follows-mouse = {
enable = true;
max-scroll-amount = "0%";
};
outputs =
let
eachMonitor = _: monitor: {
name = monitor.meta.niriName or monitor.name; # Niri might not find the monitor by name
value = {
mode = monitor.meta.mode;
position = monitor.meta.fixedPosition or null;
scale = monitor.scale or 1;
variable-refresh-rate = (monitor.adaptive_sync or "off") == "on";
};
};
in
lib.mapAttrs' eachMonitor config.common.monitors;
spawn-at-startup = [
# Wallpaper
{ command = [ (lib.getExe pkgs.swaybg) "-i" "${wallpaper}" "-m" "fill" ]; }
# Waybar
{ command = [ "systemctl" "--user" "start" "xdg-desktop-portal-gtk.service" "xdg-desktop-portal.service" ]; }
];
layout = {
gaps = 16;
preset-column-widths = [
{ proportion = 1. / 3.; }
{ proportion = 1. / 2.; }
{ proportion = 2. / 3.; }
];
default-column-width.proportion = 1. / 2.;
focus-ring = {
width = 4;
active.gradient = { from = "#00447AFF"; to = "#71C4FFAA"; angle = 45; };
inactive.color = "#505050";
};
border.enable = false;
struts = let v = 8; in { left = v; right = v; bottom = v; top = v; };
};
prefer-no-csd = true;
workspaces =
let
fixedWorkspaces = lib.filterAttrs (_: w: w.fixed) cfg.workspaces;
workspaceConfig = lib.mapAttrs
(_: w: { inherit (w) name; } // (lib.optionalAttrs (w.monitor != null) {
open-on-output = w.monitor;
}))
fixedWorkspaces;
in
workspaceConfig;
window-rules = [
# Rounded Corners
{
geometry-corner-radius = let v = 8.0; in { bottom-left = v; bottom-right = v; top-left = v; top-right = v; };
clip-to-geometry = true;
}
# Workspace assignments
{
open-on-workspace = cfg.workspaces."01".name;
open-maximized = true;
matches = [
{ at-startup = true; app-id = "^firefox$"; }
{ at-startup = true; app-id = "^librewolf$"; }
{ at-startup = true; app-id = "^zen$"; }
];
}
{
open-on-workspace = cfg.workspaces."02".name;
open-maximized = true;
matches = [
{ title = "^((d|D)iscord|((A|a)rm(c|C)ord))$"; }
{ title = "VencordDesktop"; }
{ app-id = "VencordDesktop"; }
{ title = "vesktop"; }
{ app-id = "vesktop"; }
{ title = "Slack"; }
];
}
{
open-on-workspace = cfg.workspaces."99".name;
open-maximized = true;
matches = [
{ app-id = "thunderbird"; }
{ app-id = "evolution"; }
];
}
# Floating
{
open-floating = true;
matches = [
{ app-id = ".*float.*"; }
{ app-id = "org\\.freedesktop\\.impl\\.portal\\.desktop\\..*"; }
{ title = ".*float.*"; }
{ title = "Extension: .*Bitwarden.*"; }
{ app-id = "Rofi"; }
];
}
# xwaylandvideobridge
{
matches = [{ app-id = "^xwaylandvideobridge$"; }];
open-floating = true;
focus-ring.enable = false;
opacity = 0.0;
default-floating-position = {
x = 0;
y = 0;
relative-to = "bottom-right";
};
min-width = 1;
max-width = 1;
min-height = 1;
max-height = 1;
}
# Kitty dimming
{
matches = [{ app-id = "kitty"; }];
excludes = [{ is-focused = true; }];
opacity = 0.95;
}
];
layer-rules = [
{
matches = [{ namespace = "^swaync-.*"; }];
block-out-from = "screen-capture";
}
];
binds = with config.lib.niri.actions; {
# Mod-Shift-/, which is usually the same as Mod-?,
# shows a list of important hotkeys.
"Mod+Shift+Slash".action = show-hotkey-overlay;
# Some basic spawns
"Mod+Return".action = spawn (lib.getExe config.linux.graphical.defaults.terminal.package);
"Mod+Space".action = spawn (lib.getExe pkgs.rofi) "-show" "drun";
"Mod+R".action = sh app-menu;
"Mod+Semicolon".action = spawn cfg.lock-command;
"Mod+Shift+P".action = spawn "rofi-rbw-script";
# Audio and Volume
"XF86AudioPrev" = { action = spawn playerctl "previous"; allow-when-locked = true; };
"XF86AudioPlay" = { action = spawn playerctl "play-pause"; allow-when-locked = true; };
"Shift+XF86AudioPlay" = { action = spawn playerctl "stop"; allow-when-locked = true; };
"XF86AudioNext" = { action = spawn playerctl "next"; allow-when-locked = true; };
"XF86AudioRecord" = { action = spawn amixer "-q" "set" "Capture" "toggle"; allow-when-locked = true; };
"XF86AudioMute" = { action = spawn amixer "-q" "set" "Master" "toggle"; allow-when-locked = true; };
"XF86AudioLowerVolume" = { action = spawn amixer "-q" "set" "Master" "3%-"; allow-when-locked = true; };
"XF86AudioRaiseVolume" = { action = spawn amixer "-q" "set" "Master" "3%+"; allow-when-locked = true; };
# Backlight
"XF86MonBrightnessDown".action = spawn brightnessctl "s" "10%-";
"XF86MonBrightnessUp".action = spawn brightnessctl "s" "10%+";
"Shift+XF86MonBrightnessDown".action = spawn brightnessctl "-d" "kbd_backlight" "s" "25%-";
"Shift+XF86MonBrightnessUp".action = spawn brightnessctl "-d" "kbd_backlight" "s" "25%+";
"Mod+Shift+Q".action = close-window;
"Mod+Left".action = focus-column-or-monitor-left;
"Mod+Right".action = focus-column-or-monitor-right;
"Mod+Up".action = focus-window-or-workspace-up;
"Mod+Down".action = focus-window-or-workspace-down;
"Mod+H".action = focus-column-or-monitor-left;
"Mod+L".action = focus-column-or-monitor-right;
"Mod+K".action = focus-window-or-workspace-up;
"Mod+J".action = focus-window-or-workspace-down;
"Mod+Shift+Left".action = move-column-left-or-to-monitor-left;
"Mod+Shift+Right".action = move-column-right-or-to-monitor-right;
"Mod+Shift+Up".action = move-window-up-or-to-workspace-up;
"Mod+Shift+Down".action = move-window-down-or-to-workspace-down;
"Mod+Shift+H".action = move-column-left-or-to-monitor-left;
"Mod+Shift+L".action = move-column-right-or-to-monitor-right;
"Mod+Shift+K".action = move-window-up-or-to-workspace-up;
"Mod+Shift+J".action = move-window-down-or-to-workspace-down;
"Mod+Bracketleft".action = focus-column-first;
"Mod+Bracketright".action = focus-column-last;
"Mod+Shift+Bracketleft".action = move-column-to-first;
"Mod+Shift+Bracketright".action = move-column-to-last;
# For compat with my current sway
"Mod+Ctrl+H".action = move-workspace-to-monitor-left;
"Mod+Ctrl+L".action = move-workspace-to-monitor-right;
"Mod+I".action = focus-workspace-down;
"Mod+O".action = focus-workspace-up;
"Mod+Shift+I".action = move-column-to-workspace-down;
"Mod+Shift+O".action = move-column-to-workspace-up;
"Mod+Ctrl+I".action = move-workspace-down;
"Mod+Ctrl+O".action = move-workspace-up;
# Mouse bindings
"Mod+WheelScrollDown" = { action = focus-workspace-down; cooldown-ms = 150; };
"Mod+WheelScrollUp" = { action = focus-workspace-up; cooldown-ms = 150; };
"Mod+Ctrl+WheelScrollDown" = { action = move-column-to-workspace-down; cooldown-ms = 150; };
"Mod+Ctrl+WheelScrollUp" = { action = move-column-to-workspace-up; cooldown-ms = 150; };
"Mod+WheelScrollRight".action = focus-column-right;
"Mod+WheelScrollLeft".action = focus-column-left;
"Mod+Ctrl+WheelScrollRight".action = move-column-right;
"Mod+Ctrl+WheelScrollLeft".action = move-column-left;
# You can refer to workspaces by index. However, keep in mind that
# niri is a dynamic workspace system, so these commands are kind of
# "best effort". Trying to refer to a workspace index bigger than
# the current workspace count will instead refer to the bottommost
# (empty) workspace.
#
# For example, with 2 workspaces + 1 empty, indices 3, 4, 5 and so on
# will all refer to the 3rd workspace.
"Mod+1".action = focus-workspace (cfg.workspaces."01".name);
"Mod+2".action = focus-workspace (cfg.workspaces."02".name);
"Mod+3".action = focus-workspace (cfg.workspaces."03".name);
"Mod+4".action = focus-workspace (cfg.workspaces."04".name);
"Mod+5".action = focus-workspace (cfg.workspaces."05".name);
"Mod+6".action = focus-workspace (cfg.workspaces."06".name);
"Mod+7".action = focus-workspace (cfg.workspaces."07".name);
"Mod+8".action = focus-workspace (cfg.workspaces."08".name);
"Mod+9".action = focus-workspace (cfg.workspaces."09".name);
"Mod+0".action = focus-workspace (cfg.workspaces."10".name);
"Mod+Shift+1".action = move-column-to-workspace (cfg.workspaces."01".name);
"Mod+Shift+2".action = move-column-to-workspace (cfg.workspaces."02".name);
"Mod+Shift+3".action = move-column-to-workspace (cfg.workspaces."03".name);
"Mod+Shift+4".action = move-column-to-workspace (cfg.workspaces."04".name);
"Mod+Shift+5".action = move-column-to-workspace (cfg.workspaces."05".name);
"Mod+Shift+6".action = move-column-to-workspace (cfg.workspaces."06".name);
"Mod+Shift+7".action = move-column-to-workspace (cfg.workspaces."07".name);
"Mod+Shift+8".action = move-column-to-workspace (cfg.workspaces."08".name);
"Mod+Shift+9".action = move-column-to-workspace (cfg.workspaces."09".name);
"Mod+Shift+0".action = move-column-to-workspace (cfg.workspaces."10".name);
"Mod+asciicircum".action = focus-workspace (cfg.workspaces."99".name);
"Mod+Shift+asciicircum".action = move-column-to-workspace (cfg.workspaces."99".name);
"Mod+Tab".action = focus-workspace-previous;
"Mod+Comma".action = consume-or-expel-window-left;
"Mod+Period".action = consume-or-expel-window-right;
"Mod+W".action = switch-preset-column-width;
"Mod+Shift+W".action = switch-preset-window-height;
"Mod+Ctrl+W".action = reset-window-height;
"Mod+F".action = maximize-column;
"Mod+Shift+F".action = fullscreen-window;
"Mod+E".action = center-column;
"Mod+Minus".action = set-column-width "-10%";
"Mod+At".action = set-column-width "+10%";
"Mod+Shift+Minus".action = set-window-height "-10%";
"Mod+Shift+At".action = set-window-height "+10%";
"Mod+V".action = switch-focus-between-floating-and-tiling;
"Mod+Shift+V".action = toggle-window-floating;
"Mod+Shift+Space".action = toggle-window-floating; # Sway compat
"Print".action = screenshot;
"Ctrl+Print".action = screenshot-screen;
"Shift+Print".action = screenshot-window;
"Mod+Shift+E".action = quit;
};
};
};
}

View file

@ -1,4 +1,4 @@
{ pkgs, lib, options, config, osConfig, ... }:
{ pkgs, lib, config, ... }:
with lib;
let
cfg = config.programs.my-sway;
@ -44,7 +44,10 @@ let
${pkgs.grim}/bin/grim -g (${pkgs.slurp}/bin/slurp) - | ${pkgs.swappy}/bin/swappy -f -
'';
playerctl = "${pkgs.playerctl}/bin/playerctl";
ignored-devices = [ "Surface_Headphones" ];
playerctl = "${pkgs.playerctl}/bin/playerctl --ignore-player=${strings.concatStringsSep "," ignored-devices}";
in
{
# imports = [ ./ibus.nix ];
@ -63,23 +66,22 @@ in
wallpaper = mkOption {
type = types.oneOf [ types.path types.str ];
description = "Path to the wallpaper to be used";
default = config.linux.graphical.wallpaper;
default = "";
};
terminal = mkOption {
type = types.str;
description = "The command to the terminal emulator to be used";
default = lib.getExe config.linux.graphical.defaults.terminal.package;
default = "${config.programs.kitty.package}/bin/kitty";
};
browser = mkOption {
type = types.str;
description = "The command for the browser";
default = lib.getExe config.linux.graphical.defaults.webBrowser.package;
default = "${pkgs.firefox-wayland}/bin/firefox";
};
enableLaptop = lib.mkOption {
type = lib.types.bool;
description = "Whether to enable laptop-specific stuff";
default = true;
discord = mkOption {
type = types.nullOr types.str;
description = "The command for discord";
default = "${config.nki.programs.discord.package}/bin/discord";
};
lockCmd = mkOption {
@ -89,38 +91,40 @@ in
+ (if cfg.wallpaper == "" then "" else " -i ${cfg.wallpaper} -s fill")
+ " -l -k";
};
};
enableLaptopBars = mkOption {
type = types.bool;
description = "Whether to enable laptop-specific bars (battery)";
default = true;
};
enableMpd = mkOption {
type = types.bool;
description = "Whether to enable mpd on waybar";
default = false;
};
config.systemd.user.targets.sway-session = mkIf cfg.enable {
Unit.Before = [ "tray.target" "xwayland.target" "xdg-desktop-portal.service" "xdg-desktop-autostart.target" ];
Unit.Upholds = [ "waybar.service" ];
Unit.Wants = [ "xdg-desktop-autostart.target" ];
};
# Enable waybar
config.programs.my-waybar = mkIf cfg.enable {
enable = true;
fontSize = mkDefault cfg.fontSize;
enableLaptopBars = mkDefault cfg.enableLaptop;
terminal = mkDefault cfg.terminal;
waybar = {
makeBars = mkOption {
type = types.raw;
description = "Create bars with the barWith function, return a list of bars";
default = barWith: [ (barWith { }) ];
};
extraSettings = mkOption {
type = types.raw;
description = "Extra settings to be included with every default bar";
default = { };
};
extraStyle = mkOption {
type = types.str;
description = "Additional style for the default waybar";
default = "";
};
};
};
config.wayland.windowManager.sway = mkIf cfg.enable {
enable = true;
systemd.enable = true;
systemd.variables = options.wayland.windowManager.sway.systemd.variables.default ++ [
"PATH" # for portals
"XDG_DATA_DIRS" # For extra icons
"XDG_DATA_HOME" # For extra icons
] ++ lib.optionals osConfig.services.desktopManager.plasma6.enable [
"XDG_MENU_PREFIX"
];
# systemd.extraCommands = options.wayland.windowManager.sway.systemd.extraCommands.default
# ++ [
# "systemctl --user restart xdg-desktop-portal.service"
# ];
checkConfig = false; # Not working atm
config = {
### Inputs
#
@ -147,13 +151,20 @@ in
menu = "${pkgs.dmenu}/bin/dmenu_path | ${pkgs.bemenu}/bin/bemenu | ${pkgs.findutils}/bin/xargs swaymsg exec --";
# Startup
startup = [
# # Dex for autostart
# { command = "${pkgs.dex}/bin/dex -ae sway"; }
# # Waybar
# { command = "systemctl --user restart waybar"; always = true; }
# IME
{ command = "fcitx5"; }
];
# Dex for autostart
{ command = "${pkgs.dex}/bin/dex -ae sway"; }
# Waybar
{ command = "systemctl --user restart waybar"; always = true; }
# Startup programs
{ command = "${cfg.browser}"; }
{ command = "evolution"; } # Rely on system package with plugins
] ++ (if cfg.discord != null then [
{ command = "${cfg.discord}"; }
] ++ lib.lists.optional
(!pkgs.stdenv.isAarch64)
(
{ command = "${pkgs.unstable.premid}/bin/premid"; }
) else [ ]);
### Keybindings
#
@ -206,7 +217,6 @@ in
# Launcher
"${mod}+space" = "exec rofi -show drun";
"${mod}+tab" = "exec ${./rofi-window.py}";
"${mod}+shift+p" = "exec rofi-rbw-script";
} // {
## Splits
"${mod}+v" = "split v";
@ -280,9 +290,7 @@ in
# Assigning windows to workspaces
assigns = {
"${builtins.elemAt workspaces 0}" = [
{ app_id = "^firefox$"; }
{ app_id = "^librewolf$"; }
{ app_id = "^zen$"; }
{ class = "^firefox$"; }
];
"${builtins.elemAt workspaces 1}" = [
{ class = "^((d|D)iscord|((A|a)rm(c|C)ord))$"; }
@ -290,8 +298,6 @@ in
{ app_id = "VencordDesktop"; }
{ class = "vesktop"; }
{ app_id = "vesktop"; }
{ class = "Slack"; }
];
${extraWorkspaces.mail} = [
{ app_id = "thunderbird"; }
@ -306,7 +312,6 @@ in
let
criterias = [
{ app_id = ".*float.*"; }
{ app_id = "org\\.freedesktop\\.impl\\.portal\\.desktop\\..*"; }
{ class = ".*float.*"; }
{ title = "Extension: .*Bitwarden.*"; }
];
@ -336,27 +341,23 @@ in
swaynag.enable = true;
# Environment Variables
extraSessionCommands = ''
export MOZ_ENABLE_WAYLAND=1
export SDL_VIDEODRIVER=wayland
export QT_QPA_PLATFORM=wayland
export QT_WAYLAND_DISABLE_WINDOWDECORATION="1"
export QT_IM_MODULE=fcitx
export GTK_IM_MODULE=fcitx # Til text-input is merged
# export NIXOS_OZONE_WL=1 # Until text-input is merged
# export NIXOS_OZONE_WL=1 # Yeah this fucks up IMEs
'' + (if config.services.gnome-keyring.enable then ''
# gnome-keyring
if type gnome-keyring-daemon >/dev/null; then
eval `gnome-keyring-daemon`
export SSH_AUTH_SOCK
fi
'' else "") + lib.optionalString osConfig.services.desktopManager.plasma6.enable ''
export XDG_MENU_PREFIX=plasma-
'';
eval `gnome-keyring-daemon`
export SSH_AUTH_SOCK
'' else "");
# Extra
wrapperFeatures.base = true;
wrapperFeatures.gtk = true;
extraConfig =
(if cfg.enableLaptop then ''
(if cfg.enableLaptopBars then ''
# Lock screen on lid close
bindswitch lid:off exec ${cfg.lockCmd}
@ -382,16 +383,21 @@ in
'' + ''
# Enable portal stuff
exec ${pkgs.writeShellScript "start-portals.sh" ''
# Import the WAYLAND_DISPLAY env var from sway into the systemd user session.
dbus-update-activation-environment --systemd WAYLAND_DISPLAY XDG_CURRENT_DESKTOP=sway
# Stop any services that are running, so that they receive the new env var when they restart.
systemctl --user stop pipewire pipewire-media-session xdg-desktop-portal xdg-desktop-portal-wlr
systemctl --user start pipewire-media-session
''}
'';
};
config.services.swayidle = mkIf cfg.enable {
enable = true;
systemdTarget = "sway-session.target";
timeouts = [
# Lock after 15 minutes of idle
# { timeout = 15 * 60; command = cfg.lockCmd; }
{ timeout = 15 * 60; command = cfg.lockCmd; }
];
events = [
{ event = "lock"; command = cfg.lockCmd; }
@ -399,15 +405,381 @@ in
];
};
config.programs.waybar =
let
barWith = { showMedia ? true, showConnectivity ? true, extraSettings ? { }, ... }: (mkMerge [{
position = "top";
modules-left = [
"sway/workspaces"
"sway/mode"
"sway/window"
];
modules-center = [
];
modules-right =
lib.optional showMedia (if cfg.enableMpd then "mpd" else "custom/media")
++ [
"tray"
"pulseaudio"
] ++ lib.optionals showConnectivity [
"bluetooth"
"network"
] ++ [
"cpu"
"memory"
"temperature"
] ++ lib.optionals cfg.enableLaptopBars [ "battery" "battery#bat2" ]
++ [
"clock"
];
modules = {
"sway/workspaces" = {
format = "{name}";
};
"sway/mode" = {
format = "<span style=\"italic\">{}</span>";
};
"sway/window" = {
max-length = 70;
format = "{title}";
"rewrite" = {
"(.*) Mozilla Firefox" = "[🌎] $1";
"(.*) - Mozilla Thunderbird" = "[📧] $1";
"(.*) - Kakoune" = "[] $1";
"(.*) - fish" = "[>_] $1";
"(.*) - Discord" = "[🗨] $1";
# ArmCord thing
" Discord \\| (.*)" = "[🗨] $1";
"\\((\\d+)\\) Discord \\| (.*)" = "[🗨] {$1} $2";
};
};
"tray" = {
icon-size = 21;
spacing = 10;
};
"clock" = {
format = "{:📅 %Y-%m-%d | 🕰 %H:%M [%Z]}";
tooltip-format = "\n<span size='9pt' font='Noto Sans Mono CJK JP'>{calendar}</span>";
timezones = [
"Europe/Zurich"
"America/Toronto"
"Asia/Tokyo"
"Asia/Ho_Chi_Minh"
];
calendar = {
mode = "year";
mode-mon-col = 3;
weeks-pos = "right";
on-scroll = 1;
on-click-right = "mode";
format = {
months = "<span color='#ffead3'><b>{}</b></span>";
days = "<span color='#ecc6d9'><b>{}</b></span>";
weeks = "<span color='#99ffdd'><b>W{}</b></span>";
weekdays = "<span color='#ffcc66'><b>{}</b></span>";
today = "<span color='#ff6699'><b><u>{}</u></b></span>";
};
};
actions = {
on-click-middle = "mode";
on-click-right = "tz_up";
on-scroll-up = "shift_up";
on-scroll-down = "shift_down";
};
};
"cpu" = {
format = "{usage}% ";
};
"memory" = {
format = "{}% ";
};
"temperature" = {
# thermal-zone = 2;
# hwmon-path" = "/sys/class/hwmon/hwmon2/temp1_input";
critical-threshold = 80;
# format-critical = "{temperatureC}°C ";
format = "{temperatureC}°C ";
};
"backlight" = {
# device = "acpi_video1";
format = "{percent}% {icon}";
states = [ 0 50 ];
format-icons = [ "" "" ];
};
"battery" = mkIf cfg.enableLaptopBars {
states = {
good = 95;
warning = 30;
critical = 15;
};
format = "{capacity}% {icon}";
# format-good = ""; # An empty format will hide the module
# format-full = "";
format-icons = [ "" "" "" "" "" ];
};
"battery#bat2" = mkIf cfg.enableLaptopBars {
bat = "BAT2";
};
"network" = {
# interface = wlp2s0 # (Optional) To force the use of this interface
format-wifi = "{essid} ({signalStrength}%) ";
format-ethernet = "{ifname}: {ipaddr}/{cidr} ";
format-disconnected = "Disconnected ";
interval = 7;
};
"bluetooth" = {
format = " {status}";
format-connected = " {device_alias}";
format-connected-battery = " {device_alias} {device_battery_percentage}%";
# format-device-preference= [ "device1", "device2" ], // preference list deciding the displayed devic;
tooltip-format = "{controller_alias}\t{controller_address}\n\n{num_connections} connected";
tooltip-format-connected = "{controller_alias}\t{controller_address}\n\n{num_connections} connected\n\n{device_enumerate}";
tooltip-format-enumerate-connected = "{device_alias}\t{device_address}";
tooltip-format-enumerate-connected-battery = "{device_alias}\t{device_address}\t{device_battery_percentage}%";
on-click = "${pkgs.blueman}/bin/blueman-manager";
};
"pulseaudio" = {
# scroll-step = 1;
format = "{volume}% {icon}";
format-bluetooth = "{volume}% {icon}";
format-muted = "";
format-icons = {
headphones = "";
handsfree = "";
headset = "";
phone = "";
portable = "";
car = "";
default = [ "" "" ];
};
on-click = "${pkgs.pavucontrol}/bin/pavucontrol";
};
"mpd" = {
"format" = "{stateIcon} {consumeIcon}{randomIcon}{repeatIcon}{singleIcon}{artist} - {album} - {title} ({elapsedTime:%M:%S}/{totalTime:%M:%S}) 🎧";
"format-disconnected" = "Disconnected 🎧";
"format-stopped" = "{consumeIcon}{randomIcon}{repeatIcon}{singleIcon}Stopped 🎧";
"interval" = 2;
"consume-icons" = {
"on" = " "; # Icon shows only when "consume" is on
};
"random-icons" = {
"off" = "<span color=\"#f53c3c\"></span> "; # Icon grayed out when "random" is off;
"on" = " ";
};
"repeat-icons" = {
"on" = " ";
};
"single-icons" = {
"on" = "1 ";
};
"state-icons" = {
"paused" = "";
"playing" = "";
};
"tooltip-format" = "MPD (connected)";
"tooltip-format-disconnected" = "MPD (disconnected)";
"on-click" = "${pkgs.mpc_cli}/bin/mpc toggle";
"on-click-right" = "${pkgs.mpc_cli}/bin/mpc stop";
"on-click-middle" = "${cfg.terminal} --class=kitty_ncmpcpp ${pkgs.ncmpcpp}/bin/ncmpcpp";
};
"custom/media" = {
"format" = "{icon}{}";
"return-type" = "json";
"format-icons" = {
"Playing" = " ";
"Paused" = " ";
};
"max-length" = 80;
"exec" = "${playerctl} -a metadata --format '{\"text\": \"{{artist}} - {{markup_escape(title)}}\", \"tooltip\": \"{{playerName}} : {{markup_escape(title)}}\", \"alt\": \"{{status}}\", \"class\": \"{{status}}\"}' -F";
"on-click" = "${playerctl} play-pause";
};
};
}
cfg.waybar.extraSettings
extraSettings]);
in
mkIf cfg.enable {
enable = true;
systemd.enable = true;
settings = cfg.waybar.makeBars barWith;
style = ''
* {
border: none;
border-radius: 0;
font-family: monospace, 'Font Awesome 5', 'Symbols Nerd Font Mono', 'SFNS Display', Helvetica, Arial, sans-serif;
font-size: ${toString (cfg.fontSize * 1.1)}px;
min-height: 0;
}
window#waybar {
background: rgba(43, 48, 59, 0.8);
border-bottom: 3px solid rgba(100, 114, 125, 0.5);
color: #ffffff;
}
window#waybar.hidden {
opacity: 0.0;
}
/* https://github.com/Alexays/Waybar/wiki/FAQ#the-workspace-buttons-have-a-strange-hover-effect */
#workspaces button {
padding: 0 5px;
background: transparent;
color: #ffffff;
border-bottom: 3px solid transparent;
}
#workspaces button.focused {
background: #64727D;
border-bottom: 3px solid #ffffff;
}
#workspaces button.urgent {
background-color: #eb4d4b;
}
#window, #sway, #sway-window {
padding-left: 1em;
margin-bottom: 0.4em;
}
#mode {
background: #64727D;
border-bottom: 3px solid #ffffff;
}
/* #clock, #battery, #cpu, #memory, #temperature, #backlight, #network, #pulseaudio, #bluetooth, #custom-media, #tray, #mode, #idle_inhibitor, #mpd { */
.modules-right > * > * {
margin: 0.2em 0 0.4em 0;
padding: 0.2em 0.5em;
border: 1px solid rgba(0, 0, 0, 0.25);
border-radius: 0.3em;
}
.modules-right > *:not(:last-child) > * {
margin-right: 0.4em;
}
#clock {
background-color: #64727D;
}
#battery {
background-color: #ffffff;
color: #000000;
}
#battery.charging {
color: #ffffff;
background-color: #26A65B;
}
@keyframes blink {
to {
background-color: #ffffff;
color: #000000;
}
}
#battery.critical:not(.charging) {
background: #f53c3c;
color: #ffffff;
animation-name: blink;
animation-duration: 0.5s;
animation-timing-function: linear;
animation-iteration-count: infinite;
animation-direction: alternate;
}
#cpu {
background: #2ecc71;
color: #000000;
}
#memory {
background: #9b59b6;
}
#backlight {
background: #90b1b1;
}
#network {
background: #2980b9;
}
#network.disconnected {
background: #f53c3c;
}
#pulseaudio {
background: #f1c40f;
color: #000000;
}
#pulseaudio.muted {
background: #90b1b1;
}
#bluetooth {
background: DarkSlateBlue;
color: white;
}
#custom-media {
background: #66cc99;
color: #2a5c45;
}
.custom-spotify {
background: #66cc99;
}
.custom-vlc {
background: #ffa000;
}
#temperature {
background: #f0932b;
}
#temperature.critical {
background: #eb4d4b;
}
#tray {
background-color: #2980b9;
}
#idle_inhibitor {
background-color: #2d3436;
}
#idle_inhibitor.activated {
background-color: #ecf0f1;
color: #2d3436;
}
#mpd {
background-color: teal;
color: white;
}
'' + cfg.waybar.extraStyle;
};
config.home.packages = mkIf cfg.enable (with pkgs; [
# Needed for QT_QPA_PLATFORM
kdePackages.qtwayland
qt5.qtwayland
# For waybar
font-awesome
]);
config.programs.rofi = mkIf cfg.enable {
font = lib.mkForce "monospace ${toString cfg.fontSize}";
enable = true;
package = pkgs.rofi-wayland;
cycle = true;
font = "monospace ${toString cfg.fontSize}";
terminal = cfg.terminal;
theme = "Paper";
plugins = with pkgs; [ rofi-bluetooth rofi-calc rofi-rbw rofi-power-menu ];
};
}

View file

@ -1,427 +0,0 @@
{ lib, config, pkgs, ... }:
let
cfg = config.programs.my-waybar;
in
{
options.programs.my-waybar = {
enable = lib.mkEnableOption "custom configuration for waybar";
fontSize = lib.mkOption {
type = lib.types.float;
description = "The default font size";
};
terminal = lib.mkOption {
type = lib.types.str;
description = "The command to the terminal emulator to be used";
default = "${config.programs.kitty.package}/bin/kitty";
};
enableLaptopBars = lib.mkOption {
type = lib.types.bool;
description = "Whether to enable laptop-specific bars (battery)";
default = true;
};
enableMpd = lib.mkOption {
type = lib.types.bool;
description = "Whether to enable mpd on waybar";
default = false;
};
makeBars = lib.mkOption {
type = lib.types.raw;
description = "Create bars with the barWith function, return a list of bars";
default = barWith: [ (barWith { }) ];
};
extraSettings = lib.mkOption {
type = lib.types.listOf lib.types.raw;
description = "Extra settings to be included with every default bar";
default = [ ];
};
extraStyle = lib.mkOption {
type = lib.types.lines;
description = "Additional style for the default waybar";
default = "";
};
};
config.systemd.user.services.waybar = lib.mkIf cfg.enable {
Unit.Before = [ "tray.target" ];
};
config.programs.waybar =
let
barWith = { showMedia ? true, showConnectivity ? true, extraSettings ? { }, ... }: lib.mkMerge ([{
layer = "top";
position = "top";
modules-left = [
"sway/workspaces"
"sway/mode"
"sway/window"
"niri/workspaces"
"niri/window"
];
modules-center = [
];
modules-right =
lib.optional showMedia (if cfg.enableMpd then "mpd" else "custom/media")
++ [
"tray"
"pulseaudio"
] ++ lib.optionals showConnectivity [
"bluetooth"
"network"
] ++ [
"cpu"
"memory"
"temperature"
] ++ lib.optionals cfg.enableLaptopBars [ "battery" "battery#bat2" ]
++ [
"clock"
];
modules = {
"sway/workspaces" = {
format = "{name}";
};
"sway/mode" = {
format = "<span style=\"italic\">{}</span>";
};
"sway/window" = {
max-length = 70;
format = "{title}";
"rewrite" = {
"(.*) Mozilla Firefox" = "[🌎] $1";
"(.*) - Mozilla Thunderbird" = "[📧] $1";
"(.*) - Kakoune" = "[] $1";
"(.*) - fish" = "[>_] $1";
"(.*) - Discord" = "[🗨] $1";
# ArmCord thing
" Discord \\| (.*)" = "[🗨] $1";
"\\((\\d+)\\) Discord \\| (.*)" = "[🗨] {$1} $2";
};
};
"niri/window" = {
format = "{title}";
"rewrite" = {
"(.*) Mozilla Firefox" = "[🌎] $1";
"(.*) - Mozilla Thunderbird" = "[📧] $1";
"(.*) - Kakoune" = "[] $1";
"(.*) - fish" = "[>_] $1";
"(.*) - Discord" = "[🗨] $1";
# ArmCord thing
" Discord \\| (.*)" = "[🗨] $1";
"\\((\\d+)\\) Discord \\| (.*)" = "[🗨] {$1} $2";
};
};
"tray" = {
icon-size = 21;
spacing = 10;
};
"clock" = {
# format = "{:📅 %Y-%m-%d | 🕰️ %H:%M [%Z]}";
format = "📅 {0:%Y-%m-%d} | 🕰 {0:%H:%M [%Z]}";
tooltip-format = "\n<span size='9pt' font_family='Noto Sans Mono CJK JP'>{calendar}</span>";
timezones = [
"Europe/Zurich"
"America/Toronto"
"Asia/Tokyo"
"Asia/Ho_Chi_Minh"
];
calendar = {
mode = "year";
mode-mon-col = 3;
weeks-pos = "right";
on-scroll = 1;
on-click-right = "mode";
format = {
months = "<span color='#ffead3'><b>{}</b></span>";
days = "<span color='#ecc6d9'><b>{}</b></span>";
weeks = "<span color='#99ffdd'><b>W{}</b></span>";
weekdays = "<span color='#ffcc66'><b> </b></span>"; # See https://github.com/Alexays/Waybar/issues/3132
today = "<span color='#ff6699'><b><u>{}</u></b></span>";
};
};
actions = {
on-click-middle = "mode";
on-click-right = "tz_up";
on-scroll-up = "shift_up";
on-scroll-down = "shift_down";
};
};
"cpu" = {
format = "{usage}% ";
};
"memory" = {
format = "{}% ";
};
"temperature" = {
# thermal-zone = 2;
# hwmon-path" = "/sys/class/hwmon/hwmon2/temp1_input";
critical-threshold = 80;
# format-critical = "{temperatureC}°C ";
format = "{temperatureC}°C ";
};
"backlight" = {
# device = "acpi_video1";
format = "{percent}% {icon}";
states = [ 0 50 ];
format-icons = [ "" "" ];
};
"battery" = lib.mkIf cfg.enableLaptopBars {
states = {
good = 95;
warning = 30;
critical = 15;
};
format = "{capacity}% {icon}";
# format-good = ""; # An empty format will hide the module
# format-full = "";
format-icons = [ "" "" "" "" "" ];
};
"battery#bat2" = lib.mkIf cfg.enableLaptopBars {
bat = "BAT2";
};
"network" = {
# interface = wlp2s0 # (Optional) To force the use of this interface
format-wifi = "{essid} ({signalStrength}%) ";
format-ethernet = "{ifname}: {ipaddr}/{cidr} ";
format-disconnected = "Disconnected ";
interval = 7;
};
"bluetooth" = {
format = " {status}";
format-connected = " {device_alias}";
format-connected-battery = " {device_alias} {device_battery_percentage}%";
# format-device-preference= [ "device1", "device2" ], // preference list deciding the displayed devic;
tooltip-format = "{controller_alias}\t{controller_address}\n\n{num_connections} connected";
tooltip-format-connected = "{controller_alias}\t{controller_address}\n\n{num_connections} connected\n\n{device_enumerate}";
tooltip-format-enumerate-connected = "{device_alias}\t{device_address}";
tooltip-format-enumerate-connected-battery = "{device_alias}\t{device_address}\t{device_battery_percentage}%";
on-click = "${pkgs.blueman}/bin/blueman-manager";
};
"pulseaudio" = {
# scroll-step = 1;
format = "{volume}% {icon}";
format-bluetooth = "{volume}% {icon}";
format-muted = "";
format-icons = {
headphones = "";
handsfree = "";
headset = "";
phone = "";
portable = "";
car = "";
default = [ "" "" ];
};
on-click = "${pkgs.pavucontrol}/bin/pavucontrol";
};
"mpd" = {
"format" = "{stateIcon} {consumeIcon}{randomIcon}{repeatIcon}{singleIcon}{artist} - {album} - {title} ({elapsedTime:%M:%S}/{totalTime:%M:%S}) 🎧";
"format-disconnected" = "Disconnected 🎧";
"format-stopped" = "{consumeIcon}{randomIcon}{repeatIcon}{singleIcon}Stopped 🎧";
"interval" = 2;
"consume-icons" = {
"on" = " "; # Icon shows only when "consume" is on
};
"random-icons" = {
"off" = "<span color=\"#f53c3c\"></span> "; # Icon grayed out when "random" is off;
"on" = " ";
};
"repeat-icons" = {
"on" = " ";
};
"single-icons" = {
"on" = "1 ";
};
"state-icons" = {
"paused" = "";
"playing" = "";
};
"tooltip-format" = "MPD (connected)";
"tooltip-format-disconnected" = "MPD (disconnected)";
"on-click" = "${pkgs.mpc_cli}/bin/mpc toggle";
"on-click-right" = "${pkgs.mpc_cli}/bin/mpc stop";
"on-click-middle" = "${cfg.terminal} --class=kitty_ncmpcpp ${pkgs.ncmpcpp}/bin/ncmpcpp";
};
"custom/media" = {
"format" = "{icon}{}";
"return-type" = "json";
"format-icons" = {
"Playing" = " ";
"Paused" = " ";
};
"max-length" = 80;
"exec" = "${lib.getExe pkgs.playerctl} -a metadata --format '{\"text\": \"{{artist}} - {{markup_escape(title)}}\", \"tooltip\": \"{{playerName}} : {{markup_escape(title)}}\", \"alt\": \"{{status}}\", \"class\": \"{{status}}\"}' -F";
"on-click" = "${lib.getExe pkgs.playerctl} play-pause";
};
};
}] ++
cfg.extraSettings
++ [ extraSettings ]);
in
lib.mkIf cfg.enable {
enable = true;
systemd.enable = true;
systemd.target = "sway-session.target";
settings = cfg.makeBars barWith;
style = ''
* {
border: none;
border-radius: 0;
font-family: monospace, 'Font Awesome 5', 'Symbols Nerd Font Mono', 'SFNS Display', Helvetica, Arial, sans-serif;
font-size: ${toString (cfg.fontSize * 1.1)}px;
min-height: 0;
}
window#waybar {
background: rgba(43, 48, 59, 0.8);
border-bottom: 3px solid rgba(100, 114, 125, 0.5);
color: #ffffff;
}
window#waybar.hidden {
opacity: 0.0;
}
/* https://github.com/Alexays/Waybar/wiki/FAQ#the-workspace-buttons-have-a-strange-hover-effect */
#workspaces button {
padding: 0 5px;
background: transparent;
color: #ffffff;
border-bottom: 3px solid transparent;
}
#workspaces button.focused {
background: #64727D;
border-bottom: 3px solid #ffffff;
}
#workspaces button.urgent {
background-color: #eb4d4b;
}
#window, #sway, #sway-window {
padding-left: 1em;
margin-bottom: 0.4em;
}
#mode {
background: #64727D;
border-bottom: 3px solid #ffffff;
}
/* #clock, #battery, #cpu, #memory, #temperature, #backlight, #network, #pulseaudio, #bluetooth, #custom-media, #tray, #mode, #idle_inhibitor, #mpd { */
.modules-right > * > * {
margin: 0.2em 0 0.4em 0;
padding: 0.2em 0.5em;
border: 1px solid rgba(0, 0, 0, 0.25);
border-radius: 0.3em;
}
.modules-right > *:not(:last-child) > * {
margin-right: 0.4em;
}
#clock {
background-color: #64727D;
}
#battery {
background-color: #ffffff;
color: #000000;
}
#battery.charging {
color: #ffffff;
background-color: #26A65B;
}
@keyframes blink {
to {
background-color: #ffffff;
color: #000000;
}
}
#battery.critical:not(.charging) {
background: #f53c3c;
color: #ffffff;
animation-name: blink;
animation-duration: 0.5s;
animation-timing-function: linear;
animation-iteration-count: infinite;
animation-direction: alternate;
}
#cpu {
background: #2ecc71;
color: #000000;
}
#memory {
background: #9b59b6;
}
#backlight {
background: #90b1b1;
}
#network {
background: #2980b9;
}
#network.disconnected {
background: #f53c3c;
}
#pulseaudio {
background: #f1c40f;
color: #000000;
}
#pulseaudio.muted {
background: #90b1b1;
}
#bluetooth {
background: DarkSlateBlue;
color: white;
}
#custom-media {
background: #66cc99;
color: #2a5c45;
}
.custom-spotify {
background: #66cc99;
}
.custom-vlc {
background: #ffa000;
}
#temperature {
background: #f0932b;
}
#temperature.critical {
background: #eb4d4b;
}
#tray {
background-color: #2980b9;
}
#idle_inhibitor {
background-color: #2d3436;
}
#idle_inhibitor.activated {
background-color: #ecf0f1;
color: #2d3436;
}
#mpd {
background-color: teal;
color: white;
}
'' + cfg.extraStyle;
};
}

View file

@ -4,15 +4,12 @@ let
name = "openconnect-epfl";
runtimeInputs = with pkgs; [ openconnect rbw ];
text = ''
METHOD="Microsoft Entra ID"
RBW_ENTRY="EPFL Microsoft Auth"
GASPAR_PASSWORD=$(rbw get "$RBW_ENTRY")
GASPAR_TOKEN=$(rbw code "$RBW_ENTRY")
GASPAR_PASSWORD=$(rbw get gaspar)
GASPAR_TOKEN=$(rbw code gaspar)
printf "\n%s\n%s\n%s\n" "$METHOD" "$GASPAR_PASSWORD" "$GASPAR_TOKEN" | command sudo openconnect \
printf "%s\n%s" "$GASPAR_PASSWORD" "$GASPAR_TOKEN" | sudo openconnect \
--passwd-on-stdin \
-u "pham" \
--useragent='AnyConnect' \
-u pham \
"https://vpn.epfl.ch"
'';
};

View file

@ -29,10 +29,12 @@
# Graphical set up
linux.graphical.type = "wayland";
linux.graphical.wallpaper = ./images/wallpaper_0.png;
linux.graphical.defaults.webBrowser.package = pkgs.librewolf;
linux.graphical.defaults.webBrowser = "librewolf.desktop";
# Enable sway
programs.my-sway.enable = true;
programs.my-sway.fontSize = 14.0;
programs.my-sway.terminal = "${config.programs.kitty.package}/bin/kitty";
programs.my-sway.browser = "librewolf";
wayland.windowManager.sway.config = {
# Keyboard support
input."*".xkb_layout = "jp";
@ -46,25 +48,6 @@
tap = "enabled";
};
};
programs.my-niri.enable = true;
programs.niri.settings = {
input.keyboard.xkb.options = "ctrl:swapcaps";
};
programs.my-waybar.extraSettings =
let
change-mode = pkgs.writeScript "change-mode" ''
#!/usr/bin/env ${lib.getExe pkgs.fish}
set -ax PATH ${lib.getBin pkgs.power-profiles-daemon} ${lib.getBin pkgs.rofi} ${lib.getBin pkgs.ripgrep}
set profiles (powerprofilesctl list | rg "^[ *] (\S+):" -r '$1')
set selected_index (math (contains -i (powerprofilesctl get) $profiles) - 1)
set new_profile (printf "%s\n" $profiles | rofi -dmenu -p "Switch to power profile" -a $selected_index)
powerprofilesctl set $new_profile
'';
in
[{
modules."battery"."on-click" = change-mode;
}];
# input-remapping
xdg.configFile."autostart/input-remapper-autoload.desktop".source =
@ -78,32 +61,16 @@
# Multiple screen setup
services.kanshi = with config.common.monitors; {
enable = true;
settings = [
{
profile.name = "undocked";
profile.outputs = [{ criteria = "eDP-1"; }];
}
{
profile.name = "work-both";
profile.outputs = [
{
criteria = "eDP-1";
position = "0,${toString (builtins.floor ((2160 / work.scale - 1200) + 1200 / 3))}";
status = "enable";
}
{ criteria = work.name; position = "1920,0"; }
];
}
{
profile.name = "work-one";
profile.outputs = [
{
criteria = "eDP-1";
status = "disable";
}
];
}
{ output.criteria = config.common.monitors.work.name; }
profiles.undocked.outputs = [{
criteria = "eDP-1";
}];
profiles.work-both.outputs = [
{ criteria = "eDP-1"; position = "0,${toString (builtins.floor ((2160 / work.scale - 1200) + 1200 / 3))}"; status = "enable"; }
{ criteria = work.name; position = "1920,0"; }
];
profiles.work-one.outputs = [
{ criteria = "eDP-1"; status = "disable"; }
{ criteria = config.common.monitors.work.name; }
];
};

View file

@ -1,20 +1,5 @@
{ pkgs, config, lib, ... }:
let
iio-sway = pkgs.stdenv.mkDerivation {
name = "iio-sway";
version = "0.0.1";
src = pkgs.fetchFromGitHub {
owner = "okeri";
repo = "iio-sway";
rev = "e07477d1b2478fede1446e97424a94c80767819d";
hash = "sha256-JGacKajslCOvd/BFfFSf7s1/hgF6rJqJ6H6xNnsuMb4=";
};
buildInputs = with pkgs; [ dbus ];
nativeBuildInputs = with pkgs; [ meson ninja pkg-config ];
meta.mainProgram = "iio-sway";
};
in
{
imports = [
# Common configuration
@ -44,12 +29,12 @@ in
# Graphical set up
linux.graphical.type = "wayland";
linux.graphical.wallpaper = ./images/wallpaper_0.png;
linux.graphical.startup = with pkgs; [ zen-browser-bin thunderbird vesktop slack ];
linux.graphical.defaults.webBrowser.package = pkgs.zen-browser-bin;
linux.graphical.defaults.webBrowser.desktopFile = "zen.desktop";
linux.graphical.defaults.webBrowser = "librewolf.desktop";
# Enable sway
programs.my-sway.enable = true;
programs.my-sway.fontSize = 14.0;
programs.my-sway.terminal = "${config.programs.kitty.package}/bin/kitty";
programs.my-sway.browser = "librewolf";
wayland.windowManager.sway.config = {
# Keyboard support
input."*".xkb_layout = "jp";
@ -64,25 +49,25 @@ in
startup = [
# rotation
{ command = "${lib.getExe iio-sway}"; }
(
let
iio-sway = pkgs.stdenv.mkDerivation {
name = "iio-sway";
version = "0.0.1";
src = pkgs.fetchFromGitHub {
owner = "okeri";
repo = "iio-sway";
rev = "e07477d1b2478fede1446e97424a94c80767819d";
hash = "sha256-JGacKajslCOvd/BFfFSf7s1/hgF6rJqJ6H6xNnsuMb4=";
};
buildInputs = with pkgs; [ dbus ];
nativeBuildInputs = with pkgs; [ meson ninja pkg-config ];
};
in
{ command = "${iio-sway}/bin/iio-sway"; }
)
];
};
programs.my-niri.enable = true;
# Assign some of the workspaces to big screen
programs.my-niri.workspaces = lib.genAttrs [ "06" "07" "08" "09" "10" ] (_: { monitor = config.common.monitors.work.name; });
programs.niri.settings = {
# input.keyboard.xkb.options = "ctrl:swapcaps";
input.mouse = lib.mkForce {
# Make M575 fast for now
accel-profile = "adaptive";
accel-speed = 0.4;
};
input.touch.map-to-output = "eDP-1";
switch-events = with config.lib.niri.actions; {
tablet-mode-on.action = spawn "systemctl" "--user" "kill" "--signal" "SIGUSR2" "wvkbd";
tablet-mode-off.action = spawn "systemctl" "--user" "kill" "--signal" "SIGUSR1" "wvkbd";
};
};
## Virtual keyboard
systemd.user.services.wvkbd = {
Unit = {
@ -111,25 +96,14 @@ in
# Multiple screen setup
services.kanshi = with config.common.monitors; {
enable = true;
settings = [
{
profile.name = "undocked";
profile.outputs = [{ criteria = "eDP-1"; }];
}
{
profile.name = "work-both";
profile.outputs = [
{ criteria = "eDP-1"; position = "0,${toString (builtins.floor ((2160 / work.scale - 1200) + 1200 / 3))}"; status = "enable"; }
{ criteria = work.name; position = "1920,0"; }
];
}
{
profile.name = "work-one";
profile.outputs = [
{ criteria = "eDP-1"; status = "disable"; }
{ criteria = work.name; }
];
}
profiles.undocked.outputs = [{ criteria = "LVDS-1"; }];
profiles.work-both.outputs = [
{ criteria = "eDP-1"; position = "0,${toString (builtins.floor ((2160 / work.scale - 1200) + 1200 / 3))}"; status = "enable"; }
{ criteria = work.name; position = "1920,0"; }
];
profiles.work-one.outputs = [
{ criteria = "eDP-1"; status = "disable"; }
{ criteria = config.common.monitors.work.name; }
];
};

View file

@ -5,28 +5,28 @@ let
osu-pkg = with pkgs; with lib;
appimageTools.wrapType2 rec {
pname = "osu-lazer-bin";
version = "2025.101.0";
src = fetchurl {
version = "2024.312.1";
src = pkgs.fetchurl {
url = "https://github.com/ppy/osu/releases/download/${version}/osu.AppImage";
hash = "sha256-GsnTxVpNk2RXHLET6Ugv0/ZOlq8RUkw2ZXqRjkU+dzw=";
hash = "sha256-1dzgs1p3/pf4eCdKvQ9JxowN+oBPBNaZv5e6qHeFPEM=";
};
extraPkgs = pkgs: with pkgs; [ icu ];
extraInstallCommands =
let contents = appimageTools.extract { inherit pname version src; };
in
''
mv -v $out/bin/${pname} $out/bin/osu\!
mv -v $out/bin/${pname}-${version} $out/bin/osu\!
install -m 444 -D ${contents}/osu\!.desktop -t $out/share/applications
for i in 16 32 48 64 96 128 256 512 1024; do
install -D ${contents}/osu.png $out/share/icons/hicolor/''${i}x$i/apps/osu.png
install -D ${contents}/osu\!.png $out/share/icons/hicolor/''${i}x$i/apps/osu\!.png
done
'';
};
in
{
home.packages = [ osu-pkg ];
xdg.mimeApps.defaultApplications."x-scheme-handler/osu" = "osu!.desktop";
# home.packages = [ pkgs.osu-lazer ];
}

View file

@ -70,7 +70,7 @@
services.printing.drivers = with pkgs; [ epfl-cups-drivers ];
# Enable touchpad support (enabled default in most desktopManager).
services.libinput.enable = true;
services.xserver.libinput.enable = true;
# Keyboard
services.input-remapper.enable = true;
services.input-remapper.serviceWantedBy = [ "multi-user.target" ];

View file

@ -12,8 +12,8 @@ let
images = {
postgresql = mkImage {
imageName = "postgres";
finalImageTag = "16-alpine";
imageDigest = "sha256:b40547ea0c7bcb401d8f11c6a233ebe65e2067e5966e54ccf9b03c5f01c2957c";
finalImageTag = "12-alpine";
imageDigest = "sha256:f52ffee699232c84d820c35c28656363f4fda6a3e3e934b83f4e5e1898e2bdfa";
};
redis = mkImage {
imageName = "redis";
@ -22,8 +22,8 @@ let
};
authentik = mkImage {
imageName = "ghcr.io/goauthentik/server";
finalImageTag = "2024.1.0.4";
imageDigest = "sha256:5843c56a56be58dcee8927c705f8c9def0af7189511c0e8e687d430f543b0f6d";
finalImageTag = "2024.2.2";
imageDigest = "sha256:29417285cf1e1a0f2dd23ae10e8e299e4acba9cf6376bf184e05a290bc578fbb";
};
};
authentikEnv = pkgs.writeText "authentik.env" ''
@ -48,14 +48,7 @@ in
};
config = mkIf cfg.enable {
systemd.services.arion-authentik = {
serviceConfig.EnvironmentFile = cfg.envFile;
serviceConfig.Type = "notify";
serviceConfig.NotifyAccess = "all";
script = lib.mkBefore ''
${lib.getExe pkgs.wait4x} http http://127.0.0.1:${toString cfg.port} --expect-status-code 200 -t 0 -q -- systemd-notify --ready &
'';
};
systemd.services.arion-authentik.serviceConfig.EnvironmentFile = cfg.envFile;
virtualisation.arion.projects.authentik.settings = {
services.postgresql.service = {
image = images.postgresql;
@ -105,7 +98,6 @@ in
ports = [
"127.0.0.1:${toString cfg.port}:9000"
];
};
services.worker.service = {
image = images.authentik;
@ -124,7 +116,6 @@ in
AUTHENTIK_POSTGRESQL__NAME = "authentik";
};
env_file = [ cfg.envFile "${authentikEnv}" ];
user = "root";
};
docker-compose.volumes = {
database.driver = "local";

View file

@ -74,8 +74,6 @@ with lib;
global.port = instance.port;
global.allow_registration = instance.allow_registration;
global.database_path = "/mnt/data/${srvName}/";
global.well_known_client = "https://${instance.host}";
global.well_known_server = "${instance.host}:443";
});
in
{
@ -105,7 +103,7 @@ with lib;
"@system-service"
"~@privileged"
];
# StateDirectory = "/mnt/data/${srvName}";
StateDirectory = "/mnt/data/${srvName}";
BindPaths = [ "/mnt/data/${srvName}" ];
ExecStart = "${cfg.package}/bin/conduit";
Restart = "on-failure";
@ -116,12 +114,61 @@ with lib;
))
cfg.instances);
# Serving .well-known files
# This is a single .well-known/matrix/server file that points to the server,
# which is NOT on port 8448 since Cloudflare doesn't allow us to route HTTPS
# through that port.
config.services.nginx = mkIf cfg.enable
{
enable = true;
virtualHosts = lib.attrsets.mapAttrs'
(name: instance: lib.attrsets.nameValuePair "conduit-${name}-well-known" {
listen = [{ addr = "127.0.0.1"; port = instance.well-known_port; }];
# Check https://github.com/spantaleev/matrix-docker-ansible-deploy/blob/master/docs/configuring-well-known.md
# for the file structure.
root = pkgs.symlinkJoin
{
name = "well-known-files-for-conduit-${name}";
paths = [
(pkgs.writeTextDir ".well-known/matrix/client" (builtins.toJSON {
"m.homeserver".base_url = "https://${instance.host}";
"org.matrix.msc3575.proxy".url = "https://${instance.host}";
}))
(pkgs.writeTextDir ".well-known/matrix/server" (builtins.toJSON {
"m.server" = "${instance.host}:443";
}))
];
};
extraConfig =
# Enable CORS from anywhere since we want all clients to find us out
''
add_header 'Access-Control-Allow-Origin' "*";
'' +
# Force returning values to be JSON data
''
default_type application/json;
'';
})
cfg.instances;
};
config.cloud.traefik.hosts = mkIf cfg.enable (
(lib.attrsets.mapAttrs'
(name: instance: lib.attrsets.nameValuePair "conduit-${name}" ({
inherit (instance) host port noCloudflare;
}))
cfg.instances)
// (lib.attrsets.mapAttrs'
(name: instance: lib.attrsets.nameValuePair "conduit-${name}-well-known" (
let
server_name = if instance.server_name == "" then instance.host else instance.server_name;
in
{
port = instance.well-known_port;
filter = "Host(`${server_name}`) && PathPrefix(`/.well-known`)";
}
))
cfg.instances)
);
}

View file

@ -33,7 +33,7 @@ with lib; {
{
systemd.services.heisenbridge = {
description = "Matrix<->IRC bridge";
requires = [ "matrix-conduit-nkagami.service" "matrix-synapse.service" ]; # So the registration file can be used by Synapse
requires = [ "matrix-synapse.service" ]; # So the registration file can be used by Synapse
wantedBy = [ "multi-user.target" ];
serviceConfig = rec {

View file

@ -4,7 +4,6 @@ let
cfg = config.cloud.gotosocial;
dbUser = "gotosocial";
storageLocation = "/mnt/data/gotosocial";
in
{
options.cloud.gotosocial = {
@ -75,9 +74,6 @@ in
# Media
media-emoji-remote-max-size = 256 * 1024 /* bytes */;
media-emoji-local-max-size = 256 * 1024 /* bytes */;
media-remote-cache-days = 7;
media-cleanup-from = "00:00";
media-cleanup-every = "24h";
# OIDC
oidc-enabled = true;
oidc-idp-name = "DTTH";
@ -86,22 +82,10 @@ in
http-client.block-ips = [ "11.0.0.0/24" ];
# Advanced
advanced-rate-limit-requests = 0;
# Storage
storage-backend = "local";
storage-local-base-path = "${storageLocation}/storage";
# instance-inject-mastodon-version = true;
};
};
systemd.services.gotosocial.requires = mkAfter [ "postgresql.service" "arion-authentik.service" ];
systemd.services.gotosocial.after = mkAfter [ "postgresql.service" "arion-authentik.service" ];
systemd.services.gotosocial.unitConfig = {
RequiresMountsFor = [ storageLocation ];
ReadWritePaths = [ storageLocation ];
};
systemd.tmpfiles.settings."10-gotosocial".${storageLocation}.d = {
user = dbUser;
group = dbUser;
mode = "0700";
};
systemd.services.gotosocial.requires = mkAfter [ "minio.service" "postgresql.service" ];
systemd.services.gotosocial.after = mkAfter [ "minio.service" "postgresql.service" ];
};
}

View file

@ -17,7 +17,6 @@ index d218dbc1f..24c0505e3 100644
/**
* The product name
*/
@Public
- public APP_NAME = "Outline";
+ public APP_NAME = "DTTH Wiki";

View file

@ -1,183 +0,0 @@
commit 8c7f8c28fabc174a71499a4737579b24b5c4b244
Author: Natsu Kagami <nki@nkagami.me>
Date: Mon Oct 21 02:17:36 2024 +0200
Support R2
diff --git a/.env.sample b/.env.sample
index eb57ad85c..94ffcee07 100644
--- a/.env.sample
+++ b/.env.sample
@@ -66,6 +66,8 @@ AWS_S3_UPLOAD_BUCKET_URL=http://s3:4569
AWS_S3_UPLOAD_BUCKET_NAME=bucket_name_here
AWS_S3_FORCE_PATH_STYLE=true
AWS_S3_ACL=private
+AWS_S3_R2=true
+AWS_S3_R2_PUBLIC_URL=http://s3:4569
# AUTHENTICATION
diff --git a/app/utils/files.ts b/app/utils/files.ts
index 6607a6b12..5138f68ad 100644
--- a/app/utils/files.ts
+++ b/app/utils/files.ts
@@ -63,8 +63,13 @@ export const uploadFile = async (
xhr.addEventListener("loadend", () => {
resolve(xhr.readyState === 4 && xhr.status >= 200 && xhr.status < 400);
});
- xhr.open("POST", data.uploadUrl, true);
- xhr.send(formData);
+ xhr.open(data.method, data.uploadUrl, true);
+ xhr.setRequestHeader("Content-Type", file.type);
+ if (data.method === "POST") {
+ xhr.send(formData);
+ } else {
+ xhr.send(file);
+ }
});
if (!success) {
diff --git a/server/env.ts b/server/env.ts
index 5b420f2e1..4ea1e8d3c 100644
--- a/server/env.ts
+++ b/server/env.ts
@@ -519,6 +519,14 @@ export class Environment {
environment.AWS_S3_UPLOAD_BUCKET_NAME
);
+ @IsOptional()
+ public AWS_S3_R2 = this.toBoolean(environment.AWS_S3_R2 ?? "false");
+
+ @IsOptional()
+ public AWS_S3_R2_PUBLIC_URL = this.toOptionalString(
+ environment.AWS_S3_R2_PUBLIC_URL
+ );
+
/**
* Whether to force path style URLs for S3 objects, this is required for some
* S3-compatible storage providers.
diff --git a/server/routes/api/attachments/attachments.ts b/server/routes/api/attachments/attachments.ts
index 5e6c27594..b7620f440 100644
--- a/server/routes/api/attachments/attachments.ts
+++ b/server/routes/api/attachments/attachments.ts
@@ -3,6 +3,7 @@ import { v4 as uuidv4 } from "uuid";
import { AttachmentPreset } from "@shared/types";
import { bytesToHumanReadable } from "@shared/utils/files";
import { AttachmentValidation } from "@shared/validations";
+import env from "@server/env";
import { AuthorizationError, ValidationError } from "@server/errors";
import auth from "@server/middlewares/authentication";
import { rateLimiter } from "@server/middlewares/rateLimiter";
@@ -90,16 +91,30 @@ router.post(
{ transaction }
);
- const presignedPost = await FileStorage.getPresignedPost(
- key,
- acl,
- maxUploadSize,
- contentType
- );
+ let uploadUrl;
+ let method;
+ let presignedPost = {
+ fields: {},
+ };
+ if (env.AWS_S3_R2) {
+ uploadUrl = await FileStorage.getPresignedPut(key);
+ method = "PUT";
+ } else {
+ uploadUrl = FileStorage.getUploadUrl();
+ method = "POST";
+
+ presignedPost = await FileStorage.getPresignedPost(
+ key,
+ acl,
+ maxUploadSize,
+ contentType
+ );
+ }
ctx.body = {
data: {
- uploadUrl: FileStorage.getUploadUrl(),
+ uploadUrl,
+ method,
form: {
"Cache-Control": "max-age=31557600",
"Content-Type": contentType,
diff --git a/server/storage/files/BaseStorage.ts b/server/storage/files/BaseStorage.ts
index ce0287ebc..a1931c83d 100644
--- a/server/storage/files/BaseStorage.ts
+++ b/server/storage/files/BaseStorage.ts
@@ -26,6 +26,8 @@ export default abstract class BaseStorage {
contentType: string
): Promise<Partial<PresignedPost>>;
+ public abstract getPresignedPut(key: string): Promise<string>;
+
/**
* Returns a promise that resolves with a stream for reading a file from the storage provider.
*
diff --git a/server/storage/files/LocalStorage.ts b/server/storage/files/LocalStorage.ts
index 83cf98c50..324e60dd9 100644
--- a/server/storage/files/LocalStorage.ts
+++ b/server/storage/files/LocalStorage.ts
@@ -30,6 +30,10 @@ export default class LocalStorage extends BaseStorage {
});
}
+ public async getPresignedPut(key: string) {
+ return this.getUrlForKey(key);
+ }
+
public getUploadUrl() {
return "/api/files.create";
}
diff --git a/server/storage/files/S3Storage.ts b/server/storage/files/S3Storage.ts
index a42442e0c..d55ef5472 100644
--- a/server/storage/files/S3Storage.ts
+++ b/server/storage/files/S3Storage.ts
@@ -4,6 +4,7 @@ import {
S3Client,
DeleteObjectCommand,
GetObjectCommand,
+ PutObjectCommand,
ObjectCannedACL,
} from "@aws-sdk/client-s3";
import { Upload } from "@aws-sdk/lib-storage";
@@ -58,6 +59,16 @@ export default class S3Storage extends BaseStorage {
return createPresignedPost(this.client, params);
}
+ public async getPresignedPut(key: string) {
+ const params = {
+ Bucket: env.AWS_S3_UPLOAD_BUCKET_NAME,
+ Key: key,
+ };
+
+ const command = new PutObjectCommand(params);
+ return await getSignedUrl(this.client, command, { expiresIn: 3600 });
+ }
+
private getPublicEndpoint(isServerUpload?: boolean) {
if (env.AWS_S3_ACCELERATE_URL) {
return env.AWS_S3_ACCELERATE_URL;
@@ -137,10 +148,17 @@ export default class S3Storage extends BaseStorage {
);
}
+ public getR2ObjectUrl = async (key: string) =>
+ env.AWS_S3_R2_PUBLIC_URL + "/" + key;
+
public getSignedUrl = async (
key: string,
expiresIn = S3Storage.defaultSignedUrlExpires
) => {
+ if (env.AWS_S3_R2) {
+ return this.getR2ObjectUrl(key);
+ }
+
const isDocker = env.AWS_S3_UPLOAD_BUCKET_URL.match(/http:\/\/s3:/);
const params = {
Bucket: this.getBucket(),

View file

@ -31,13 +31,6 @@ in
ensureDatabases = cfg.databases;
ensureUsers = (map userFromDatabase cfg.databases);
dataDir = "/mnt/data/postgresql/${config.services.postgresql.package.psqlSchema}";
};
config.systemd.services.postgresql.serviceConfig = {
StateDirectory = "postgresql postgresql ${config.services.postgresql.dataDir}";
StateDirectoryMode = "0750";
};
# Backup settings

View file

@ -12,6 +12,7 @@ let
users.users.${config.common.linux.username}.extraGroups = [ "adbusers" ];
};
ios = { config, pkgs, ... }: mkIf config.common.linux.enable {
services.avahi.enable = true;
services.usbmuxd.enable = true;
services.usbmuxd.package = pkgs.usbmuxd2;
environment.systemPackages = with pkgs; [
@ -26,38 +27,22 @@ let
};
};
graphics = { config, pkgs, ... }: {
hardware.graphics.enable = true;
hardware.graphics.enable32Bit = true;
# Monitor backlight
hardware.i2c.enable = true;
services.ddccontrol.enable = true;
environment.systemPackages = [ pkgs.luminance pkgs.ddcutil ];
};
accounts = { pkgs, ... }: mkIf (config.common.linux.enable && !pkgs.stdenv.isAarch64) {
environment.systemPackages = [ pkgs.glib (pkgs.gnome-control-center or pkgs.gnome.gnome-control-center) ];
environment.systemPackages = with pkgs.gnome; [ pkgs.glib gnome-control-center ];
services.accounts-daemon.enable = true;
services.gnome.gnome-online-accounts.enable = true;
# programs.evolution.enable = true;
# programs.evolution.plugins = with pkgs; [ evolution-ews ];
# services.gnome.evolution-data-server.enable = true;
# services.gnome.evolution-data-server.plugins = with pkgs; [ evolution-ews ];
programs.evolution.enable = true;
programs.evolution.plugins = with pkgs; [ evolution-ews ];
services.gnome.evolution-data-server.enable = true;
services.gnome.evolution-data-server.plugins = with pkgs; [ evolution-ews ];
};
wlr = { lib, config, ... }: mkIf config.common.linux.enable {
wlr = { ... }: mkIf config.common.linux.enable {
# swaync disable notifications on screencast
xdg.portal.wlr.settings.screencast = {
exec_before = ''which swaync-client && swaync-client --inhibitor-add "xdg-desktop-portal-wlr" || true'';
exec_after = ''which swaync-client && swaync-client --inhibitor-remove "xdg-desktop-portal-wlr" || true'';
};
# Niri stuff
# https://github.com/sodiboo/niri-flake/blob/main/docs.md
programs.niri.enable = true;
programs.niri.package = pkgs.niri-stable;
# Override gnome-keyring disabling
services.gnome.gnome-keyring.enable = lib.mkForce false;
};
logitech = { pkgs, ... }: mkIf cfg.enable {
@ -65,14 +50,6 @@ let
environment.systemPackages = with pkgs; [ piper ];
};
kwallet = { pkgs, lib, ... }: mkIf cfg.enable {
environment.systemPackages = [ pkgs.kdePackages.kwallet ];
services.dbus.packages = [ pkgs.kdePackages.kwallet ];
xdg.portal = {
extraPortals = [ pkgs.kdePackages.kwallet ];
};
};
virtualisation = { pkgs, ... }: mkIf cfg.enable {
virtualisation.podman = {
enable = true;
@ -82,7 +59,7 @@ let
virtualisation.oci-containers.backend = "podman";
virtualisation.virtualbox.host.enable = false;
virtualisation.virtualbox.host.enable = !pkgs.stdenv.isAarch64;
users.extraGroups.vboxusers.members = [ cfg.username ];
};
};
@ -93,7 +70,7 @@ let
enable = true;
# defaults (no need to be set unless modified)
quantum = 32;
rate = 44100;
rate = 48000;
};
security.rtkit.enable = true;
@ -119,19 +96,7 @@ let
};
in
{
imports = with modules; [
./sops.nix
adb
ios
graphics
wlr
logitech
kwallet
virtualisation
accounts
rt-audio
];
imports = with modules; [ adb ios wlr logitech virtualisation accounts rt-audio ];
options.common.linux = {
enable = mkOption {
@ -154,7 +119,7 @@ in
dnsServers = mkOption {
type = types.listOf types.str;
description = "DNS server list";
default = [ "1.1.1.1" "2606:4700:4700:1111" ];
default = [ "8.8.8.8" "8.8.4.4" ];
};
networks = mkOption {
type = types.attrsOf (types.submodule {
@ -191,7 +156,7 @@ in
loader.timeout = 60;
loader.systemd-boot.enable = true;
loader.efi.canTouchEfiVariables = true;
supportedFilesystems.ntfs = true;
supportedFilesystems = [ "ntfs" ];
};
boot.initrd.systemd.enable = builtins.length (builtins.attrNames (cfg.luksDevices)) > 0;
# LUKS devices
@ -214,6 +179,7 @@ in
services.fwupd.enable = true;
# Enable sound.
sound.enable = true;
services.pipewire = {
enable = true;
# alsa is optional
@ -245,12 +211,9 @@ in
"wheel" # Enable sudo for the user.
"plugdev" # Enable openrazer-daemon privileges
"audio"
"video"
"input"
];
shell = pkgs.fish;
};
nix.settings.trusted-users = [ "root" cfg.username ];
## Network configuration
systemd.network.enable = true;
@ -279,24 +242,19 @@ in
services.tailscale.enable = true;
## Time and Region
time.timeZone = lib.mkDefault "Europe/Zurich";
time.timeZone = "Europe/Zurich";
# Select internationalisation properties.
console.keyMap = "jp106"; # Console key layout
i18n.defaultLocale = "ja_JP.UTF-8";
# Input methods (only fcitx5 works reliably on Wayland)
i18n.inputMethod = {
fcitx5.waylandFrontend = true;
enabled = "fcitx5";
fcitx5.addons = with pkgs; [
fcitx5-mozc
fcitx5-unikey
fcitx5-gtk
];
} // (if config.system.nixos.release == "24.05" then {
enabled = "fcitx5";
} else {
enable = true;
type = "fcitx5";
});
};
# Default packages
environment.systemPackages = with pkgs; [
@ -313,7 +271,7 @@ in
libsForQt5.qtkeychain
## Wayland
kdePackages.qtwayland
qt5.qtwayland
];
# Add a reliable terminal
programs.fish.enable = true;
@ -322,22 +280,24 @@ in
programs.kdeconnect.enable = true;
# Flatpaks are useful... sometimes...
services.flatpak.enable = true;
# AppImages should run
programs.appimage = {
enable = true;
binfmt = true;
};
# DConf for GNOME configurations
programs.dconf.enable = true;
# Gaming! (not for ARM64)
programs.steam.enable = !pkgs.stdenv.isAarch64;
hardware.opengl.enable = true;
hardware.opengl.driSupport = true;
hardware.opengl.driSupport32Bit = !pkgs.stdenv.isAarch64; # For 32 bit applications
## Services
# gnome-keyring for storing keys
services.gnome.gnome-keyring.enable = true;
# OpenSSH so you can SSH to me
services.openssh.enable = true;
# PAM
security.pam.services.login.enableKwallet = true;
security.pam.services.login.enableGnomeKeyring = true;
security.pam.services.lightdm.enableKwallet = true;
security.pam.services.lightdm.enableGnomeKeyring = true;
security.pam.services.swaylock = { };
# Printers
services.printing.enable = true;
@ -345,18 +305,12 @@ in
xdg.portal = {
enable = true;
wlr.enable = true;
xdgOpenUsePortal = true;
# gtk portal needed to make gtk apps happy
extraPortals = [ pkgs.kdePackages.xdg-desktop-portal-kde pkgs.xdg-desktop-portal-gtk ];
extraPortals = [ pkgs.xdg-desktop-portal-gtk ];
config.sway.default = [ "wlr" "kde" "kwallet" ];
config.niri = {
default = [ "gnome" "gtk" ];
"org.freedesktop.impl.portal.Access" = "gtk";
"org.freedesktop.impl.portal.Notification" = "gtk";
"org.freedesktop.impl.portal.Secret" = "kwallet";
"org.freedesktop.impl.portal.FileChooser" = "kde";
};
config.common.default = [ "gtk" ];
config.common."org.freedesktop.impl.portal.Secret" = [ "gnome-keyring" ];
config.sway.default = [ "wlr" "gtk" ];
};
# D-Bus
services.dbus.packages = with pkgs; [ gcr ];

View file

@ -1,18 +0,0 @@
{ config, lib, ... }:
with { inherit (lib) types mkOption mkEnableOption; };
let
cfg = config.common.linux.sops;
in
{
options.common.linux.sops = {
enable = mkEnableOption "Enable sops configuration";
file = mkOption {
type = types.path;
description = "Path to the default sops file";
};
};
config = lib.mkIf cfg.enable {
sops.defaultSopsFile = cfg.file;
sops.age.sshKeyPaths = [ "/etc/ssh/ssh_host_ed25519_key" ];
};
}

View file

@ -34,6 +34,11 @@ in
default = 655;
description = "The port to listen on";
};
meshIp = mkOption {
type = types.str;
description = "The mesh ip to be assigned by hostname";
};
};
config = mkIf cfg.enable (builtins.seq
@ -46,6 +51,7 @@ in
myMeshIp = myHost.subnetAddr;
in
{
services.my-tinc.meshIp = myMeshIp;
# Scripts that set up the tinc services
environment.etc = {
"tinc/${networkName}/tinc-up".source = pkgs.writeScript "tinc-up-${networkName}" ''
@ -78,11 +84,6 @@ in
# firewall
networking.firewall.allowedUDPPorts = [ 655 ];
networking.firewall.allowedTCPPorts = [ 655 ];
networking.firewall.interfaces."tinc.${networkName}" = {
allowedUDPPortRanges = [{ from = 0; to = 65535; }];
allowedTCPPortRanges = [{ from = 0; to = 65535; }];
};
# configure tinc service
# ----------------------

View file

@ -23,13 +23,10 @@ in
hosts;
# Add all of them to host
nki.services.edns = {
enable = true;
cloaking-rules =
(lib.attrsets.mapAttrs'
(name: host: { name = "${name}.tinc"; value = host.subnetAddr; })
hosts)
;
};
networking.extraHosts = lib.strings.concatStringsSep
"\n"
(lib.attrsets.mapAttrsToList
(name: host: "${host.subnetAddr} ${name}.tinc")
hosts);
};
}

View file

@ -22,14 +22,4 @@
subnetAddr = "11.0.0.4";
ed25519PublicKey = "6MN5LVE4juavv8qJW2dTN4t/haKCADWquAQj/ADF7iN";
};
yoga = {
subnetAddr = "11.0.0.5";
ed25519PublicKey = "n+gIZjuuTPxi0OBqw2oOcmXd3loOHG+GQHBMXNlgyqI";
};
framework = {
subnetAddr = "11.0.0.6";
ed25519PublicKey = "YL7NA6Ydv/3FBfSzOPvyHlGweAViPvsG3b0Zh8L0NzF";
};
}

View file

@ -2,35 +2,44 @@
with lib;
let
nerd-fonts =
if builtins.hasAttr "nerd-fonts" pkgs then
pkgs.nerd-fonts.symbols-only
else
pkgs.nerdfonts.override { fonts = [ "NerdFontsSymbolsOnly" ]; };
noto-fonts-emoji-blob-bin =
let
pname = "noto-fonts-emoji-blob-bin";
version = "15.0";
in
pkgs.fetchurl {
name = "${pname}-${version}";
url = "https://github.com/C1710/blobmoji/releases/download/v${version}/Blobmoji.ttf";
sha256 = "sha256-n5yVk2w9x7UVrMe0Ho6nwu1Z9E/ktjo1UHdHKStoJWc=";
downloadToTemp = true;
recursiveHash = true;
postFetch = ''
install -Dm 444 $downloadedFile $out/share/fonts/blobmoji/Blobmoji.ttf
'';
};
in
{
imports = [ ./mounting.nix ];
# Fonts
config.fonts = {
packages = with pkgs; mkForce [
packages = with pkgs; [
noto-fonts-emoji-blob-bin
ibm-plex
nerd-fonts
(nerdfonts.override { fonts = [ "NerdFontsSymbolsOnly" ]; })
noto-fonts
(pkgs.noto-fonts-cjk-sans or pkgs.noto-fonts-cjk)
noto-fonts-cjk
merriweather
corefonts
font-awesome
hack-font # for Plasma
];
} // (if pkgs.stdenv.isLinux then {
enableDefaultPackages = false;
fontconfig = {
defaultFonts = {
emoji = lib.mkBefore [ "Blobmoji" ];
serif = lib.mkBefore [ "IBM Plex Serif" "IBM Plex Sans JP" "IBM Plex Sans KR" "Blobmoji" ];
sansSerif = lib.mkBefore [ "IBM Plex Sans" "IBM Plex Sans JP" "IBM Plex Sans KR" "Blobmoji" ];
monospace = lib.mkBefore [ "IBM Plex Mono" "Font Awesome 6 Free" "Symbols Nerd Font" "Blobmoji" "IBM Plex Sans JP" ];
serif = lib.mkBefore [ "IBM Plex Serif" "IBM Plex Sans JP" "IBM Plex Sans KR" ];
sansSerif = lib.mkBefore [ "IBM Plex Sans" "IBM Plex Sans JP" "IBM Plex Sans KR" ];
monospace = lib.mkBefore [ "IBM Plex Mono" ];
};
localConf = ''
<?xml version="1.0"?>
@ -42,7 +51,6 @@ in
<family>IBM Plex Sans</family>
<family>IBM Plex Sans JP</family>
<family>IBM Plex Sans KR</family>
<family>Blobmoji</family>
</prefer>
</alias>
</fontconfig>

View file

@ -12,7 +12,7 @@ lib.mkIf pkgs.stdenv.isLinux {
name = "system-icons";
paths = with pkgs; [
#libsForQt5.breeze-qt5 # for plasma
(pkgs.gnome-themes-extra or gnome.gnome-themes-extra) # Until 24.11
gnome.gnome-themes-extra
];
pathsToLink = [ "/share/icons" ];
};

View file

@ -16,7 +16,7 @@ in
security.pam = mkIf pkgs.stdenv.isLinux {
u2f = {
enable = true;
settings.cue = true;
cue = true;
};
# Services

View file

@ -8,11 +8,6 @@ in
options.nki.services.edns = {
enable = mkEnableOption "Enable encrypted DNS";
ipv6 = mkEnableOption "Enable ipv6";
cloaking-rules = mkOption {
type = types.attrsOf types.str;
default = { };
description = "A set of domain -> ip mapping for cloaking_rules";
};
};
config = mkIf cfg.enable {
@ -47,11 +42,6 @@ in
{ server_name = "*"; via = [ "anon-plan9-dns" "anon-v.dnscrypt.up-ipv4" ]; }
];
anonymized_dns.skip_incompatible = true;
# Cloaking rules
cloaking_rules = pkgs.writeText "cloaking_rules.txt" (lib.strings.concatStringsSep
"\n"
(lib.attrsets.mapAttrsToList (name: ip: "${name} ${ip}") cfg.cloaking-rules));
};
};
};

View file

@ -1,66 +0,0 @@
{ config, lib, ... }:
with { inherit (lib) mkOption types mkIf; };
let
cfg = config.services.nix-build-farm;
hosts = import ./hosts.nix;
build-user = "nix-builder";
isBuilder = host: host ? "builder";
allBuilders = lib.filterAttrs (_: isBuilder) hosts;
in
{
options.services.nix-build-farm = {
enable = mkOption {
type = types.bool;
default = true;
description = "Whether to enable nix-build-farm as a client";
};
hostname = mkOption {
type = types.enum (builtins.attrNames hosts);
description = "The hostname as listed in ./hosts.nix file";
};
privateKeyFile = mkOption {
type = types.path;
description = "The path to the private SSH key file";
};
ipAddrs = mkOption {
type = types.str;
description = "The ip addresses to limit access to";
default = "11.0.0.*";
};
};
config = mkIf cfg.enable (
let
host = hosts.${cfg.hostname};
otherHosts = lib.filterAttrs (name: _: name != cfg.hostname) hosts;
otherBuilders = lib.filterAttrs (name: _: name != cfg.hostname) allBuilders;
in
{
nix.distributedBuilds = true;
nix.buildMachines = lib.mapAttrsToList
(name: host: {
hostName = host.host;
sshUser = build-user;
sshKey = cfg.privateKeyFile;
} // host.builder)
otherBuilders;
users = mkIf (isBuilder host) {
users.${build-user} = {
description = "Nix build farm user";
group = build-user;
isNormalUser = true;
openssh.authorizedKeys.keys = lib.mapAttrsToList (_: host: ''from="${cfg.ipAddrs}" ${host.pubKey}'') otherHosts;
};
groups.${build-user} = { };
};
nix.settings.trusted-users = mkIf (isBuilder host) [ build-user ];
}
);
}

View file

@ -1,37 +0,0 @@
{
cloud = {
host = "cloud.tinc";
pubKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIE87ddj0fTH0NuvJz0dT5ln7v7zbafXqDVdM2A4ddOb0 root@nki-personal-do";
};
home = {
host = "home.tinc";
pubKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIK6N1uTxnbo73tyzD9X7d7OgPeoOpY7JmQaHASjSWFPI nki@kagamiPC";
builder = {
publicHostKey = "c3NoLWVkMjU1MTkgQUFBQUMzTnphQzFsWkRJMU5URTVBQUFBSUhiVTh2NlNBa0kyOTBCc1QzVG1IRVVJQWdXcVFyNm9jRmpjakRRczRoT2ggcm9vdEBrYWdhbWlQQwo=";
systems = [ "x86_64-linux" "aarch64-linux" ];
maxJobs = 16;
speedFactor = 2;
supportedFeatures = [ "nixos-test" "benchmark" "big-parallel" "kvm" ];
};
};
yoga = {
host = "yoga.tinc";
pubKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIE6ZrO/xIdmwBCUx80cscBSpJBBTp55OHGrXYBGRXKAw nki@nki-yoga-g8";
};
framework = {
host = "framework.tinc";
pubKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIH/g472MaT7YySUhBjxClfmMjpn98qYnKXDKlzWHYwuO nki@nki-framework";
builder = {
publicHostKey = "c3NoLWVkMjU1MTkgQUFBQUMzTnphQzFsWkRJMU5URTVBQUFBSUdOUlBCVFRkNTVVMXY1U1Jac0FjYVdhS3JGZTY0ZjIxOVViODVTQ2NWd28gcm9vdEBua2ktZnJhbWV3b3JrCg==";
systems = [ "x86_64-linux" "aarch64-linux" ];
maxJobs = 16;
speedFactor = 3;
supportedFeatures = [ "nixos-test" "benchmark" "big-parallel" "kvm" ];
};
};
}

View file

@ -1 +0,0 @@
nix.home.tinc:zG2uDy0MbLY0wLuoVH/qKzTD6hTfKZufA2cWDSTCZMA=

View file

@ -1,60 +0,0 @@
{ config, pkgs, lib, ... }:
with { inherit (lib) mkEnableOption mkOption types mkIf; };
let
cfg = config.nki.services.nix-cache;
bindAddr = "127.0.0.1:5000";
in
{
options.nki.services.nix-cache = {
enableClient = mkOption {
type = types.bool;
default = !cfg.enableServer;
description = "Enable nix-cache client";
};
enableServer = mkEnableOption "Enable nix-cache server";
host = mkOption {
type = types.str;
default = "nix.home.tinc";
};
publicKey = mkOption {
type = types.str;
default = builtins.readFile ./cache-pub-key.pem;
};
privateKeyFile = mkOption {
type = types.path;
description = "Path to the private key .pem file";
};
};
config = {
nix.settings = mkIf cfg.enableClient {
substituters = lib.mkAfter [ "http://${cfg.host}" ];
trusted-public-keys = [ cfg.publicKey ];
};
services.harmonia = mkIf cfg.enableServer {
enable = true;
signKeyPaths = [ cfg.privateKeyFile ];
settings = {
bind = bindAddr;
priority = 45;
};
};
services.nginx = mkIf cfg.enableServer {
enable = true;
recommendedProxySettings = true;
virtualHosts = {
# ... existing hosts config etc. ...
"${cfg.host}" = {
locations."/".proxyPass = "http://${bindAddr}";
};
};
};
};
}

View file

@ -0,0 +1,12 @@
{ pkgs, config, lib, ... }:
with lib;
let
cfg = config.nki.services.pam;
in
{
options.nki.services.pam.enableGnomeKeyring = mkEnableOption "Enable gnome-keyring on login";
config = mkIf cfg.enableGnomeKeyring {
security.pam.services.login.enableGnomeKeyring = true;
# security.pam.services.login.gnupg.enable = true;
};
}

View file

@ -13,22 +13,9 @@
../modules/personal/fonts
# Encrypted DNS
../modules/services/edns
# Wireless card
./wireless.nix
];
# Sops
common.linux.sops.enable = true;
common.linux.sops.file = ./secrets.yaml;
sops.secrets."nix-build-farm/private-key" = { mode = "0400"; };
services.nix-build-farm.hostname = "framework";
services.nix-build-farm.privateKeyFile = config.sops.secrets."nix-build-farm/private-key".path;
# services.xserver.enable = true;
# services.xserver.displayManager.sddm.enable = true;
# services.xserver.displayManager.sddm.wayland.enable = true;
services.desktopManager.plasma6.enable = true;
services.xserver.desktopManager.plasma5.enable = true;
# Power Management
services.upower = {
@ -42,13 +29,13 @@
services.power-profiles-daemon.enable = true;
# powerManagement.enable = true;
# powerManagement.powertop.enable = true;
services.logind.lidSwitch = "suspend-then-hibernate";
services.logind.lidSwitch = "suspend";
# Printing
services.printing.drivers = with pkgs; [ epfl-cups-drivers ];
# Enable touchpad support (enabled default in most desktopManager).
services.libinput.enable = true;
services.xserver.libinput.enable = true;
# Keyboard
services.input-remapper.enable = true;
services.input-remapper.serviceWantedBy = [ "multi-user.target" ];
@ -85,16 +72,6 @@
security.pam.services.swaylock.fprintAuth = true;
security.pam.services.login.fprintAuth = true;
# tinc network
sops.secrets."tinc-private-key" = { };
services.my-tinc = {
enable = true;
hostName = "framework";
ed25519PrivateKey = config.sops.secrets."tinc-private-key".path;
bindPort = 6565;
};
# Secrets
# sops.defaultSopsFile = ./secrets.yaml;
# sops.age.sshKeyPaths = [ "/etc/ssh/ssh_host_ed25519_key" ];

View file

@ -12,7 +12,10 @@
boot.initrd.availableKernelModules = [ "nvme" "xhci_pci" "thunderbolt" "usb_storage" "sd_mod" ];
boot.initrd.kernelModules = [ ];
boot.kernelModules = [ "kvm-amd" ];
boot.extraModulePackages = [ ];
boot.kernelParams = [
# See https://community.frame.work/t/tracking-graphical-corruption-in-fedora-39-amd-3-03-bios/39073/143
"amdgpu.sg_display=0"
# Hibernation
"resume=UUID=fa8aaf51-b99f-4fb4-9230-8c0957d8af3f"
"resume_offset=5776640" # btrfs inspect-internal map-swapfile -r /var/swapfile

View file

@ -1,32 +0,0 @@
tinc-private-key: ENC[AES256_GCM,data:cKtOFrF5FRSHWxe/QxH5O9GAba1WcWeCwW1IOzmbgdtFufRoWbCtYeaLP+WQhQ70z6xobiY9DN8Jrh7mDptKSsfKrrx2SH5JrdpsoINhLMbetXq7E29+q6CkS8NlLgE/KyV8eFjQySNsYiA/+Efq9xj9e1wOmHBDsND/jgiJDkA1qsEIFZg/vuv8LdoRY3TV/oKJ4pao9+70G4H+8Ef1sMZHGNe9qJ94Wa71nNX2fTSjKH5YBbRijMAePWr/IeCpZ9Phs7RqjBs=,iv:l0iB136X7nLVblQjFi7K4f42JKSxdsiLIRy5GPzK1nc=,tag:HAgkvWkl0Rx62ejGZckdKA==,type:str]
nix-build-farm:
private-key: ENC[AES256_GCM,data:w56sobHrhvVcKbuK15Yj20EqgFrR/5pNy/rcQjlZCiFEfgPC6ZrbsxziIdMTOX+Q4DyllKCKo+g/MIKm5S+s+nIff509ODlILhTtXlZvZlnT9+wvm5oN3WQCdkkqOr1gNnErPupfMxA6V35tNvNBCeomYuM8Xb7TuH4I1fXa5GFeT0Tnp5A2WqWoS1MzXz34CxlKXvoL6i4w7nUngt5zaGr9oMZbSa0pHxEzIhXk6h/lE864QlJid7q/mDok9gh2R80WvArGtNNj9PrT6cNKYIFuXo7vITkSkT3vo9BLflg5sgRxy4+7rBaRla0ziihQvZLtworwo6aNDvEqPqYfiX0TMYPpEWvJpp/HGDJy+Po7b0ZZkwSza+tB0J79vFH2h11Px1XSwXdq757Bn9G4OTb5oKWS5ycBU4xTnnHQBjYTSooFxYyTAvfKNEsB8AHKSuQRZgtVhWoOSPpJ6YL94ClUIT+DTaNmQjwouqNatf/nif+N/FCar8JWZDE+FX1TBu0yLmJie/f0LCdCQx+6IVdyXg7oVCoNiGg0,iv:TeSxlVc0WlOMMUtv/uq3f7JvW/kNCM7LjguhZxL51a4=,tag:m1iuk4pAX/yugM0ObzkJHQ==,type:str]
sops:
kms: []
gcp_kms: []
azure_kv: []
hc_vault: []
age:
- recipient: age188tgu3psvywk6shq85mk2q0jdjwd0tcswzwlwu5pa5n3pndx75dq090z59
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBmL2Z1RzBWaTI1TDl6WDNa
NTNVdEhTSFU5enNlTGVNWTI5anBZb1BtaVhjCm1BRnJDSXl1cWdBRUs1VnREVjBU
QWZxdkgzdm9JL0k5WmhDL1RCNTltdm8KLS0tIFhvQTlKMDZiVklTRWd4TzVmc2ll
bmpjcWdBV1doZml2NjlzQzdQczJ3alEKBMRP3POxtPIqBWnrvxY/++5jtVE70Uxa
EVfhsUO76A/hzyxfzpLEy1QGFE+DB/zlU0CK7HkNGPD2TrBHbzkPJA==
-----END AGE ENCRYPTED FILE-----
- recipient: age1vgh6kvee8lvxylm7z86fpl3xzjyjs4u3zdfkyf064rjvxk9fpumsew7n27
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB2MUxQU0dZOGRaekF4MWdo
T0krcERtRTJndFR1RHZmL0t6MjBxMW5PSENNCkR6SUhxQ0FoaEhuaWpiUzJ0MnJE
RXRERzVhL0lRVW1iRUlac0c5OHZsckEKLS0tIC9VM1dNZTNzdkFnMWk2YUwvcDNB
TDZnVjBaVzZBem5lZDB1MW4xQ0RmZ28K6d7mF+f3ZyilXlSIQGT2pBrTWuYLccE1
rYIJjHjFft/2wPX2gAW9VTiwfMT3lKJhJRqNdoie5phV5BZhkb3D9w==
-----END AGE ENCRYPTED FILE-----
lastmodified: "2024-08-17T14:58:10Z"
mac: ENC[AES256_GCM,data:ZCrzXDttLxYUvdLiqM5I17Ys6O3zoOVKq8xP78VaLb3AAoV4RGGQxixKVQ6K9h84e8bFymh512BR7xKa9fqebxTyL1XCqPkRaSZy0aWjbc6QCaK+JD4yqivgO/x5x2xgMpX/ZhPFzKNLpMga61bnm6plvF8ocG+wOqYvj3vL0Ss=,iv:QZ8YJD7h2QD2jqVKo4bg0rwpZSTyyNw6zZDcBfClKPo=,tag:PH2XnTqxV2irymg2+Z+Egg==,type:str]
pgp: []
unencrypted_suffix: _unencrypted
version: 3.9.0

View file

@ -1,13 +0,0 @@
{ pkgs, lib, ... }: {
environment.systemPackages = with pkgs; [ iw ];
# Disable power_save on boot
services.udev.packages = [
(pkgs.writeTextFile {
name = "udev_disable_wifi_power_save";
destination = "/etc/udev/rules.d/10-wifi-power_save.rules";
text = ''
ACTION=="add", SUBSYSTEM=="net", KERNEL=="wl*", RUN+="${lib.getExe pkgs.iw} dev $name set power_save off"
'';
})
];
}

View file

@ -1,5 +1,5 @@
{ config, pkgs, lib, ... }: {
environment.etc = {
"wireplumber/wireplumber.conf.d/51-sdac.conf".source = ./sdac.conf.json;
"wireplumber/main.lua.d/51-sdac.lua".source = ./sdac.lua;
};
}

View file

@ -1,19 +0,0 @@
monitor.alsa.rules = [
{
matches = [
{
device.name = "alsa_output.usb-Grace_Design_SDAC-00.*"
}
]
actions = {
update-props = {
# audio.format = "S24_3LE"
audio.rate = 88200
api.alsa.period-size = 2
api.alsa.headroom = 0
api.alsa.disable-batch = true
}
}
}
]

View file

@ -6,7 +6,7 @@ rule = {
},
apply_properties = {
["audio.format"] = "S24_3LE",
["audio.rate"] = 44100,
["audio.rate"] = 96000,
["api.alsa.period-size"] = 2,
["api.alsa.headroom"] = 0,
["api.alsa.disable-batch"] = true

View file

@ -5,53 +5,28 @@
{ lib, config, pkgs, ... }:
with lib;
let
openrazer = { pkgs, ... }: {
# Razer stuff
hardware.openrazer = {
enable = true;
users = [ "nki" ];
};
environment.systemPackages = with pkgs; [ polychromatic ];
};
in
{
imports =
[
# Include the results of the hardware scan.
./hardware-configuration.nix
# secret management
./secrets
# Fonts
../modules/personal/fonts
# Encrypted DNS
../modules/services/edns
# Other services
../modules/personal/u2f.nix
./peertube-runner.nix
openrazer
];
# Kernel
boot.kernelPackages = pkgs.linuxKernel.packages.linux_xanmod_stable;
# Plasma!
services.desktopManager.plasma6.enable = true;
## Encryption
# Kernel modules needed for mounting USB VFAT devices in initrd stage
common.linux.luksDevices.root = "/dev/disk/by-uuid/7c6e40a8-900b-4f85-9712-2b872caf1892";
common.linux.sops.enable = true;
common.linux.sops.file = ./secrets.yaml;
# Nix cache server
sops.secrets."nix-cache/private-key" = { owner = "harmonia"; group = "harmonia"; mode = "0600"; };
nki.services.nix-cache = {
enableServer = true;
privateKeyFile = config.sops.secrets."nix-cache/private-key".path;
};
sops.secrets."nix-build-farm/private-key" = { mode = "0400"; };
services.nix-build-farm.hostname = "home";
services.nix-build-farm.privateKeyFile = config.sops.secrets."nix-build-farm/private-key".path;
# Networking
common.linux.networking =
@ -82,11 +57,13 @@ in
PrivateKeyFile = config.sops.secrets."dtth-wg/private-key".path;
};
wireguardPeers = [{
PublicKey = "+7iI4jwmM1Qr+/DKB1Hv8JgFkGu7lSV0PAoo+O5d3yQ=";
PresharedKeyFile = config.sops.secrets."dtth-wg/preshared-key".path;
AllowedIPs = [ "100.64.0.0/10" "fd00::/106" ];
Endpoint = "vpn.dtth.ch:51820";
PersistentKeepalive = 25;
wireguardPeerConfig = {
PublicKey = "+7iI4jwmM1Qr+/DKB1Hv8JgFkGu7lSV0PAoo+O5d3yQ=";
PresharedKeyFile = config.sops.secrets."dtth-wg/preshared-key".path;
AllowedIPs = [ "100.64.0.0/10" "fd00::/106" ];
Endpoint = "vpn.dtth.ch:51820";
PersistentKeepalive = 25;
};
}];
};
systemd.network.networks."dtth-wg" = {
@ -94,8 +71,8 @@ in
address = [ "100.73.146.80/32" "fd00::33:105b/128" ];
DHCP = "no";
routes = [
{ Destination = "100.64.0.0/10"; Scope = "link"; }
{ Destination = "fd00::/106"; }
{ routeConfig = { Destination = "100.64.0.0/10"; Scope = "link"; }; }
{ routeConfig.Destination = "fd00::/106"; }
];
};
@ -124,7 +101,6 @@ in
"/mnt/Data" = ntfsMount "/dev/disk/by-uuid/A90680F8BBE62FE3";
"/mnt/Stuff" = ntfsMount "/dev/disk/by-uuid/717BF2EE20BB8A62";
"/mnt/Shared" = ntfsMount "/dev/disk/by-uuid/76AC086BAC0827E7";
"/mnt/osu" = ntfsMount "/dev/disk/by-uuid/530D3E1648CD1C26";
};
# PAM
@ -192,13 +168,5 @@ in
passwordFile = config.sops.secrets."scrobble/listenbrainz".path;
};
};
programs.virt-manager.enable = true;
users.groups.libvirtd.members = [ "nki" ];
virtualisation.libvirtd.enable = true;
virtualisation.spiceUSBRedirection.enable = true;
}

View file

@ -36,6 +36,10 @@
swapDevices =
[{ device = "/dev/disk/by-uuid/561f6441-1915-4059-a5e1-76a449b0c9bf"; }];
# GPU options
services.xserver.videoDrivers = [ "amdgpu" ];
hardware.opengl.enable = true;
# bluetooth usb
hardware.firmware = [ pkgs.rtl8761b-firmware ];
}

View file

@ -1,67 +0,0 @@
{ config, pkgs, lib, ... }:
let
user = "peertube-runner-nodejs";
instance = "systemd-instance";
in
{
sops.secrets."peertube/dtth-key" = {
restartUnits = [ "peertube-runner.service" ];
};
users.groups.${user} = { };
users.users.${user} = {
isSystemUser = true;
group = user;
};
sops.templates."peertube-config.toml".owner = user;
sops.templates."peertube-config.toml".content = ''
[jobs]
concurrency = 2
[ffmpeg]
threads = 12
nice = 20
[[registeredInstances]]
url = "https://peertube.dtth.ch"
runnerToken = "${config.sops.placeholder."peertube/dtth-key"}"
runnerName = "kagamipc"
'';
environment.etc."${user}/${instance}/config.toml".source = config.sops.templates."peertube-config.toml".path;
systemd.services.peertube-runner = {
description = "PeerTube runner daemon";
wantedBy = [ "multi-user.target" ];
after = [ "network.target" ];
requires = [ ];
serviceConfig =
{
ExecStart = "${lib.getExe' pkgs.peertube.runner "peertube-runner"} server --id ${instance}";
User = user;
RuntimeDirectory = user;
StateDirectory = user;
CacheDirectory = user;
# Hardening
ProtectSystem = "full";
PrivateDevices = false;
NoNewPrivileges = true;
ProtectHome = true;
CapabilityBoundingSet = "~CAP_SYS_ADMIN";
};
environment = {
NODE_ENV = "production";
# Override XDG values to fit env-path
# https://github.com/sindresorhus/env-paths/blob/main/index.js
XDG_DATA_HOME = "/run";
XDG_CONFIG_HOME = "/etc";
XDG_CACHE_HOME = "/var/cache";
XDG_STATE_HOME = "/var/lib";
};
path = with pkgs; [ nodejs ffmpeg ];
};
}

View file

@ -0,0 +1,6 @@
{ config, pkgs, ... }:
{
sops.defaultSopsFile = ./secrets.yaml;
sops.age.sshKeyPaths = [ "/etc/ssh/ssh_host_ed25519_key" ];
}

View file

@ -11,12 +11,6 @@ scrobble:
dtth-wg:
private-key: ENC[AES256_GCM,data:ySxPGzOplKwNLxRnPNw7If7xzxMwRkwTasT7FaQE9n5YB04R+gaQVjDqPqg=,iv:f5t94bUoo9sCGGwWytiuhg5jcKjzRjbR3Q0OIM28VDU=,tag:fJos9Hb9XytQbfGaPMa1/A==,type:str]
preshared-key: ENC[AES256_GCM,data:96q0ZfvPz4pb53XvTGameVkcETamYH8Xbv69672RBdacH6QjRCCVvPnBTfA=,iv:Q2Yonb07/Uu6KidhMgRX4zJuNU1ZySNC7g/5TwpMU80=,tag:1qQQdk20yIQlGZmX+/25RA==,type:str]
peertube:
dtth-key: ENC[AES256_GCM,data:Gu7qOisVBZrFXKBr51165FJ7Ej4hV+lIf3AMC02R3UFNXOnTHF2xC8E=,iv:F83FuD1VjZEJFMcx3gkQuKCpJmYdHtO15fRHkYdMxJM=,tag:ScH42Tr5ZsIo9JMnXhylSw==,type:str]
nix-cache:
private-key: ENC[AES256_GCM,data:4sbfIQb10Y50CrZbgjN+1iXEbXTpDqMbIB/yA3WlaAqhLtb8HKib5aZX3DLoxFbVihJcztQsvBBgEAhT9iMijoksaT9qzBQ5yIn4NGCfFem1DK8DQdjhTLMCVTyMFCT7hQHu/2Sd7w==,iv:zTSxuKOtOLekOBKBvl9MScD/Bo1Hviqq/n8Saa+1Cgo=,tag:fx73fCDPY9d07V3KKMw3DA==,type:str]
nix-build-farm:
private-key: ENC[AES256_GCM,data:m5neeWCEdaZ1MRhNwTptfDIgv3ABNlYyNil3oTD81Jbe/6WxWaS5Q++CRlHCjc2hOoHsWsZixw8iGZVTA+QXgH6B9C6A4oOAhgR9m92EGTEfFw0qxQbdzs7U98Yonx/N8SApUycZZB/EU81+MrDNY4GGzCiO6s00/vZLkDTYnqRFgbo+8KTG0BQTl4q+VYP2q3l0wy+Ivz5CWPmbz42Xdin/sBnjeFHKDuof4iZZnN3i8gUJ/mMw3lbdiHd6A8DL0G5Ut46ljzMC2aMsZOATCID3mPOPgI0xIetDofPJLDqVsNqptRHo8WB+KwDidvl222f5F7JqdSqgAMOJYPscrX0odufApiJfg5bbXBygvrDfAlPSruW7GsWGoKAhw0qC4NC/j+qYCwhS0qdorCLnIy3zzMtA6HkHtE675hy7/7oLj7k9Y8MhE4PxztjXTmDazaVCtKhnA/DpaxP2mH84gfCkJFD1YF9jtPm+P3e+46FwkW+WnHaA2L+H7Evava30DLEBhh5y9Gd1A3JN4isn,iv:7KUWg7+GWgmGJkbIvsy9gtccZBb+1Y5uDWhXQFk0obk=,tag:qJdM684XPHxecLVxVb5pgw==,type:str]
sops:
kms: []
gcp_kms: []
@ -50,8 +44,8 @@ sops:
bUhIT0Z2b1dVWGNyS1hRVFRyZTA4d00KchP7EhSOMwBl5vFuuskzosRoi8jUu1sw
hVjJNF2a40ewgkQgVAoWEzirHbknbQORzmepDDRth7Bve3UQU64+GA==
-----END AGE ENCRYPTED FILE-----
lastmodified: "2024-08-16T13:59:20Z"
mac: ENC[AES256_GCM,data:ncT8fbtEb9ZcLcftXwgAKJRPPSG4TRHFMArtVgWNmIjDRcCNNT7ICa+9Dl8DAYKRJ+8pgelV9StIg2f7rvypHYlckontEP5nwSFzEApLItG3AZXewTC8VPoDYb4T8/OWKDoa5kBMvGrDr1bFP/CZz7H8No+k5TV7fVExsw0PHpg=,iv:vxbkeJtHkOAq7NcaZEIOMV3qGEqBUg/vpJYumBBfY70=,tag:T0yw2x1O5Tp0UllLpcFryg==,type:str]
lastmodified: "2023-04-29T13:03:22Z"
mac: ENC[AES256_GCM,data:ZNDRS6LLy89TZoW27c57RMnjs6M/GBH0XfKKlrhys8gL7+I0V/++ry59VDbLxvqS4nPR4C5hk777+B5dqnseyYW2xRT3NKYxocCQu5kO6A8L/wB00j3bm3SSIGwLcKJPibEqi7ymU53K0bmZdjRMChkBwv3CnDNkM3Dc6rvZ2DM=,iv:Z1ZjnYW1Yk+oEzNknQDytTengjKxcud95LZTFfKMnpw=,tag:pnZ+UGQWuRCKoTll00oUKA==,type:str]
pgp: []
unencrypted_suffix: _unencrypted
version: 3.9.0
version: 3.7.3

View file

@ -12,9 +12,6 @@
../modules/cloud/conduit
../modules/cloud/gotosocial
# Encrypted DNS
../modules/services/edns
./headscale.nix
./gitea.nix
./miniflux.nix
@ -24,13 +21,8 @@
./invidious.nix
./owncast.nix
./peertube.nix
./outline.nix
./vikunja.nix
./n8n.nix
];
system.stateVersion = "21.11";
common.linux.enable = false; # Don't enable the "common linux" module, this is a special machine.
# Personal user
@ -62,19 +54,22 @@
];
virtualisation.docker.enable = true;
virtualisation.docker.extraOptions = "--data-root /mnt/data/docker";
services.do-agent.enable = true;
system.autoUpgrade = {
enable = true;
allowReboot = true;
flake = "github:natsukagami/nix-home#nki-personal-do";
};
nix = {
package = pkgs.nixUnstable;
extraOptions = ''
experimental-features = nix-command flakes
'';
};
nki.services.edns.enable = true;
nki.services.edns.ipv6 = true;
# Secret management
sops.defaultSopsFile = ./secrets/secrets.yaml;
sops.age.sshKeyPaths = [ "/etc/ssh/ssh_host_ed25519_key" ];
@ -87,10 +82,6 @@
services.my-tinc.rsaPrivateKey = config.sops.secrets."tinc/rsa-private-key".path;
services.my-tinc.ed25519PrivateKey = config.sops.secrets."tinc/ed25519-private-key".path;
sops.secrets."nix-build-farm/private-key" = { mode = "0400"; };
services.nix-build-farm.hostname = "home";
services.nix-build-farm.privateKeyFile = config.sops.secrets."nix-build-farm/private-key".path;
# Set up traefik
sops.secrets.cloudflare-dns-api-token = { owner = "traefik"; };
sops.secrets.traefik-dashboard-users = { owner = "traefik"; };
@ -154,7 +145,7 @@
};
# Mail
sops.secrets.mail-users = { owner = "maddy"; reloadUnits = [ "maddy.service" ]; };
sops.secrets.mail-users = { owner = "maddy"; };
cloud.mail = {
enable = true;
debug = true;
@ -195,13 +186,74 @@
protocol = "udp";
};
# Outline
sops.secrets.minio-secret-key = { owner = "root"; mode = "0444"; };
sops.secrets.authentik-oidc-client-secret = { owner = "outline"; };
sops.secrets."outline/smtp-password" = { owner = "outline"; };
services.outline = {
enable = true;
package = pkgs.outline.overrideAttrs (attrs: {
patches = if builtins.hasAttr "patches" attrs then attrs.patches else [ ] ++ [ ../modules/cloud/outline/dtth-wiki.patch ];
});
databaseUrl = "postgres://outline:outline@localhost/outline?sslmode=disable";
redisUrl = "local";
publicUrl = "https://wiki.dtth.ch";
port = 18729;
storage = {
accessKey = "minio";
secretKeyFile = config.sops.secrets.minio-secret-key.path;
region = config.services.minio.region;
uploadBucketUrl = "https://s3.dtth.ch";
uploadBucketName = "dtth-outline";
uploadMaxSize = 50 * 1024 * 1000;
};
maximumImportSize = 50 * 1024 * 1000;
oidcAuthentication = {
clientId = "3a0c10e00cdcb4a1194315577fa208a747c1a5f7";
clientSecretFile = config.sops.secrets.authentik-oidc-client-secret.path;
authUrl = "https://auth.dtth.ch/application/o/authorize/";
tokenUrl = "https://auth.dtth.ch/application/o/token/";
userinfoUrl = "https://auth.dtth.ch/application/o/userinfo/";
displayName = "DTTH Account";
};
smtp = {
fromEmail = "DTTH Wiki <dtth.wiki@nkagami.me>";
replyEmail = "";
host = "mx1.nkagami.me";
username = "dtth.wiki@nkagami.me";
passwordFile = config.sops.secrets."outline/smtp-password".path;
port = 465;
secure = true;
};
forceHttps = false;
};
cloud.postgresql.databases = [ "outline" ];
systemd.services.outline.requires = [ "postgresql.service" ];
cloud.traefik.hosts.outline = { host = "wiki.dtth.ch"; port = 18729; };
# GoToSocial
sops.secrets.gts-env = { restartUnits = [ "gotosocial.service" ]; };
sops.secrets.gts-env = { };
cloud.gotosocial = {
enable = true;
envFile = config.sops.secrets.gts-env.path;
};
# Minio
sops.secrets.minio-credentials = { };
services.minio = {
enable = true;
listenAddress = ":61929";
consoleAddress = ":62929";
rootCredentialsFile = config.sops.secrets.minio-credentials.path;
dataDir = lib.mkForce [ "/mnt/data/minio" ];
};
cloud.traefik.hosts.minio = { host = "s3.dtth.ch"; port = 61929; };
system.stateVersion = "21.11";
# ntfy
cloud.traefik.hosts.ntfy-sh = { host = "ntfy.nkagami.me"; port = 11161; noCloudflare = true; };
services.ntfy-sh = {

View file

@ -9,28 +9,11 @@ let
signingKey = "0x3681E15E5C14A241";
catppuccinThemes = pkgs.fetchurl {
url = "https://github.com/catppuccin/gitea/releases/download/v0.4.1/catppuccin-gitea.tar.gz";
hash = "sha256-/P4fLvswitlfeaKaUykrEKvjbNpw5Q/nzGQ/GZaLyUI=";
catppuccinThemes = builtins.fetchurl {
url = "https://github.com/catppuccin/gitea/releases/download/v0.2.1/catppuccin-gitea.tar.gz";
sha256 = "sha256:18l67whffayrgylsf5j6g7sj95anjcjl0cy7fzqn1wrm0gg2xns0";
};
staticDir = pkgs.runCommandLocal "forgejo-static" { } ''
mkdir -p $out
tmp=$(mktemp -d)
cp -r ${config.services.forgejo.package.data}/* $tmp
chmod -R +w $tmp
# Copy icons
install -m 0644 ${./gitea/img}/* $tmp/public/assets/img
# Copy the themes
env PATH=${pkgs.gzip}/bin:${pkgs.gnutar}/bin:$PATH \
tar -xvf ${catppuccinThemes} -C $tmp/public/assets/css
cp -r $tmp/* $out
'';
default-themes = "forgejo-auto, forgejo-light, forgejo-dark, gitea-auto, gitea-light, gitea-dark, forgejo-auto-deuteranopia-protanopia, forgejo-light-deuteranopia-protanopia, forgejo-dark-deuteranopia-protanopia, forgejo-auto-tritanopia, forgejo-light-tritanopia, forgejo-dark-tritanopia";
themes = strings.concatStringsSep ", " [
themes = strings.concatStringsSep "," [
"catppuccin-macchiato-green"
"catppuccin-mocha-teal"
"catppuccin-macchiato-sky"
@ -90,15 +73,7 @@ let
];
in
{
users.users.${user} = {
home = config.services.forgejo.stateDir;
useDefaultShell = true;
isSystemUser = true;
group = user;
};
users.groups.${user} = { };
sops.secrets."gitea/signing-key".owner = user;
sops.secrets."gitea/minio-secret-key".owner = user;
sops.secrets."gitea/mailer-password".owner = user;
# database
cloud.postgresql.databases = [ user ];
@ -108,20 +83,21 @@ in
noCloudflare = true;
};
systemd.services.forgejo.requires = [ "postgresql.service" ];
systemd.services.gitea.requires = [ "postgresql.service" ];
services.forgejo = {
services.gitea = {
enable = true;
inherit user;
appName = "DTTHgit";
settings = {
server = {
DOMAIN = host;
ROOT_URL = "https://${host}/";
HTTP_ADDRESS = "127.0.0.1";
HTTP_PORT = port;
STATIC_ROOT_PATH = staticDir;
};
repository = {
DEFAULT_PRIVATE = "private";
@ -135,10 +111,10 @@ in
};
"repository.signing" = {
SIGNING_KEY = signingKey;
SIGNING_NAME = "DTTHgit";
SIGNING_NAME = "DTTHGit";
SIGNING_EMAIL = "dtth-gitea@nkagami.me";
};
ui.THEMES = default-themes + "," + themes;
ui.THEMES = "auto,gitea,arc-green," + themes;
"ui.meta" = {
AUTHOR = "DTTHgit - Gitea instance for GTTH";
DESCRIPTION = "DTTHGit is a custom Gitea instance hosted for DTTH members only.";
@ -175,24 +151,13 @@ in
PATH = "${pkgs.git}/bin/git";
};
storage = {
STORAGE_TYPE = "minio";
MINIO_USE_SSL = "true";
MINIO_ENDPOINT = "60c0807121eb35ef52cdcd4a33735fa6.r2.cloudflarestorage.com";
MINIO_ACCESS_KEY_ID = "704c29ade7a8b438b77ab520da2799ca";
MINIO_BUCKET = "dtth-gitea";
MINIO_LOCATION = "auto";
MINIO_CHECKSUM_ALGORITHM = "md5"; # R2 moment
};
federation.ENABLED = true;
DEFAULT.APP_NAME = "DTTHGit";
};
stateDir = "/mnt/data/gitea";
secrets.mailer.PASSWD = secrets."gitea/mailer-password".path;
secrets.storage.MINIO_SECRET_ACCESS_KEY = config.sops.secrets."gitea/minio-secret-key".path;
mailerPasswordFile = secrets."gitea/mailer-password".path;
database = {
inherit user;
@ -210,23 +175,31 @@ in
};
# Set up gpg signing key
systemd.services.forgejo = {
systemd.services.gitea = {
path = with pkgs; [ gnupg ];
environment.GNUPGHOME = "${config.services.gitea.stateDir}/.gnupg";
# https://github.com/NixOS/nixpkgs/commit/93c1d370db28ad4573fb9890c90164ba55391ce7
serviceConfig.SystemCallFilter = mkForce "~@clock @cpu-emulation @debug @keyring @module @mount @obsolete @raw-io @reboot @setuid @swap";
preStart =
''
# Import the signing subkey
if cat ${config.services.forgejo.stateDir}/.gnupg/gpg.conf | grep -q ${signingKey}; then
echo "Keys already imported"
# imported
else
echo "Import your keys!"
${pkgs.gnupg}/bin/gpg --quiet --import ${secrets."gitea/signing-key".path}
echo "trusted-key ${signingKey}" >> ${config.services.forgejo.stateDir}/.gnupg/gpg.conf
exit 1
fi
'';
preStart = ''
# Import the signing subkey
if cat ${config.services.gitea.stateDir}/.gnupg/gpg.conf | grep -q ${signingKey}; then
echo "Keys already imported"
# imported
else
echo "Import your keys!"
${pkgs.gnupg}/bin/gpg --quiet --import ${secrets."gitea/signing-key".path}
echo "trusted-key ${signingKey}" >> ${config.services.gitea.stateDir}/.gnupg/gpg.conf
exit 1
fi
# Copy icons
mkdir -p ${config.services.gitea.stateDir}/custom/public/img
install -m 0644 ${./gitea/img}/* ${config.services.gitea.stateDir}/custom/public/img
# Copy the themes
mkdir -p ${config.services.gitea.stateDir}/custom/public/css
env PATH=${pkgs.gzip}/bin:${pkgs.gnutar}/bin:$PATH \
tar -xvf ${catppuccinThemes} -C ${config.services.gitea.stateDir}/custom/public/css/
'';
};
}

View file

@ -9,11 +9,9 @@
swapDevices = [{ device = "/var/swapfile"; size = 4 * 1024; priority = 1024; }];
zramSwap.enable = true;
# volumes
services.btrfs.autoScrub.enable = true;
fileSystems.data = {
device = "/dev/disk/by-id/scsi-0HC_Volume_101470796";
fsType = "btrfs";
device = "/dev/disk/by-id/scsi-0HC_Volume_31812942";
fsType = "ext4";
mountPoint = "/mnt/data";
options = [ "compress=zstd" ];
};
}

View file

@ -27,8 +27,7 @@ rec {
noCloudflare = true;
};
systemd.services.headscale.requires = [ "postgresql.service" "arion-authentik.service" ];
systemd.services.headscale.after = [ "postgresql.service" "arion-authentik.service" ];
systemd.services.headscale.requires = [ "postgresql.service" ];
services.headscale = {
enable = true;
inherit port;
@ -36,25 +35,23 @@ rec {
settings = {
server_url = "https://hs.dtth.ch";
database.type = "postgres";
database.postgres = {
host = "/var/run/postgresql"; # find out yourself
user = "headscale";
name = "headscale";
};
db_type = "postgres";
db_host = "/var/run/postgresql"; # find out yourself
db_user = "headscale";
db_name = "headscale";
dns = {
base_domain = "dtth.ts";
dns_config = {
base_domain = host;
};
noise = {
private_key_path = "/var/lib/headscale/noise_private.key";
};
prefixes = {
v6 = "fd7a:115c:a1e0::/48";
v4 = "100.64.0.0/10";
};
ip_prefixes = [
"fd7a:115c:a1e0::/48"
"100.64.0.0/10"
];
derp.paths = [
secrets."headscale/derp-servers/vnm".path

View file

@ -28,6 +28,7 @@ in
cloud.traefik.hosts.invidious = { host = "invi.dtth.ch"; port = 61191; };
services.invidious = {
enable = true;
package = pkgs.unstable.invidious;
domain = "invi.dtth.ch";
port = 61191;
extraSettingsFile = config.sops.secrets.invidious.path;

View file

@ -42,7 +42,7 @@ in
systemd.services.miniflux = {
description = "Miniflux service";
wantedBy = [ "multi-user.target" ];
after = [ "network.target" "postgresql.service" ];
after = [ "network.target" ];
requires = [ "postgresql.service" ];
serviceConfig = {

View file

@ -1,78 +0,0 @@
{ config, lib, pkgs, ... }:
let
secrets = config.sops.secrets;
host = "n8n.dtth.ch";
db = "n8n";
user = db;
port = 23412;
dataFolder = "/mnt/data/n8n";
plugins = pkgs.callPackage ./n8n/plugins/package.nix { };
in
{
sops.secrets."n8n/env" = { reloadUnits = [ "n8n.service" ]; };
cloud.postgresql.databases = [ db ];
cloud.traefik.hosts.n8n = {
inherit port host;
};
# users
users.users."${user}" = {
group = "${user}";
isSystemUser = true;
};
users.groups."${user}" = { };
services.n8n = {
enable = true;
webhookUrl = "https://${host}";
};
systemd.services.n8n = {
environment = {
# Database
DB_TYPE = "postgresdb";
DB_POSTGRESDB_DATABASE = db;
DB_POSTGRESDB_HOST = "/var/run/postgresql";
DB_POSTGRESDB_USER = db;
# Deployment
N8N_EDITOR_BASE_URL = "https://${host}";
N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS = "true";
N8N_USER_FOLDER = lib.mkForce dataFolder;
HOME = lib.mkForce dataFolder;
N8N_HOST = host;
N8N_PORT = toString port;
N8N_LISTEN_ADDRESS = "127.0.0.1";
N8N_HIRING_BANNER_ENABLED = "false";
N8N_PROXY_HOPS = "1";
# Logs
N8N_LOG_LEVEL = "debug";
# License
N8N_HIDE_USAGE_PAGE = "true";
# Security
N8N_BLOCK_ENV_ACCESS_IN_NODE = "true";
# Timezone
GENERIC_TIMEZONE = "Europe/Berlin";
};
serviceConfig = {
EnvironmentFile = [ secrets."n8n/env".path ];
User = user;
DynamicUser = lib.mkForce false;
ReadWritePaths = [ dataFolder ];
# ReadOnlyPaths = [ "/var/run/postgresql" ];
};
unitConfig.RequiresMountsFor = [ dataFolder ];
};
systemd.tmpfiles.settings."10-n8n" = {
${dataFolder}.d = {
user = user;
group = user;
mode = "0700";
};
"${dataFolder}/.n8n/nodes"."L+" = {
argument = "${plugins}";
};
};
}

View file

@ -1 +0,0 @@
node_modules

File diff suppressed because it is too large Load diff

View file

@ -1,8 +0,0 @@
{
"name": "n8n-plugins",
"version": "1.0.0",
"dependencies": {
"n8n-nodes-turndown-html-to-markdown": "^1.0.5",
"n8n-nodes-vikunja": "^0.2.0"
}
}

View file

@ -1,4 +0,0 @@
{ nodejs, importNpmLock }: importNpmLock.buildNodeModules {
inherit nodejs;
npmRoot = ./.;
}

View file

@ -0,0 +1,67 @@
{ lib, pkgs, config, ... }:
with lib;
let
user = "nextcloud";
host = "cloud.dtth.ch";
port = 61155;
secrets = config.sops.secrets;
in
{
sops.secrets."nextcloud/admin-password" = { owner = user; };
sops.secrets."nextcloud/minio-secret-key" = { owner = user; key = "minio-secret-key"; };
# database
cloud.postgresql.databases = [ user ];
# traefik
cloud.traefik.hosts.nextcloud = {
inherit port host;
};
systemd.services.nextcloud.requires = [ "postgresql.service" ];
services.nextcloud = {
enable = true;
hostName = host;
package = pkgs.nextcloud26;
enableBrokenCiphersForSSE = false;
home = "/mnt/data/nextcloud";
https = true;
database.createLocally = false;
extraApps = with pkgs.nextcloud26Packages.apps; {
inherit calendar contacts deck forms groupfolders news tasks;
sociallogin = pkgs.fetchNextcloudApp rec {
url = "https://github.com/zorn-v/nextcloud-social-login/releases/download/v5.4.3/release.tar.gz";
sha256 = "sha256-ZKwtF9j9WFIk3MZgng9DmN00A73S2Rb4qbehL9adaZo=";
};
};
config = {
# Database
dbtype = "pgsql";
dbname = user;
dbuser = user;
dbhost = "/run/postgresql";
# User
adminuser = "nki";
adminpassFile = secrets."nextcloud/admin-password".path;
# General
overwriteProtocol = "https";
defaultPhoneRegion = "VN";
objectstore.s3 = {
enable = true;
bucket = "nextcloud-dtth";
autocreate = true;
key = "minio";
secretFile = config.sops.secrets."nextcloud/minio-secret-key".path;
hostname = "s3.dtth.ch";
port = 443;
useSsl = true;
usePathStyle = true;
region = "us-east-1";
};
};
};
services.nginx.virtualHosts.${host}.listen = [{ inherit port; addr = "127.0.0.1"; }];
}

View file

@ -1,56 +0,0 @@
{ config, pkgs, ... }: {
sops.secrets.authentik-oidc-client-secret = { owner = "outline"; };
sops.secrets."outline/smtp-password" = { owner = "outline"; };
sops.secrets."outline/s3-secret-key" = { owner = "outline"; };
services.outline = {
enable = true;
package = pkgs.outline.overrideAttrs (attrs: {
patches = attrs.patches or [ ] ++ [
../modules/cloud/outline/dtth-wiki.patch
../modules/cloud/outline/r2.patch
];
});
databaseUrl = "postgres://outline:outline@localhost/outline?sslmode=disable";
redisUrl = "local";
publicUrl = "https://wiki.dtth.ch";
port = 18729;
storage = {
accessKey = "6ef730e13f172d2ed6ed77f0b5b9bad9";
secretKeyFile = config.sops.secrets."outline/s3-secret-key".path;
region = "auto";
uploadBucketUrl = "https://60c0807121eb35ef52cdcd4a33735fa6.r2.cloudflarestorage.com";
uploadBucketName = "dtth-outline";
uploadMaxSize = 50 * 1024 * 1000;
};
maximumImportSize = 50 * 1024 * 1000;
oidcAuthentication = {
clientId = "3a0c10e00cdcb4a1194315577fa208a747c1a5f7";
clientSecretFile = config.sops.secrets.authentik-oidc-client-secret.path;
authUrl = "https://auth.dtth.ch/application/o/authorize/";
tokenUrl = "https://auth.dtth.ch/application/o/token/";
userinfoUrl = "https://auth.dtth.ch/application/o/userinfo/";
displayName = "DTTH Account";
};
smtp = {
fromEmail = "DTTH Wiki <dtth.wiki@nkagami.me>";
replyEmail = "";
host = "mx1.nkagami.me";
username = "dtth.wiki@nkagami.me";
passwordFile = config.sops.secrets."outline/smtp-password".path;
port = 465;
secure = true;
};
forceHttps = false;
};
cloud.postgresql.databases = [ "outline" ];
systemd.services.outline.requires = [ "postgresql.service" ];
systemd.services.outline.environment = {
AWS_S3_R2 = "true";
AWS_S3_R2_PUBLIC_URL = "https://s3.wiki.dtth.ch";
};
cloud.traefik.hosts.outline = { host = "wiki.dtth.ch"; port = 18729; };
}

View file

@ -3,14 +3,12 @@ let
secrets = config.sops.secrets;
cfg = config.services.peertube;
user = "peertube";
host = "peertube.dtth.ch";
dataFolder = "/mnt/data/peertube";
port = 19878;
in
{
sops.secrets."peertube" = { owner = cfg.user; restartUnits = [ "peertube.service" ]; };
sops.secrets."peertube-env" = { owner = cfg.user; restartUnits = [ "peertube.service" ]; };
sops.secrets."peertube" = { owner = cfg.user; };
sops.secrets."peertube-env" = { owner = cfg.user; };
# database
cloud.postgresql.databases = [ "peertube" ];
# traefik
@ -59,6 +57,12 @@ in
settings.client.videos = {
resumable_upload.max_chunk_size = "90MB";
};
settings.storage = {
storyboards = "/var/lib/peertube/storage/storyboards/";
tmp = "/mnt/data/peertube/tmp/";
tmp_persistent = "/mnt/data/peertube/tmp_persistent/";
web_videos = "/mnt/data/peertube/web-videos/";
};
# Trust proxy
settings.trust_proxy = [ "loopback" ] ++ config.services.traefik.staticConfigOptions.entrypoints.https.forwardedHeaders.trustedIPs;
@ -72,22 +76,5 @@ in
dataDirs = [ "/var/lib/peertube" "/mnt/data/peertube" ];
};
systemd.services.peertube = {
requires = [ "arion-authentik.service" ];
after = [ "arion-authentik.service" ];
unitConfig.RequiresMountsFor = [ dataFolder ];
};
systemd.tmpfiles.settings."10-peertube" = {
# The service hard-codes a lot of paths here, so it's nicer if we just symlink
"/var/lib/peertube"."L+" = {
argument = dataFolder;
};
${dataFolder}."d" = {
user = user;
group = user;
mode = "0700";
};
};
}

File diff suppressed because one or more lines are too long

View file

@ -1,99 +0,0 @@
{ pkgs, lib, config, ... }:
let
secrets = config.sops.secrets;
host = "kanban.dtth.ch";
user = "vikunja";
port = 12785;
storageMount = "/mnt/data/vikunja";
in
{
sops.secrets."vikunja/env" = { restartUnits = [ "vikunja.service" ]; };
sops.secrets."vikunja/provider-clientsecret" = { restartUnits = [ "vikunja.service" ]; };
cloud.postgresql.databases = [ user ];
cloud.traefik.hosts.vikunja = {
inherit port host;
};
# users
users.users."${user}" = {
group = "${user}";
isSystemUser = true;
};
users.groups."${user}" = { };
services.vikunja = {
inherit port;
enable = true;
frontendScheme = "https";
frontendHostname = host;
environmentFiles = [ secrets."vikunja/env".path ];
database = {
type = "postgres";
host = "/var/run/postgresql";
user = user;
database = user;
};
settings = {
service = {
publicurl = "https://${host}";
enableregistration = false;
enablepublicteams = true;
};
mailer = {
enabled = true;
host = "mx1.nkagami.me";
port = 465;
forcessl = true;
};
files.basepath = lib.mkForce storageMount;
migration = {
todoist.enable = true;
trello.enable = true;
};
backgrounds.providers.unsplash.enabled = true;
auth = {
local.enabled = false;
openid = {
enabled = true;
providers.authentik = {
name = "DTTH Discord Account";
authurl = "https://auth.dtth.ch/application/o/vikunja/";
logouturl = "https://auth.dtth.ch/application/o/vikunja/end-session/";
clientid = "GvCIBtdE2ZRbAo5BJzw4FbZjer7umJlaROT1Pvlp";
scope = "openid profile email vikunja_scope";
};
};
};
defaultsettings = {
avatar_provider = "gravatar";
week_start = 1;
language = "VN";
timezone = "Asia/Ho_Chi_Minh";
};
};
};
systemd.services.vikunja = {
serviceConfig.LoadCredential = [ "VIKUNJA_AUTH_OPENID_PROVIDERS_AUTHENTIK_CLIENTSECRET_FILE:${secrets."vikunja/provider-clientsecret".path}" ];
serviceConfig.User = user;
serviceConfig.DynamicUser = lib.mkForce false;
serviceConfig.ReadWritePaths = [ storageMount ];
environment.VIKUNJA_AUTH_OPENID_PROVIDERS_AUTHENTIK_CLIENTSECRET_FILE = "%d/VIKUNJA_AUTH_OPENID_PROVIDERS_AUTHENTIK_CLIENTSECRET_FILE";
unitConfig = {
RequiresMountsFor = [ storageMount ];
};
};
systemd.tmpfiles.settings."10-vikunja".${storageMount}.d = {
user = user;
group = user;
mode = "0700";
};
}

View file

@ -11,7 +11,7 @@ in
# traefik
cloud.traefik.hosts.writefreely-dtth = { inherit host port; };
sops.secrets."writefreely-oauth-secret" = { owner = user; };
sops.secrets."writefreely-dtth" = { owner = user; };
users.users.${user} = {
isSystemUser = true;
@ -53,24 +53,22 @@ in
monetization = false;
};
"oauth.generic" = { };
};
oauth = {
enable = true;
clientId = "rpoTTr2Wz0h4EgOSCHe0G85O8DCQDMup7JW9U9fV";
clientSecretFile = config.sops.secrets."writefreely-oauth-secret".path;
host = "https://auth.dtth.ch";
displayName = "DTTH";
tokenEndpoint = "/application/o/token/";
inspectEndpoint = "/application/o/userinfo/";
authEndpoint = "/application/o/authorize/";
scopes = [ "email" "openid" "profile" ];
mapUserId = "nickname";
mapUsername = "preferred_username";
mapDisplayName = "name";
"oauth.generic" = {
client_id = "rpoTTr2Wz0h4EgOSCHe0G85O8DCQDMup7JW9U9fV";
host = "https://auth.dtth.ch";
display_name = "DTTH";
token_endpoint = "/application/o/token/";
inspect_endpoint = "/application/o/userinfo/";
auth_endpoint = "/application/o/authorize/";
scope = "email openid profile";
map_user_id = "nickname";
map_username = "preferred_username";
map_display_name = "name";
allow_registration = true;
};
};
extraSettingsFile = config.sops.secrets."writefreely-dtth".path;
database.type = "sqlite3";

View file

@ -15,7 +15,8 @@ let
if value == true then "true" else "false"
else
toString value);
in "${key} = ${value'}";
in
"${key} = ${value'}";
};
cfg = config.nki.services.writefreely;
@ -31,19 +32,20 @@ let
host = cfg.settings.app.host or "${hostProtocol}://${cfg.host}";
};
database = if cfg.database.type == "sqlite3" then {
type = "sqlite3";
filename = cfg.settings.database.filename or "writefreely.db";
database = cfg.database.name;
} else {
type = "mysql";
username = cfg.database.user;
password = "#dbpass#";
database = cfg.database.name;
host = cfg.database.host;
port = cfg.database.port;
tls = cfg.database.tls;
};
database =
if cfg.database.type == "sqlite3" then {
type = "sqlite3";
filename = cfg.settings.database.filename or "writefreely.db";
database = cfg.database.name;
} else {
type = "mysql";
username = cfg.database.user;
password = "#dbpass#";
database = cfg.database.name;
host = cfg.database.host;
port = cfg.database.port;
tls = cfg.database.tls;
};
server = cfg.settings.server or { } // {
bind = cfg.settings.server.bind or "localhost";
@ -56,24 +58,6 @@ let
cfg.settings.server.pages_parent_dir or cfg.package.src;
keys_parent_dir = cfg.settings.server.keys_parent_dir or cfg.stateDir;
};
"oauth.generic" = cfg.settings."oauth.generic" or { } // (if cfg.oauth.enable then {
client_id = cfg.oauth.clientId;
client_secret = "#oauth_client_secret#";
host = cfg.oauth.host;
display_name = cfg.oauth.displayName;
callback_proxy = cfg.oauth.callbackProxy;
callback_proxy_api = cfg.oauth.callbackProxyApi;
token_endpoint = cfg.oauth.tokenEndpoint;
inspect_endpoint = cfg.oauth.inspectEndpoint;
auth_endpoint = cfg.oauth.authEndpoint;
scope = lib.concatStringsSep " " cfg.oauth.scopes;
allow_disconnect = cfg.oauth.allowDisconnect;
map_user_id = cfg.oauth.mapUserId;
map_username = cfg.oauth.mapUsername;
map_display_name = cfg.oauth.mapDisplayName;
map_email = cfg.oauth.mapEmail;
} else { });
};
configFile = format.generate "config.ini" settings;
@ -107,14 +91,10 @@ let
optionalString (cfg.database.passwordFile != null)
"$(head -n1 ${cfg.database.passwordFile})"
}
oauth_client_secret=${
optionalString cfg.oauth.enable
"$(head -n1 ${cfg.oauth.clientSecretFile})"
}
cp -f ${configFile} '${cfg.stateDir}/config.ini'
install -m 0660 ${configFile} '${cfg.stateDir}/config.ini'
sed -e "s,#dbpass#,$db_pass,g" -i '${cfg.stateDir}/config.ini'
sed -e "s,#oauth_client_secret#,$oauth_client_secret,g" -i '${cfg.stateDir}/config.ini'
${if cfg.extraSettingsFile != null then "cat ${cfg.extraSettingsFile} >> ${cfg.stateDir}/config.ini" else ""}
chmod 440 '${cfg.stateDir}/config.ini'
${text}
@ -143,7 +123,7 @@ let
withConfigFile ''
query () {
local result=$(${sqlite}/bin/sqlite3 \
'${cfg.stateDir}/${settings.database.filename}' \
'${cfg.stateDir}/${settings.database.filename}'
"$1" \
)
@ -152,46 +132,53 @@ let
${text}
'';
in {
in
{
options.nki.services.writefreely = {
enable =
lib.mkEnableOption "Writefreely, build a digital writing community";
lib.mkEnableOption (lib.mdDoc "Writefreely, build a digital writing community");
package = lib.mkOption {
type = lib.types.package;
default = pkgs.writefreely;
defaultText = lib.literalExpression "pkgs.writefreely";
description = "Writefreely package to use.";
description = lib.mdDoc "Writefreely package to use.";
};
stateDir = mkOption {
type = types.path;
default = "/var/lib/writefreely";
description = "The state directory where keys and data are stored.";
description = lib.mdDoc "The state directory where keys and data are stored.";
};
user = mkOption {
type = types.str;
default = "writefreely";
description = "User under which Writefreely is ran.";
description = lib.mdDoc "User under which Writefreely is ran.";
};
group = mkOption {
type = types.str;
default = "writefreely";
description = "Group under which Writefreely is ran.";
description = lib.mdDoc "Group under which Writefreely is ran.";
};
host = mkOption {
type = types.str;
default = "";
description = "The public host name to serve.";
description = lib.mdDoc "The public host name to serve.";
example = "example.com";
};
extraSettingsFile = mkOption {
type = types.nullOr types.path;
default = null;
description = lib.mdDoc "Additional configs to be appended to the config file";
};
settings = mkOption {
default = { };
description = ''
description = lib.mdDoc ''
Writefreely configuration ({file}`config.ini`). Refer to
<https://writefreely.org/docs/latest/admin/config>
for details.
@ -205,7 +192,7 @@ in {
theme = mkOption {
type = types.str;
default = "write";
description = "The theme to apply.";
description = lib.mdDoc "The theme to apply.";
};
};
@ -214,7 +201,7 @@ in {
type = types.port;
default = if cfg.nginx.enable then 18080 else 80;
defaultText = "80";
description = "The port WriteFreely should listen on.";
description = lib.mdDoc "The port WriteFreely should listen on.";
};
};
};
@ -225,158 +212,74 @@ in {
type = mkOption {
type = types.enum [ "sqlite3" "mysql" ];
default = "sqlite3";
description = "The database provider to use.";
description = lib.mdDoc "The database provider to use.";
};
name = mkOption {
type = types.str;
default = "writefreely";
description = "The name of the database to store data in.";
description = lib.mdDoc "The name of the database to store data in.";
};
user = mkOption {
type = types.nullOr types.str;
default = if cfg.database.type == "mysql" then "writefreely" else null;
defaultText = "writefreely";
description = "The database user to connect as.";
description = lib.mdDoc "The database user to connect as.";
};
passwordFile = mkOption {
type = types.nullOr types.path;
default = null;
description = "The file to load the database password from.";
description = lib.mdDoc "The file to load the database password from.";
};
host = mkOption {
type = types.str;
default = "localhost";
description = "The database host to connect to.";
description = lib.mdDoc "The database host to connect to.";
};
port = mkOption {
type = types.port;
default = 3306;
description = "The port used when connecting to the database host.";
description = lib.mdDoc "The port used when connecting to the database host.";
};
tls = mkOption {
type = types.bool;
default = false;
description = "Whether or not TLS should be used for the database connection.";
description =
lib.mdDoc "Whether or not TLS should be used for the database connection.";
};
migrate = mkOption {
type = types.bool;
default = true;
description = "Whether or not to automatically run migrations on startup.";
description =
lib.mdDoc "Whether or not to automatically run migrations on startup.";
};
createLocally = mkOption {
type = types.bool;
default = false;
description = ''
description = lib.mdDoc ''
When {option}`services.writefreely.database.type` is set to
`"mysql"`, this option will enable the MySQL service locally.
'';
};
};
oauth = {
enable = lib.mkEnableOption "Enable generic OAuth authentication";
clientId = mkOption {
type = types.str;
description = "The client ID associated with WriteFreely in the OAuth provider application.";
};
clientSecretFile = mkOption {
type = types.str;
description = "The file to load the OAuth client secret from.";
};
host = mkOption {
type = types.str;
description = "The base url of the OAuth provider application, including the protocol.";
example = "https://example.com";
};
displayName = mkOption {
type = types.str;
description = "The human-readable name of the OAuth service that appears on the login button, will appear as `Log in with [display_name]`.";
};
callbackProxy = mkOption {
type = types.str;
default = "";
description = "The url of an inbound proxy that sits in front of the default `/oauth/callback/generic` endpoint. Use if you want the OAuth callback to be somewhere other than that generic location. Default is blank.";
example = "https://example.com/whatever/path";
};
callbackProxyApi = mkOption {
type = types.str;
default = "";
description = "The url of an outbound proxy to send your OAuth requests through. Default is blank.";
example = "https://my-proxy.example.com";
};
tokenEndpoint = mkOption {
type = types.str;
description = "The API endpoint of the OAuth provider implementation to obtain an access token by presenting an authorization grant or refresh token. This is a fragment of a url, appended to host as described above.";
example = "/oauth/token";
};
inspectEndpoint = mkOption {
type = types.str;
description = "The API endpoint of the OAuth provider that returns basic user info given their authentication information. This is a fragment of a url, appended to host as described above.";
example = "/oauth/userinfo";
};
authEndpoint = mkOption {
type = types.str;
description = "The API endpoint of the OAuth provider that returns an authorization grant. This is a fragment of a url, appended to host as described above.";
example = "public";
};
scopes = mkOption {
type = types.listOf types.str;
default = [ "read_user" ];
description = "A scope or set of scopes required by some OAuth providers. This will usually be blank in this config file, and is set to `read_user` by default.";
};
allowDisconnect = mkOption {
type = types.bool;
default = false;
description = "Whether or not an individual user is allowed to disconnect this OAuth provider from their account.";
};
mapUserId = mkOption {
type = types.str;
default = "";
defaultText = "<none>";
description = "Use this User ID key in the provider's user info, instead of the default key (user_id).";
};
mapUsername = mkOption {
type = types.str;
default = "";
defaultText = "<none>";
description = "Use this Username key in the provider's user info, instead of the default key (username)";
};
mapDisplayName = mkOption {
type = types.str;
default = "";
defaultText = "<none>";
description = "Use this Display Name key in the provider's user info, instead of the default key (*none*)";
};
mapEmail = mkOption {
type = types.str;
default = "";
defaultText = "<none>";
description = "Use this Email key in the provider's user info, instead of the default key (email)";
};
};
admin = {
name = mkOption {
type = types.nullOr types.str;
description = "The name of the first admin user.";
description = lib.mdDoc "The name of the first admin user.";
default = null;
};
initialPasswordFile = mkOption {
type = types.path;
description = ''
description = lib.mdDoc ''
Path to a file containing the initial password for the admin user.
If not provided, the default password will be set to `nixos`.
'';
@ -389,13 +292,14 @@ in {
enable = mkOption {
type = types.bool;
default = false;
description = "Whether or not to enable and configure nginx as a proxy for WriteFreely.";
description =
lib.mdDoc "Whether or not to enable and configure nginx as a proxy for WriteFreely.";
};
forceSSL = mkOption {
type = types.bool;
default = false;
description = "Whether or not to force the use of SSL.";
description = lib.mdDoc "Whether or not to force the use of SSL.";
};
};
@ -403,7 +307,8 @@ in {
enable = mkOption {
type = types.bool;
default = false;
description = "Whether or not to automatically fetch and configure SSL certs.";
description =
lib.mdDoc "Whether or not to automatically fetch and configure SSL certs.";
};
};
};
@ -439,12 +344,11 @@ in {
optionalAttrs (cfg.group == "writefreely") { writefreely = { }; };
};
systemd.tmpfiles.settings."10-writefreely".${cfg.stateDir}.d = {
inherit (cfg) user group;
mode = "0750";
};
systemd.tmpfiles.rules =
[ "d '${cfg.stateDir}' 0750 ${cfg.user} ${cfg.group} - -" ];
systemd.services.writefreely = {
path = with pkgs; [ openssl ];
after = [ "network.target" ]
++ optional isSqlite "writefreely-sqlite-init.service"
++ optional isMysql "writefreely-mysql-init.service"
@ -489,27 +393,29 @@ in {
cfg.admin.initialPasswordFile;
};
script = let
migrateDatabase = optionalString cfg.database.migrate ''
${cfg.package}/bin/writefreely -c '${cfg.stateDir}/config.ini' db migrate
'';
script =
let
migrateDatabase = optionalString cfg.database.migrate ''
${cfg.package}/bin/writefreely -c '${cfg.stateDir}/config.ini' db migrate
'';
createAdmin = optionalString (cfg.admin.name != null) ''
if [[ $(query "SELECT COUNT(*) FROM users") == 0 ]]; then
admin_pass=$(head -n1 ${cfg.admin.initialPasswordFile})
createAdmin = optionalString (cfg.admin.name != null) ''
if [[ $(query "SELECT COUNT(*) FROM users") == 0 ]]; then
admin_pass=$(head -n1 ${cfg.admin.initialPasswordFile})
${cfg.package}/bin/writefreely -c '${cfg.stateDir}/config.ini' --create-admin ${cfg.admin.name}:$admin_pass
${cfg.package}/bin/writefreely -c '${cfg.stateDir}/config.ini' --create-admin ${cfg.admin.name}:$admin_pass
fi
'';
in
withSqlite ''
if ! test -f '${settings.database.filename}'; then
${cfg.package}/bin/writefreely -c '${cfg.stateDir}/config.ini' db init
fi
${migrateDatabase}
${createAdmin}
'';
in withSqlite ''
if ! test -f '${settings.database.filename}'; then
${cfg.package}/bin/writefreely -c '${cfg.stateDir}/config.ini' db init
fi
${migrateDatabase}
${createAdmin}
'';
};
systemd.services.writefreely-mysql-init = mkIf isMysql {
@ -526,36 +432,38 @@ in {
cfg.admin.initialPasswordFile;
};
script = let
updateUser = optionalString isMysqlLocal ''
# WriteFreely currently *requires* a password for authentication, so we
# need to update the user in MySQL accordingly. By default MySQL users
# authenticate with auth_socket or unix_socket.
# See: https://github.com/writefreely/writefreely/issues/568
${config.services.mysql.package}/bin/mysql --skip-column-names --execute "ALTER USER '${cfg.database.user}'@'localhost' IDENTIFIED VIA unix_socket OR mysql_native_password USING PASSWORD('$db_pass'); FLUSH PRIVILEGES;"
'';
script =
let
updateUser = optionalString isMysqlLocal ''
# WriteFreely currently *requires* a password for authentication, so we
# need to update the user in MySQL accordingly. By default MySQL users
# authenticate with auth_socket or unix_socket.
# See: https://github.com/writefreely/writefreely/issues/568
${config.services.mysql.package}/bin/mysql --skip-column-names --execute "ALTER USER '${cfg.database.user}'@'localhost' IDENTIFIED VIA unix_socket OR mysql_native_password USING PASSWORD('$db_pass'); FLUSH PRIVILEGES;"
'';
migrateDatabase = optionalString cfg.database.migrate ''
${cfg.package}/bin/writefreely -c '${cfg.stateDir}/config.ini' db migrate
'';
migrateDatabase = optionalString cfg.database.migrate ''
${cfg.package}/bin/writefreely -c '${cfg.stateDir}/config.ini' db migrate
'';
createAdmin = optionalString (cfg.admin.name != null) ''
if [[ $(query 'SELECT COUNT(*) FROM users') == 0 ]]; then
admin_pass=$(head -n1 ${cfg.admin.initialPasswordFile})
${cfg.package}/bin/writefreely -c '${cfg.stateDir}/config.ini' --create-admin ${cfg.admin.name}:$admin_pass
createAdmin = optionalString (cfg.admin.name != null) ''
if [[ $(query 'SELECT COUNT(*) FROM users') == 0 ]]; then
admin_pass=$(head -n1 ${cfg.admin.initialPasswordFile})
${cfg.package}/bin/writefreely -c '${cfg.stateDir}/config.ini' --create-admin ${cfg.admin.name}:$admin_pass
fi
'';
in
withMysql ''
${updateUser}
if [[ $(query "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = '${cfg.database.name}'") == 0 ]]; then
${cfg.package}/bin/writefreely -c '${cfg.stateDir}/config.ini' db init
fi
${migrateDatabase}
${createAdmin}
'';
in withMysql ''
${updateUser}
if [[ $(query "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = '${cfg.database.name}'") == 0 ]]; then
${cfg.package}/bin/writefreely -c '${cfg.stateDir}/config.ini' db init
fi
${migrateDatabase}
${createAdmin}
'';
};
services.mysql = mkIf isMysqlLocal {
@ -589,3 +497,4 @@ in {
};
};
}

View file

@ -12,6 +12,7 @@
# Fonts
../modules/personal/fonts
# Some PAM stuff
../modules/services/pam/gnome-keyring.nix
../modules/services/swaylock.nix
];
# Use the latest kernel
@ -115,6 +116,9 @@
pciutils
];
# Gnome-keyring is useful
services.gnome.gnome-keyring.enable = true;
## Environment variables
environment.variables = {
# Input method overrides
@ -141,6 +145,7 @@
# };
# List services that you want to enable:
nki.services.pam.enableGnomeKeyring = true;
services.input-remapper.enable = true;
services.swaylock.enable = true;

View file

@ -36,4 +36,6 @@
[{ device = "/dev/disk/by-uuid/2694d189-2ff6-4719-a449-367c52ed3ad6"; }];
hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
hardware.opengl.enable = true;
}

View file

@ -4,14 +4,6 @@
{ config, pkgs, lib, ... }:
let
vmware = { pkgs, ... }: {
environment.systemPackages = [ pkgs.vmware-horizon-client ];
virtualisation.vmware.host = {
enable = true;
};
};
in
{
imports =
[
@ -21,28 +13,9 @@ in
../modules/personal/fonts
# Encrypted DNS
../modules/services/edns
vmware
];
# Secrets
common.linux.sops.enable = true;
common.linux.sops.file = ./secrets.yaml;
# Build farm
sops.secrets."nix-build-farm/private-key" = { mode = "0400"; };
services.nix-build-farm.hostname = "yoga";
services.nix-build-farm.privateKeyFile = config.sops.secrets."nix-build-farm/private-key".path;
## tinc
sops.secrets."tinc-private-key" = { };
services.my-tinc = {
enable = true;
hostName = "yoga";
ed25519PrivateKey = config.sops.secrets."tinc-private-key".path;
};
services.desktopManager.plasma6.enable = true;
services.xserver.desktopManager.plasma5.enable = true;
# Power Management
services.upower = {
@ -80,7 +53,7 @@ in
services.printing.drivers = with pkgs; [ epfl-cups-drivers ];
# Enable touchpad support (enabled default in most desktopManager).
services.libinput.enable = true;
services.xserver.libinput.enable = true;
# Keyboard
services.input-remapper.enable = true;
services.input-remapper.serviceWantedBy = [ "multi-user.target" ];

View file

@ -13,11 +13,6 @@
boot.initrd.kernelModules = [ ];
boot.kernelModules = [ "kvm-intel" ];
boot.extraModulePackages = [ ];
boot.kernelParams = [
# Hibernation
"resume=UUID=b32d27bf-9df6-43c1-8b93-c0693811bf5b"
"resume_offset=9731998" # btrfs inspect-internal map-swapfile -r /var/swapfile
];
fileSystems."/" =
{

View file

@ -1,32 +0,0 @@
tinc-private-key: ENC[AES256_GCM,data:lzmisexQPfRlIMGqbmb+uqGtOPceQ3CJGlVOeOC6nbP/IDwkufSWtxugYmUwi9IJKwO0mldijiKWuG3p9005H++8567hhPy/bU7fA4vyVC+3UVGW6l0mE+yKQXTyI7kzxkXMCK5a4Q4rUJj544vU6pt75/mytfg+Cox2woGZAHZvJ/pRuHDe2t3R6w3EYYTu6x1w5azGnFvCOVdR6XPsGJA2p3oRnEpz64L7KD2QOdtm0YsfMnorH9FbvkZgNr927VbRnBRJ1QM=,iv:4K4w6ruQxtRGjmFnWszlXZKp36TuTTnrB0sDEE/tmrM=,tag:NBP897Sw84bvZTvo/+fVfA==,type:str]
nix-build-farm:
private-key: ENC[AES256_GCM,data:etqFl2T2atN2djxqktFRtrTGqsC61A+ZUd2yS0PLm5KPO2s2/k6XqQGac9rUWP86C1YGpTJhUMzYuOPGW4yNc0YmoeHVslxBR7nX8pubXabZNdB2YMm/yAgsdeeflo4slbxJ6+00eH0iCrtWcHtWbZafHnxojborZABOvCsODdx/ahJ4J9aHqf22cAqe9iJY3L0TgE+iazKS8OO+C/PTaQiV02NZjP8GajRMXzPVoYT7wz3u0t0q0m/t8FkhMIDl9QKL+kFUDeLEGoCBzR57JXLZiW1gJsRxbkP8hVIB3s4TQnhasxqQQlCJuqBSNFl/cGdBm/ADm/yi78VHQG7rUxUrFVDL4Aoidjp6GyoojLIEpdQjtlvC7RCLNpTibV6B71EB3obpjMmmIwfoDLT4jEWhXNx3b8DnMoa0Qh4ba+HBJf+XKA93B0qOJWwJzj4qH9uqBK3xPOGTkqQMmd9M1HYrStTcI/JUX0WvEMwk8xI8MZN/TsLij4w5i6NCwSqa8Dn2lyLK0BGp5C8RT8R4k6U2ieyY6lmxsGIe,iv:703rM/FQz65upd1JWTHNsjAXh2BeoknkALShKuHUsis=,tag:yAB6KJqpm1mOFT5GzKRPBw==,type:str]
sops:
kms: []
gcp_kms: []
azure_kv: []
hc_vault: []
age:
- recipient: age1vhjhmxura35apu5zdwg5ur5r40xay45ld9szh07dy0ph9chgsu7shfm4h9
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBseVN3ODV1YkZnNms0Y09l
dUFBK1EyOTEydWg5KzAwcjZVSG8yYlRDWlhRCklLS2ZJNlBvSlEyOGF2ZFg2UGVW
UC9LN0hxdmtGN3JlOWJaTU5hbGwvc2MKLS0tIGM1NGZxd1NoTXNacEJqMVlsbTdi
MytuNUNydmJYWFYyQk9DaHVuVk85cjAKScucMPO8pyMlSxFw09NqzqVmDYVEh5xT
4fSTAsMwIiuOyV7jvHYORxKWNMLr5t6fnj8+OFq5qUc//jNWf9pVuA==
-----END AGE ENCRYPTED FILE-----
- recipient: age1axvjllyv2gutngwmp3pvp4xtq2gqneldaq2c4nrzmaye0uwmk9lqsealdv
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBYNXp6MWZGb2dGdW1ML2xq
ZWMvSVdWalQ5Z2NzTWd3Z1AweXlXZnRwUWtRCkY1VFhPS0NtbFZKU0VCMlAvSmhG
N2NmdWxTUEpMb05Ld3p6MzhhRkdBc3cKLS0tIGQ0TmFxdk1GV205azRzZ0hUWitj
eitNc1E2SzY5bkUxNWtNczRsWWJaU2MKUIu9GT7zu0MvvnXxiQfLW9pQcxFKOwPm
VRU2k3XQkYjSDZX29DxrOzaPS/L3OYNyBYMyOW8GyMa2V12lMH6lPQ==
-----END AGE ENCRYPTED FILE-----
lastmodified: "2024-08-16T14:17:07Z"
mac: ENC[AES256_GCM,data:qrMyVDLhtK4URqrHFBx+08PMrFyfib4iH0y7iAeVB/oFGazjm3O5MeS9fNYJeONghuelux69nh2FRfSJHG/moEBcWlL68R4xbCb4he528P+n7mQnR54BNFJdT2oOra4bqO9n/4m2UA8jmA0veoqSrZUVjnmjftqOedjnRESY1L8=,iv:jql79ItwPcJg/nnbsUywOzWz/UJy0ZpY04pvEF290c4=,tag:XKrToym2dXdippnivoK1/Q==,type:str]
pgp: []
unencrypted_suffix: _unencrypted
version: 3.8.1

View file

@ -1,14 +1,19 @@
{ nixpkgs, nixpkgs-unstable, ... }@inputs:
{ nixpkgs, nixpkgs-unstable, nur, ... }@inputs:
let
overlay-unstable = final: prev: {
stable = import nixpkgs { config.allowUnfree = true; system = prev.system; };
unstable = import nixpkgs-unstable { config.allowUnfree = true; system = prev.system; };
x86 = import nixpkgs-unstable { system = prev.system; config.allowUnsupportedSystem = true; };
nixpkgs-peertube = import inputs.nixpkgs-peertube { system = prev.system; };
};
overlay-needs-unstable = final: prev: {
# override some packages that needs unstable that cannot be changed in the setup.
nix-direnv = prev.unstable.nix-direnv;
# Typst updates really quickly.
typst = final.unstable.typst;
typst-lsp = final.unstable.typst-lsp;
peertube = final.nixpkgs-peertube.peertube;
};
overlay-imported = final: prev: {
sway = prev.sway.override { sway-unwrapped = final.swayfx-unwrapped; };
@ -22,7 +27,7 @@ let
overlay-versioning = final: prev: {
gotosocial = prev.gotosocial.overrideAttrs (attrs: rec {
version = "0.17.1";
version = "0.14.2";
ldflags = [
"-s"
"-w"
@ -32,13 +37,13 @@ let
web-assets = final.fetchurl {
url = "https://github.com/superseriousbusiness/gotosocial/releases/download/v${version}/gotosocial_${version}_web-assets.tar.gz";
hash = "sha256-rGntLlIbgfCtdqpD7tnvAY8qwF+BpYbQWfAGMhdOTgY=";
hash = "sha256-3aSOP8BTHdlODQnZr6DOZuybLl+02SWgP9YZ21guAPU=";
};
src = final.fetchFromGitHub {
owner = "superseriousbusiness";
repo = "gotosocial";
rev = "v${version}";
hash = "sha256-oWWsCs9jgd244yzWhgLkuHp7kY0BQ8+Ay6KpuBVG+U8=";
hash = "sha256-oeOxP9FkGsOH66Uk946H0b/zggz536YvRRuo1cINxSM=";
};
postInstall = ''
tar xf ${web-assets}
@ -55,84 +60,72 @@ let
src = inputs.kakoune;
patches = [
# patches in the original package was already applied
# https://github.com/mawww/kakoune/pull/5108
(final.fetchpatch {
url = "https://github.com/mawww/kakoune/commit/64b3433905eeb33653ed617d61906ba68c686916.patch";
hash = "sha256-XYA4GcOEuWHsnDhMI0nXbg9Myv2o1UZ8qvzavIXbkJo=";
})
];
});
swayfx-unwrapped = prev.swayfx-unwrapped.overrideAttrs (attrs: {
patches = (attrs.patches or [ ]) ++ [
(final.fetchurl {
url = "https://patch-diff.githubusercontent.com/raw/WillPower3309/swayfx/pull/315.patch";
hash = "sha256-zamOLHUjlzRs8PytPTAzEsdzgVtK+HVziHgrhwPcB+E=";
})
];
});
librewolf = (prev.librewolf.override {
nativeMessagingHosts = with final; [ kdePackages.plasma-browser-integration ];
});
vikunja =
# builtins.seq
# (final.lib.assertMsg (prev.vikunja.version == "0.24.5") "Vikunja probably doesn't need custom versions anymore")
(final.callPackage ./packages/common/vikunja.nix { });
luminance = prev.luminance.overrideAttrs (attrs: {
nativeBuildInputs = attrs.nativeBuildInputs ++ [ final.wrapGAppsHook ];
buildInputs = attrs.buildInputs ++ [ final.glib ];
postInstall = attrs.postInstall + ''
glib-compile-schemas $out/share/glib-2.0/schemas
'';
});
};
overlay-libs = final: prev: {
libs.crane = inputs.crane.mkLib final;
libs.crane = inputs.crane.lib.${prev.system};
};
overlay-packages = final: prev: {
kak-tree-sitter = final.callPackage ./packages/common/kak-tree-sitter.nix { rustPlatform = final.unstable.rustPlatform; };
kak-lsp =
let
src = inputs.kak-lsp;
cargoArtifacts = final.libs.crane.buildDepsOnly { inherit src; };
in
final.libs.crane.buildPackage {
inherit src cargoArtifacts;
buildInputs = (with final;
lib.optionals stdenv.isDarwin (with darwin.apple_sdk.frameworks; [ Security SystemConfiguration CoreServices ])
) ++ (with final; [ libiconv ]);
};
zen-browser-bin = final.callPackage inputs.zen-browser.packages.${final.stdenv.system}.zen-browser.override {
wrap-firefox = opts: final.wrapFirefox (opts // {
nativeMessagingHosts = with final; [ kdePackages.plasma-browser-integration ];
});
zen-browser-unwrapped = final.callPackage inputs.zen-browser.packages.${final.stdenv.system}.zen-browser-unwrapped.override {
sources = inputs.zen-browser.inputs;
};
};
};
overlay-aarch64-linux = final: prev:
let
optionalOverride = pkg: alt:
if prev.stdenv.isLinux && prev.stdenv.isAarch64 then alt else pkg;
in
{
# See https://github.com/sharkdp/fd/issues/1085
fd = optionalOverride prev.fd (prev.fd.overrideAttrs (attrs: {
preBuild = ''
export JEMALLOC_SYS_WITH_LG_PAGE=16
'';
}));
# See https://www.reddit.com/r/AsahiLinux/comments/zqejue/kitty_not_working_with_mesaasahiedge/
kitty = optionalOverride prev.kitty (final.writeShellApplication {
name = "kitty";
runtimeInputs = [ ];
text = ''
MESA_GL_VERSION_OVERRIDE=3.3 MESA_GLSL_VERSION_OVERRIDE=330 ${prev.kitty}/bin/kitty "$@"
'';
});
# Zotero does not have their own aarch64-linux build
zotero = optionalOverride prev.zotero (final.callPackage ./packages/aarch64-linux/zotero.nix { });
# Typora for aarch64-linux only
typora = optionalOverride
(builtins.abort "no support for non-aarch64-linux")
(final.callPackage ./packages/aarch64-linux/typora.nix { });
};
overlay-asahi = inputs.nixos-m1.overlays.default;
in
[
# inputs.swayfx.inputs.scenefx.overlays.override
# inputs.swayfx.overlays.override
inputs.mpd-mpris.overlays.default
inputs.rust-overlay.overlays.default
inputs.youmubot.overlays.default
inputs.niri.overlays.niri
(import ./overlays/openrazer)
overlay-unstable
overlay-needs-unstable
overlay-packages
overlay-imported
overlay-versioning
overlay-libs
overlay-asahi
overlay-aarch64-linux
nur.overlay
(import ./packages/common)
inputs.mpd-mpris.overlays.default
inputs.swayfx.overlays.default
inputs.youmubot.overlays.default
# Bug fixes
] # we assign the overlay created before to the overlays of nixpkgs.

View file

@ -0,0 +1,31 @@
final: prev:
let
version = "3.3.0";
src = final.fetchFromGitHub {
owner = "openrazer";
repo = "openrazer";
rev = "v${version}";
sha256 = "sha256-lElE1nIiJ5fk2DupHu43tmxRjRsS5xeL1Yz/LuRlgtM=";
};
in
rec
{
openrazer-daemon = prev.openrazer-daemon.overrideAttrs (old: {
inherit src version;
});
python3 = prev.python3.override {
packageOverrides = self: super: {
openrazer-daemon = super.openrazer-daemon.overrideAttrs (old: {
inherit src version;
});
};
};
python3Packages = python3.pkgs;
linuxPackages_latest = prev.linuxPackages_latest.extend (self: super: {
openrazer = super.openrazer.overrideAttrs (old: {
inherit src version;
});
});
}

View file

@ -1,41 +1,20 @@
{ lib, rustPlatform, fetchFromSourcehut, symlinkJoin, clang, git, writeText, ... }:
{ lib, rustPlatform, fetchFromGitHub, symlinkJoin, clang, git, ... }:
let
src = fetchFromSourcehut {
owner = "~hadronized";
src = fetchFromGitHub {
owner = "phaazon";
repo = "kak-tree-sitter";
rev = "kak-tree-sitter-v1.1.2";
hash = "sha256-wBWfSyR8LGtug/mCD0bJ4lbdN3trIA/03AnCxZoEOSA=";
rev = "61cce127ca03e3c969df1ff46f41074a3c69be31";
hash = "sha256-wcgc1L6Y6obLTIonWLJzNK72fWW8oJ0yMEfGotCg5b8=";
};
kak-tree-sitter = rustPlatform.buildRustPackage {
kak-tree-sitter = rustPlatform.buildRustPackage rec {
inherit src;
pname = "kak-tree-sitter";
version = "1.1.2";
cargoHash = "sha256-OQPUWqJAts8DbFNSsC/CmMCbuZ9TVxRTR05O7oiodKI=";
version = "0.5.5-${lib.substring 0 6 src.rev}";
cargoHash = "sha256-Ozzcn4k+1Q+50zxCy9Flvv8vZKNcAesrHT/izVAgn54=";
cargoBuildOptions = [ "--package" "kak-tree-sitter" "--package" "ktsctl" ];
nativeBuildInputs = [ clang git ];
patches = [
# Allow absolute-path style repos
(writeText "resources.patch" ''
diff --git a/ktsctl/src/resources.rs b/ktsctl/src/resources.rs
index f1da3ff..ac89345 100644
--- a/ktsctl/src/resources.rs
+++ b/ktsctl/src/resources.rs
@@ -48,7 +48,8 @@ impl Resources {
url
.trim_start_matches("http")
.trim_start_matches('s')
- .trim_start_matches("://"),
+ .trim_start_matches(":/")
+ .trim_start_matches("/"),
);
self.runtime_dir.join("sources").join(url_dir)
'')
];
};
in
kak-tree-sitter

View file

@ -1,128 +0,0 @@
{ lib, fetchFromGitHub, stdenv, nodejs, pnpm, buildGoModule, mage, writeShellScriptBin, nixosTests, autoPatchelfHook, musl }:
let
version = "0.24.5-git";
src = fetchFromGitHub {
owner = "go-vikunja";
repo = "vikunja";
rev = "e57f04ec23e9ff8aa9877d2ea7d571c2a44790b0";
hash = "sha256-W6o1h6XBPvT1lH1zO5N7HcodksKill5eqSuaFl2kfuY=";
};
frontend = stdenv.mkDerivation (finalAttrs: {
pname = "vikunja-frontend";
inherit version src;
sourceRoot = "${finalAttrs.src.name}/frontend";
pnpmDeps = pnpm.fetchDeps {
inherit (finalAttrs) pname version src sourceRoot;
hash = "sha256-sOCaJDBgEMID+lN5plQpSqaGBIUs5h2tAwDzhtOH53o=";
};
nativeBuildInputs = [
nodejs
pnpm.configHook
autoPatchelfHook
];
buildInputs = [
musl # For sass-embedded
];
doCheck = true;
dontAutoPatchelf = true;
# See https://github.com/sass/embedded-host-node/issues/334
preBuild = ''
autoPatchelf node_modules/.pnpm/sass-embedded*
'';
postBuild = ''
pnpm run build
'';
checkPhase = ''
pnpm run test:unit --run
'';
installPhase = ''
cp -r dist/ $out
'';
});
# Injects a `t.Skip()` into a given test since there's apparently no other way to skip tests here.
skipTest = lineOffset: testCase: file:
let
jumpAndAppend = lib.concatStringsSep ";" (lib.replicate (lineOffset - 1) "n" ++ [ "a" ]);
in
''
sed -i -e '/${testCase}/{
${jumpAndAppend} t.Skip();
}' ${file}
'';
in
buildGoModule {
inherit src version;
pname = "vikunja";
nativeBuildInputs =
let
fakeGit = writeShellScriptBin "git" ''
if [[ $@ = "describe --tags --always --abbrev=10" ]]; then
echo "${version}"
else
>&2 echo "Unknown command: $@"
exit 1
fi
'';
in
[ fakeGit mage ];
vendorHash = "sha256-UWjlivF9ySXCAr84A1trCJ/n9pB98ZhEyG11qz3PL7g=";
inherit frontend;
prePatch = ''
cp -r ${frontend} frontend/dist
'';
postConfigure = ''
# These tests need internet, so we skip them.
${skipTest 1 "TestConvertTrelloToVikunja" "pkg/modules/migration/trello/trello_test.go"}
${skipTest 1 "TestConvertTodoistToVikunja" "pkg/modules/migration/todoist/todoist_test.go"}
'';
buildPhase = ''
runHook preBuild
# Fixes "mkdir /homeless-shelter: permission denied" - "Error: error compiling magefiles" during build
export HOME=$(mktemp -d)
mage build:build
runHook postBuild
'';
checkPhase = ''
mage test:unit
mage test:integration
'';
installPhase = ''
runHook preInstall
install -Dt $out/bin vikunja
runHook postInstall
'';
passthru.tests.vikunja = nixosTests.vikunja;
meta = {
changelog = "https://kolaente.dev/vikunja/api/src/tag/v${version}/CHANGELOG.md";
description = "Todo-app to organize your life";
homepage = "https://vikunja.io/";
license = lib.licenses.agpl3Plus;
maintainers = with lib.maintainers; [ leona ];
mainProgram = "vikunja";
platforms = lib.platforms.linux;
};
}