Compare commits

...

16 commits

25 changed files with 470 additions and 671 deletions

View file

@ -17,13 +17,13 @@
projectRootFile = ".git/config"; projectRootFile = ".git/config";
programs = { programs = {
nixfmt-rfc-style.enable = true; nixfmt-rfc-style.enable = true;
deadnix.enable = true; deadnix.enable = false;
}; };
}; };
pre-commit.settings.hooks = { pre-commit.settings.hooks = {
nixfmt-rfc-style.enable = true; nixfmt-rfc-style.enable = true;
deadnix.enable = true; deadnix.enable = false;
}; };
}; };

124
flake.lock generated
View file

@ -119,6 +119,26 @@
"type": "github" "type": "github"
} }
}, },
"emacs-overlay": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": "nixpkgs_3",
"nixpkgs-stable": "nixpkgs-stable"
},
"locked": {
"lastModified": 1728638138,
"narHash": "sha256-9BNhvMzh/bQmm0VhhRrl3fmiIjQnvRrVUwXIM5mtYY4=",
"owner": "nix-community",
"repo": "emacs-overlay",
"rev": "b3101a3a0f3883f97fa867ef56b0f29fa2b2b7f1",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "emacs-overlay",
"type": "github"
}
},
"fan-control": { "fan-control": {
"flake": false, "flake": false,
"locked": { "locked": {
@ -267,6 +287,24 @@
"inputs": { "inputs": {
"systems": "systems_3" "systems": "systems_3"
}, },
"locked": {
"lastModified": 1726560853,
"narHash": "sha256-X6rJYSESBVr3hBoH0WbKE5KvhPU5bloyZ2L4K60/fPQ=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "c1dfcf08411b08f6b8615f7d8971a2bfa81d5e8a",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"flake-utils_3": {
"inputs": {
"systems": "systems_4"
},
"locked": { "locked": {
"lastModified": 1681202837, "lastModified": 1681202837,
"narHash": "sha256-H+Rh19JDwRtpVPAWp64F+rlEtxUWBAQW28eAi3SRSzg=", "narHash": "sha256-H+Rh19JDwRtpVPAWp64F+rlEtxUWBAQW28eAi3SRSzg=",
@ -321,7 +359,7 @@
"nixpkgs": [ "nixpkgs": [
"nixpkgs" "nixpkgs"
], ],
"nixpkgs-stable": "nixpkgs-stable" "nixpkgs-stable": "nixpkgs-stable_2"
}, },
"locked": { "locked": {
"lastModified": 1726745158, "lastModified": 1726745158,
@ -383,7 +421,7 @@
"hercules-ci-effects": { "hercules-ci-effects": {
"inputs": { "inputs": {
"flake-parts": "flake-parts_3", "flake-parts": "flake-parts_3",
"nixpkgs": "nixpkgs_5" "nixpkgs": "nixpkgs_6"
}, },
"locked": { "locked": {
"lastModified": 1701009247, "lastModified": 1701009247,
@ -557,7 +595,7 @@
}, },
"lix-module": { "lix-module": {
"inputs": { "inputs": {
"flake-utils": "flake-utils", "flake-utils": "flake-utils_2",
"flakey-profile": "flakey-profile", "flakey-profile": "flakey-profile",
"lix": [ "lix": [
"lix" "lix"
@ -626,7 +664,7 @@
"inputs": { "inputs": {
"home-manager": "home-manager_2", "home-manager": "home-manager_2",
"nix-formatter-pack": "nix-formatter-pack", "nix-formatter-pack": "nix-formatter-pack",
"nixpkgs": "nixpkgs_3", "nixpkgs": "nixpkgs_4",
"nixpkgs-docs": "nixpkgs-docs", "nixpkgs-docs": "nixpkgs-docs",
"nixpkgs-for-bootstrap": "nixpkgs-for-bootstrap", "nixpkgs-for-bootstrap": "nixpkgs-for-bootstrap",
"nmd": "nmd" "nmd": "nmd"
@ -647,7 +685,7 @@
}, },
"nixDarwin": { "nixDarwin": {
"inputs": { "inputs": {
"nixpkgs": "nixpkgs_4" "nixpkgs": "nixpkgs_5"
}, },
"locked": { "locked": {
"lastModified": 1727003835, "lastModified": 1727003835,
@ -814,6 +852,22 @@
} }
}, },
"nixpkgs-stable": { "nixpkgs-stable": {
"locked": {
"lastModified": 1728500571,
"narHash": "sha256-dOymOQ3AfNI4Z337yEwHGohrVQb4yPODCW9MDUyAc4w=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "d51c28603def282a24fa034bcb007e2bcb5b5dd0",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-24.05",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs-stable_2": {
"locked": { "locked": {
"lastModified": 1720386169, "lastModified": 1720386169,
"narHash": "sha256-NGKVY4PjzwAa4upkGtAMz1npHGoRzWotlSnVlqI40mo=", "narHash": "sha256-NGKVY4PjzwAa4upkGtAMz1npHGoRzWotlSnVlqI40mo=",
@ -829,7 +883,7 @@
"type": "github" "type": "github"
} }
}, },
"nixpkgs-stable_2": { "nixpkgs-stable_3": {
"locked": { "locked": {
"lastModified": 1720386169, "lastModified": 1720386169,
"narHash": "sha256-NGKVY4PjzwAa4upkGtAMz1npHGoRzWotlSnVlqI40mo=", "narHash": "sha256-NGKVY4PjzwAa4upkGtAMz1npHGoRzWotlSnVlqI40mo=",
@ -862,6 +916,22 @@
} }
}, },
"nixpkgs_3": { "nixpkgs_3": {
"locked": {
"lastModified": 1728492678,
"narHash": "sha256-9UTxR8eukdg+XZeHgxW5hQA9fIKHsKCdOIUycTryeVw=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "5633bcff0c6162b9e4b5f1264264611e950c8ec7",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_4": {
"locked": { "locked": {
"lastModified": 1708172716, "lastModified": 1708172716,
"narHash": "sha256-3M94oln0b61m3dUmLyECCA9hYAHXZEszM4saE3CmQO4=", "narHash": "sha256-3M94oln0b61m3dUmLyECCA9hYAHXZEszM4saE3CmQO4=",
@ -876,7 +946,7 @@
"type": "github" "type": "github"
} }
}, },
"nixpkgs_4": { "nixpkgs_5": {
"locked": { "locked": {
"lastModified": 0, "lastModified": 0,
"narHash": "sha256-bvGoiQBvponpZh8ClUcmJ6QnsNKw0EMrCQJARK3bI1c=", "narHash": "sha256-bvGoiQBvponpZh8ClUcmJ6QnsNKw0EMrCQJARK3bI1c=",
@ -888,7 +958,7 @@
"type": "indirect" "type": "indirect"
} }
}, },
"nixpkgs_5": { "nixpkgs_6": {
"locked": { "locked": {
"lastModified": 1697723726, "lastModified": 1697723726,
"narHash": "sha256-SaTWPkI8a5xSHX/rrKzUe+/uVNy6zCGMXgoeMb7T9rg=", "narHash": "sha256-SaTWPkI8a5xSHX/rrKzUe+/uVNy6zCGMXgoeMb7T9rg=",
@ -904,13 +974,13 @@
"type": "github" "type": "github"
} }
}, },
"nixpkgs_6": { "nixpkgs_7": {
"locked": { "locked": {
"lastModified": 1728241625, "lastModified": 1728492678,
"narHash": "sha256-yumd4fBc/hi8a9QgA9IT8vlQuLZ2oqhkJXHPKxH/tRw=", "narHash": "sha256-9UTxR8eukdg+XZeHgxW5hQA9fIKHsKCdOIUycTryeVw=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "c31898adf5a8ed202ce5bea9f347b1c6871f32d1", "rev": "5633bcff0c6162b9e4b5f1264264611e950c8ec7",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -920,7 +990,7 @@
"type": "github" "type": "github"
} }
}, },
"nixpkgs_7": { "nixpkgs_8": {
"locked": { "locked": {
"lastModified": 1678470307, "lastModified": 1678470307,
"narHash": "sha256-OEeMUr3ueLIXyW/OaFUX5jUdimyQwMg/7e+/Q0gC/QE=", "narHash": "sha256-OEeMUr3ueLIXyW/OaFUX5jUdimyQwMg/7e+/Q0gC/QE=",
@ -936,7 +1006,7 @@
"type": "github" "type": "github"
} }
}, },
"nixpkgs_8": { "nixpkgs_9": {
"locked": { "locked": {
"lastModified": 1682134069, "lastModified": 1682134069,
"narHash": "sha256-TnI/ZXSmRxQDt2sjRYK/8j8iha4B4zP2cnQCZZ3vp7k=", "narHash": "sha256-TnI/ZXSmRxQDt2sjRYK/8j8iha4B4zP2cnQCZZ3vp7k=",
@ -1031,7 +1101,7 @@
"lanzaboote", "lanzaboote",
"nixpkgs" "nixpkgs"
], ],
"nixpkgs-stable": "nixpkgs-stable_2" "nixpkgs-stable": "nixpkgs-stable_3"
}, },
"locked": { "locked": {
"lastModified": 1721042469, "lastModified": 1721042469,
@ -1091,7 +1161,7 @@
"fan-control": "fan-control", "fan-control": "fan-control",
"flake-parts": "flake-parts_4", "flake-parts": "flake-parts_4",
"kernel-src": "kernel-src", "kernel-src": "kernel-src",
"nixpkgs": "nixpkgs_7", "nixpkgs": "nixpkgs_8",
"nixpkgs-kernel": "nixpkgs-kernel", "nixpkgs-kernel": "nixpkgs-kernel",
"panfork": "panfork", "panfork": "panfork",
"tow-boot": "tow-boot", "tow-boot": "tow-boot",
@ -1117,6 +1187,7 @@
"catppuccin": "catppuccin", "catppuccin": "catppuccin",
"disko": "disko", "disko": "disko",
"dream2nix": "dream2nix", "dream2nix": "dream2nix",
"emacs-overlay": "emacs-overlay",
"flakeParts": "flakeParts", "flakeParts": "flakeParts",
"git-hooks-nix": "git-hooks-nix", "git-hooks-nix": "git-hooks-nix",
"homeManager": "homeManager", "homeManager": "homeManager",
@ -1130,7 +1201,7 @@
"nixDarwin": "nixDarwin", "nixDarwin": "nixDarwin",
"nixThePlanet": "nixThePlanet", "nixThePlanet": "nixThePlanet",
"nixosHardware": "nixosHardware", "nixosHardware": "nixosHardware",
"nixpkgs": "nixpkgs_6", "nixpkgs": "nixpkgs_7",
"rock5b": "rock5b", "rock5b": "rock5b",
"treefmt-nix": "treefmt-nix_2", "treefmt-nix": "treefmt-nix_2",
"vscode-server": "vscode-server" "vscode-server": "vscode-server"
@ -1240,6 +1311,21 @@
"type": "github" "type": "github"
} }
}, },
"systems_4": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
},
"tow-boot": { "tow-boot": {
"flake": false, "flake": false,
"locked": { "locked": {
@ -1300,8 +1386,8 @@
}, },
"vscode-server": { "vscode-server": {
"inputs": { "inputs": {
"flake-utils": "flake-utils_2", "flake-utils": "flake-utils_3",
"nixpkgs": "nixpkgs_8" "nixpkgs": "nixpkgs_9"
}, },
"locked": { "locked": {
"lastModified": 1713958148, "lastModified": 1713958148,

View file

@ -57,6 +57,7 @@
inputs.nixpkgs.follows = "nixpkgs"; inputs.nixpkgs.follows = "nixpkgs";
}; };
catppuccin.url = "github:catppuccin/nix"; catppuccin.url = "github:catppuccin/nix";
emacs-overlay.url = "github:nix-community/emacs-overlay";
}; };
outputs = outputs =

View file

@ -1,10 +1,49 @@
{ {
lib, lib,
age, fleetFlake,
pkgs,
... ...
}: }:
let
emacs = fleetFlake.packages.${pkgs.system}.emacs;
in
{ {
ccrEmacs.enable = true; home.sessionVariables.EDITOR = lib.mkForce "emacsclient -c";
home.sessionVariables.EDITOR = lib.mkForce "emacsclient"; programs.emacs = {
systemd.user.services.emacs.Service.EnvironmentFile = age.secrets.chatgpt-token.path; enable = true;
package = emacs;
};
services.emacs = {
enable = true;
client.enable = true;
defaultEditor = true;
socketActivation.enable = false;
startWithUserSession = true;
package = emacs;
};
home.packages =
with pkgs;
[
binutils
delta
(ripgrep.override { withPCRE2 = true; })
gnutls
fd
hunspell
python3
imagemagick
ghostscript_headless
mupdf-headless
poppler_utils
ffmpegthumbnailer
mediainfo
unzipNLS
nodejs_20
pkgs.qadwaitadecorations
pkgs.kdePackages.qtwayland
]
++ (with hunspellDicts; [
en_US-large
it_IT
]);
} }

View file

@ -8,7 +8,19 @@
{ {
programs.mbsync.enable = true; programs.mbsync.enable = true;
programs.msmtp.enable = true; programs.msmtp.enable = true;
services.mbsync.enable = true; services.mbsync = {
enable = true;
postExec = lib.getExe (
pkgs.writeShellScriptBin "mbsync-post-exec" ''
${lib.getExe pkgs.notmuch} new
for _ in _ _
do
afew -C ~/.config/notmuch/default/config --tag --new -vv
afew -C ~/.config/notmuch/default/config --move --new -vv
done
''
);
};
home.file.".config/aerc/stylesets" = home.file.".config/aerc/stylesets" =
let let
@ -188,6 +200,58 @@
}; };
}; };
programs.notmuch = {
enable = true;
new.tags = [ "new" ];
search.excludeTags = [
"trash"
"deleted"
"spam"
];
maildir.synchronizeFlags = true;
};
programs.afew = {
enable = true;
extraConfig = ''
[Filter.1]
message = "Tag GitHub notifications"
tags = +github
query = from:noreply@github.com OR from:notifications@github.com
[Filter.2]
query = "folder:autistici/Inbox"
tags = +autistici
message = "Tag personal autistici emails"
[Filter.3]
query = "not folder:autistici/Inbox"
tag = -new
message = "Sanity check: remove the new tag for emails moved out from Inbox"
[Filter.4]
query = "not folder:autistici/Inbox"
tag = -new
message = "Sanity check: remove the new tag for emails moved out from Inbox"
[Filter.5]
query = "not folder:autistici/Sent"
tag = +sent
message = "Sanity check: add the sent tag for emails in Sent"
[Filter.6]
query = "not folder:autistici/Drafts"
tag = +draft
message = "Sanity check: add the draft tag for emails in Draft"
[MailMover]
folders = autistici/Inbox
rename = true
autistici/Inbox = 'tag:archive':autistici/Archive 'tag:github':autistici/GitHub 'NOT tag:new':autistici/Trash
'';
};
systemd.user.services.emails-watcher = { systemd.user.services.emails-watcher = {
Unit.Description = "Send notifications when new emails arrive"; Unit.Description = "Send notifications when new emails arrive";
Install = { Install = {
@ -214,6 +278,8 @@
mbsync = { mbsync = {
enable = true; enable = true;
create = "maildir"; create = "maildir";
expunge = "both";
remove = "both";
}; };
msmtp.enable = true; msmtp.enable = true;
notmuch.enable = true; notmuch.enable = true;

View file

@ -36,6 +36,7 @@ bind = $mod, m, exec, footclient $SHELL -C "aerc"
bind = $mod, d, exec, fuzzel --background-color=253559cc --border-radius=5 --border-width=0 bind = $mod, d, exec, fuzzel --background-color=253559cc --border-radius=5 --border-width=0
bind = $mod, s, exec, screenshot.sh bind = $mod, s, exec, screenshot.sh
bind = $mod, n, exec, logseq bind = $mod, n, exec, logseq
bind = $mod, x, exec, emacsclient -c
bind = , XF86MonBrightnessUp, exec, brightnessctl s +5% bind = , XF86MonBrightnessUp, exec, brightnessctl s +5%
bind = , XF86MonBrightnessDown, exec, brightnessctl s 5%- bind = , XF86MonBrightnessDown, exec, brightnessctl s 5%-
bind = $mod, code:60, exec, brightnessctl s +5% bind = $mod, code:60, exec, brightnessctl s +5%

View file

@ -8,7 +8,7 @@ let
hash = "sha256-Hy/zk8ZCkWajsMRUMsewLvkKpMpsBZYnFootPU9y6Z0="; hash = "sha256-Hy/zk8ZCkWajsMRUMsewLvkKpMpsBZYnFootPU9y6Z0=";
}; };
}; };
logseq-wayland = pkgs.writeScriptBin "logseq" "${lib.getExe logseq} --enable-features=UseOzonePlatform --ozone-platform=wayland"; logseq-wayland = pkgs.writeScriptBin "logseq" "${lib.getExe' logseq "logseq"} --enable-features=UseOzonePlatform --ozone-platform=wayland";
in in
{ {
home.packages = [ logseq-wayland ]; home.packages = [ logseq-wayland ];

View file

@ -1,7 +1,7 @@
{ {
fleetModules, fleetModules,
lib, lib,
config, pkgs,
... ...
}: }:
{ {
@ -27,13 +27,14 @@
"printing" "printing"
"pam" "pam"
"wireguard-client" "wireguard-client"
"restic"
"greetd" "greetd"
"syncthing" "syncthing"
"mount-rock5b" "mount-sisko"
"adb" "adb"
"binfmt" "binfmt"
"prometheus-exporters" "prometheus-exporters"
"promtail"
"syncthing"
] ]
++ [ ++ [
./disko.nix ./disko.nix
@ -71,6 +72,9 @@
"zathura" "zathura"
"imv" "imv"
"catppuccin" "catppuccin"
"libreoffice"
"logseq"
"emacs"
]; ];
extraGroups = [ ]; extraGroups = [ ];
backupPaths = [ ]; backupPaths = [ ];
@ -87,7 +91,7 @@
"kvm-intel" "kvm-intel"
]; ];
boot.kernelPackages = config.boot.zfs.package.latestCompatibleLinuxPackages; boot.kernelPackages = pkgs.linuxKernel.packages.linux_6_10;
boot.loader.efi.canTouchEfiVariables = true; boot.loader.efi.canTouchEfiVariables = true;
boot.loader.systemd-boot = { boot.loader.systemd-boot = {
@ -101,8 +105,13 @@
powerManagement.cpuFreqGovernor = lib.mkDefault "schedutil"; powerManagement.cpuFreqGovernor = lib.mkDefault "schedutil";
hardware.enableRedistributableFirmware = lib.mkDefault true; hardware.enableRedistributableFirmware = lib.mkDefault true;
hardware.opengl = { hardware.graphics = {
enable = true; enable = true;
driSupport32Bit = true; enable32Bit = true;
};
zramSwap = {
enable = true;
algorithm = "zstd";
}; };
} }

View file

@ -31,13 +31,13 @@
"wireguard-client" "wireguard-client"
"binfmt" "binfmt"
"greetd" "greetd"
# "syncthing" "syncthing"
"hass-poweroff" "hass-poweroff"
"forgejo-runners" "forgejo-runners"
"teamviewer" "teamviewer"
"macos-ventura" "macos-ventura"
"sunshine" "sunshine"
"mount-rock5b" "mount-sisko"
"adb" "adb"
"guix" "guix"
"prometheus-exporters" "prometheus-exporters"
@ -91,6 +91,7 @@
"imv" "imv"
"libreoffice" "libreoffice"
"logseq" "logseq"
"emacs"
]; ];
extraGroups = [ ]; extraGroups = [ ];
backupPaths = [ ]; backupPaths = [ ];

View file

@ -15,10 +15,10 @@
"home-assistant" "home-assistant"
"adguard-home" "adguard-home"
"cloudflare-dyndns" "cloudflare-dyndns"
"rock5b-proxy" "sisko-proxy"
"invidious" "invidious"
"searx" "searx"
"rock5b-samba" "sisko-nfs"
"forgejo" "forgejo"
"prometheus" "prometheus"
"grafana" "grafana"
@ -29,6 +29,8 @@
"restic" "restic"
"atuin" "atuin"
"immich" "immich"
"paperless"
"syncthing"
] ]
++ [ ++ [
./disko.nix ./disko.nix

View file

@ -12,12 +12,10 @@
"search.aciceri.dev" "search.aciceri.dev"
"invidious.aciceri.dev" "invidious.aciceri.dev"
"vpn.aciceri.dev" "vpn.aciceri.dev"
"cache.aciceri.dev"
"matrix.aciceri.dev"
"syncv3.matrix.aciceri.dev"
"jellyfin.aciceri.dev"
"photos.aciceri.dev" "photos.aciceri.dev"
"status.aciceri.dev" "status.aciceri.dev"
"paper.aciceri.dev"
"cloud.aciceri.dev"
]; ];
apiTokenFile = config.age.secrets.cloudflare-dyndns-api-token.path; apiTokenFile = config.age.secrets.cloudflare-dyndns-api-token.path;
}; };

View file

@ -4,6 +4,7 @@
}: }:
{ {
services.forgejo = { services.forgejo = {
# TODO migrate to Postgres
enable = true; enable = true;
settings = { settings = {
DEFAULT = { DEFAULT = {

View file

@ -1,5 +0,0 @@
PUBLIC_LOGIN_PAGE_MESSAGE=
IMMICH_WEB_URL=http://immich-web:3000
IMMICH_SERVER_URL=http://immich-server:3001
IMMICH_MACHINE_LEARNING_URL=http://immich-machine-learning:3003

View file

@ -1,584 +0,0 @@
{
config,
lib,
pkgs,
...
}:
let
inherit (lib)
hasAttr
hasPrefix
maintainers
mapAttrs
mkDefault
mkEnableOption
mkIf
mkMerge
mkOption
mkPackageOption
optional
optionalAttrs
optionalString
types
;
cfg = config.services.immich;
serverCfg = config.services.immich.server;
backendCfg = serverCfg.backend;
microservicesCfg = serverCfg.microservices;
webCfg = cfg.web;
mlCfg = cfg.machineLearning;
isServerPostgresUnix = hasPrefix "/" serverCfg.postgres.host;
postgresEnv =
if isServerPostgresUnix then
{
# If passwordFile is given, this will be overwritten in ExecStart
DB_URL = "socket://${serverCfg.postgres.host}?dbname=${serverCfg.postgres.database}";
}
else
{
DB_HOSTNAME = serverCfg.postgres.host;
DB_PORT = toString serverCfg.postgres.port;
DB_DATABASE_NAME = serverCfg.postgres.database;
DB_USERNAME = serverCfg.postgres.username;
};
typesenseEnv =
{
TYPESENSE_ENABLED = toString serverCfg.typesense.enable;
}
// optionalAttrs serverCfg.typesense.enable {
TYPESENSE_HOST = serverCfg.typesense.host;
TYPESENSE_PORT = toString serverCfg.typesense.port;
TYPESENSE_PROTOCOL = serverCfg.typesense.protocol;
};
# Don't start a redis instance if the user sets a custom redis connection
enableRedis =
!hasAttr "REDIS_URL" serverCfg.extraConfig && !hasAttr "REDIS_SOCKET" serverCfg.extraConfig;
redisServerCfg = config.services.redis.servers.immich;
redisEnv = optionalAttrs enableRedis {
REDIS_SOCKET = redisServerCfg.unixSocket;
};
serverEnv =
postgresEnv
// typesenseEnv
// redisEnv
// {
NODE_ENV = "production";
IMMICH_MEDIA_LOCATION = serverCfg.mediaDir;
IMMICH_MACHINE_LEARNING_URL =
if serverCfg.machineLearningUrl != null then serverCfg.machineLearningUrl else "false";
};
serverStartWrapper = program: ''
set -euo pipefail
mkdir -p ${serverCfg.mediaDir}
${optionalString (serverCfg.postgres.passwordFile != null) (
if isServerPostgresUnix then
''export DB_URL="socket://${serverCfg.postgres.username}:$(cat ${serverCfg.postgres.passwordFile})@${serverCfg.postgres.host}?dbname=${serverCfg.postgres.database}"''
else
"export DB_PASSWORD=$(cat ${serverCfg.postgres.passwordFile})"
)}
${optionalString serverCfg.typesense.enable ''
export TYPESENSE_API_KEY=$(cat ${serverCfg.typesense.apiKeyFile})
''}
exec ${program}
'';
commonServiceConfig = {
Restart = "on-failure";
# Hardening
CapabilityBoundingSet = "";
LockPersonality = true;
MemoryDenyWriteExecute = true;
NoNewPrivileges = true;
PrivateUsers = true;
PrivateTmp = true;
PrivateDevices = true;
PrivateMounts = true;
ProtectClock = true;
ProtectControlGroups = true;
ProtectHome = true;
ProtectHostname = true;
ProtectKernelLogs = true;
ProtectKernelModules = true;
ProtectKernelTunables = true;
ProtectProc = "invisible";
ProcSubset = "pid";
# Would re-mount paths ignored by temporary root
# TODO ProtectSystem = "strict";
RemoveIPC = true;
RestrictAddressFamilies = [
"AF_INET"
"AF_INET6"
"AF_UNIX"
];
RestrictNamespaces = true;
RestrictRealtime = true;
RestrictSUIDSGID = true;
SystemCallArchitectures = "native";
SystemCallFilter = [
"@system-service"
"~@privileged"
"@pkey"
];
UMask = "0077";
};
serverServiceConfig = {
DynamicUser = true;
User = "immich";
Group = "immich";
SupplementaryGroups = optional enableRedis redisServerCfg.user;
StateDirectory = "immich";
StateDirectoryMode = "0750";
WorkingDirectory = "/var/lib/immich";
MemoryDenyWriteExecute = false; # nodejs requires this.
EnvironmentFile = mkIf (serverCfg.environmentFile != null) serverCfg.environmentFile;
TemporaryFileSystem = "/:ro";
BindReadOnlyPaths = [
"/nix/store"
"-/etc/resolv.conf"
"-/etc/nsswitch.conf"
"-/etc/hosts"
"-/etc/localtime"
"-/run/postgresql"
] ++ optional enableRedis redisServerCfg.unixSocket;
};
in
{
options.services.immich = {
enable = mkEnableOption "immich" // {
description = ''
Enables immich which consists of a backend server, microservices,
machine-learning and web ui. You can disable or reconfigure components
individually using the subsections.
'';
};
package = mkPackageOption pkgs "immich" { };
server = {
mediaDir = mkOption {
type = types.str;
default = "/var/lib/immich/media";
description = "Directory used to store media files.";
};
backend = {
enable = mkEnableOption "immich backend server" // {
default = true;
};
port = mkOption {
type = types.port;
default = 3001;
description = "Port to bind to.";
};
openFirewall = mkOption {
default = false;
type = types.bool;
description = "Whether to open the firewall for the specified port.";
};
extraConfig = mkOption {
type = types.attrs;
default = { };
example = {
LOG_LEVEL = "debug";
};
description = ''
Extra configuration options (environment variables).
Refer to [the documented variables](https://documentation.immich.app/docs/install/environment-variables) tagged with 'server' for available options.
'';
};
environmentFile = mkOption {
type = types.nullOr types.path;
default = null;
description = ''
Environment file as defined in systemd.exec(5). May be used to provide
additional secret variables to the service without adding them to the
world-readable Nix store.
'';
};
};
microservices = {
enable = mkEnableOption "immich microservices" // {
default = true;
};
port = mkOption {
type = types.port;
default = 3002;
description = "Port to bind to.";
};
openFirewall = mkOption {
default = false;
type = types.bool;
description = "Whether to open the firewall for the specified port.";
};
extraConfig = mkOption {
type = types.attrs;
default = { };
example = {
REVERSE_GEOCODING_PRECISION = 1;
};
description = ''
Extra configuration options (environment variables).
Refer to [the documented variables](https://documentation.immich.app/docs/install/environment-variables) tagged with 'microservices' for available options.
'';
};
environmentFile = mkOption {
type = types.nullOr types.path;
default = null;
description = ''
Environment file as defined in systemd.exec(5). May be used to provide
additional secret variables to the service without adding them to the
world-readable Nix store.
'';
};
};
typesense = {
enable = mkEnableOption "typesense" // {
default = true;
};
host = mkOption {
type = types.str;
default = "127.0.0.1";
example = "typesense.example.com";
description = "Hostname/address of the typesense server to use.";
};
port = mkOption {
type = types.port;
default = 8108;
description = "The port of the typesense server to use.";
};
protocol = mkOption {
type = types.str;
default = "http";
description = "The protocol to use when connecting to the typesense server.";
};
apiKeyFile = mkOption {
type = types.path;
description = "Sets the api key for authentication with typesense.";
};
};
postgres = {
host = mkOption {
type = types.str;
default = "/run/postgresql";
description = "Hostname/address of the postgres server to use. If an absolute path is given here, it will be interpreted as a unix socket path.";
};
port = mkOption {
type = types.port;
default = 5432;
description = "The port of the postgres server to use.";
};
username = mkOption {
type = types.str;
default = "immich";
description = "The postgres username to use.";
};
passwordFile = mkOption {
type = types.nullOr types.path;
default = null;
description = ''
Sets the password for authentication with postgres.
May be unset when using socket authentication.
'';
};
database = mkOption {
type = types.str;
default = "immich";
description = "The postgres database to use.";
};
};
useMachineLearning = mkOption {
description = "Use the given machine learning server endpoint to enable ML functionality in immich.";
default = true;
type = types.bool;
};
machineLearningUrl = mkOption {
type = types.str;
default = "http://127.0.0.1:3003";
example = "https://immich-ml.internal.example.com";
description = "The machine learning server endpoint to use.";
};
extraConfig = mkOption {
type = types.attrs;
default = { };
example = {
REDIS_SOCKET = "/run/custom-redis";
};
description = ''
Extra configuration options (environment variables) for both backend and microservices.
Refer to [the documented variables](https://documentation.immich.app/docs/install/environment-variables) tagged with both 'server' and 'microservices' for available options.
'';
};
environmentFile = mkOption {
type = types.nullOr types.path;
default = null;
description = ''
Environment file as defined in systemd.exec(5). May be used to provide
additional secret variables to the backend and microservices servers without
adding them to the world-readable Nix store.
'';
};
};
web = {
enable = mkEnableOption "immich web frontend" // {
default = true;
};
port = mkOption {
type = types.port;
default = 3000;
description = "Port to bind to.";
};
openFirewall = mkOption {
default = false;
type = types.bool;
description = "Whether to open the firewall for the specified port.";
};
serverUrl = mkOption {
type = types.str;
default = "http://127.0.0.1:3001";
example = "https://immich-backend.internal.example.com";
description = "The backend server url to use.";
};
apiUrlExternal = mkOption {
type = types.str;
default = "/web";
description = "The api url to use for external requests.";
};
extraConfig = mkOption {
type = types.attrs;
default = { };
example = {
PUBLIC_LOGIN_PAGE_MESSAGE = "My awesome Immich instance!";
};
description = ''
Extra configuration options (environment variables).
Refer to [the documented variables](https://documentation.immich.app/docs/install/environment-variables) tagged with 'web' for available options.
'';
};
};
machineLearning = {
enable = mkEnableOption "immich machine-learning server" // {
default = true;
};
port = mkOption {
type = types.port;
default = 3003;
description = "Port to bind to.";
};
openFirewall = mkOption {
default = false;
type = types.bool;
description = "Whether to open the firewall for the specified port.";
};
extraConfig = mkOption {
type = types.attrs;
default = { };
example = {
MACHINE_LEARNING_MODEL_TTL = 600;
};
description = ''
Extra configuration options (environment variables).
Refer to [the documented variables](https://documentation.immich.app/docs/install/environment-variables) tagged with 'machine learning' for available options.
'';
};
};
};
config = mkIf cfg.enable {
assertions = [
{
assertion = !isServerPostgresUnix -> serverCfg.postgres.passwordFile != null;
message = "A database password must be provided when unix sockets are not used.";
}
];
networking.firewall.allowedTCPPorts = mkMerge [
(mkIf (backendCfg.enable && backendCfg.openFirewall) [ backendCfg.port ])
(mkIf (microservicesCfg.enable && microservicesCfg.openFirewall) [ microservicesCfg.port ])
(mkIf (webCfg.enable && webCfg.openFirewall) [ webCfg.port ])
(mkIf (mlCfg.enable && mlCfg.openFirewall) [ mlCfg.port ])
];
services.redis.servers.immich.enable = mkIf enableRedis true;
services.redis.vmOverCommit = mkIf enableRedis (mkDefault true);
systemd.services.immich-server = mkIf backendCfg.enable {
description = "Immich backend server (Self-hosted photo and video backup solution)";
after = [
"network.target"
"typesense.service"
"postgresql.service"
"immich-machine-learning.service"
] ++ optional enableRedis "redis-immich.service";
wantedBy = [ "multi-user.target" ];
environment =
serverEnv
// {
SERVER_PORT = toString backendCfg.port;
}
// mapAttrs (_: toString) serverCfg.extraConfig
// mapAttrs (_: toString) backendCfg.extraConfig;
script = serverStartWrapper "${cfg.package}/bin/server";
serviceConfig = mkMerge [
(commonServiceConfig // serverServiceConfig)
{
EnvironmentFile = mkIf (backendCfg.environmentFile != null) backendCfg.environmentFile;
}
];
};
systemd.services.immich-microservices = mkIf microservicesCfg.enable {
description = "Immich microservices (Self-hosted photo and video backup solution)";
after = [
"network.target"
"typesense.service"
"postgresql.service"
"immich-machine-learning.service"
] ++ optional enableRedis "redis-immich.service";
wantedBy = [ "multi-user.target" ];
environment =
serverEnv
// {
MICROSERVICES_PORT = toString microservicesCfg.port;
}
// mapAttrs (_: toString) serverCfg.extraConfig
// mapAttrs (_: toString) microservicesCfg.extraConfig;
script = serverStartWrapper "${cfg.package}/bin/microservices";
serviceConfig = mkMerge [
(commonServiceConfig // serverServiceConfig)
{
EnvironmentFile = mkIf (microservicesCfg.environmentFile != null) microservicesCfg.environmentFile;
}
];
};
systemd.services.immich-web = mkIf webCfg.enable {
description = "Immich web (Self-hosted photo and video backup solution)";
after = [
"network.target"
"immich-server.service"
];
wantedBy = [ "multi-user.target" ];
environment = {
NODE_ENV = "production";
PORT = toString webCfg.port;
IMMICH_SERVER_URL = webCfg.serverUrl;
IMMICH_API_URL_EXTERNAL = webCfg.apiUrlExternal;
} // mapAttrs (_: toString) webCfg.extraConfig;
script = ''
set -euo pipefail
export PUBLIC_IMMICH_SERVER_URL=$IMMICH_SERVER_URL
export PUBLIC_IMMICH_API_URL_EXTERNAL=$IMMICH_API_URL_EXTERNAL
exec ${cfg.package.web}/bin/web
'';
serviceConfig = commonServiceConfig // {
DynamicUser = true;
User = "immich-web";
Group = "immich-web";
MemoryDenyWriteExecute = false; # nodejs requires this.
TemporaryFileSystem = "/:ro";
BindReadOnlyPaths = [
"/nix/store"
"-/etc/resolv.conf"
"-/etc/nsswitch.conf"
"-/etc/hosts"
"-/etc/localtime"
];
};
};
systemd.services.immich-machine-learning = mkIf mlCfg.enable {
description = "Immich machine learning (Self-hosted photo and video backup solution)";
after = [ "network.target" ];
wantedBy = [ "multi-user.target" ];
environment = {
NODE_ENV = "production";
MACHINE_LEARNING_PORT = toString mlCfg.port;
MACHINE_LEARNING_CACHE_FOLDER = "/var/cache/immich-ml";
TRANSFORMERS_CACHE = "/var/cache/immich-ml";
} // mapAttrs (_: toString) mlCfg.extraConfig;
serviceConfig = commonServiceConfig // {
ExecStart = "${cfg.package.machine-learning}/bin/machine-learning";
DynamicUser = true;
User = "immich-ml";
Group = "immich-ml";
MemoryDenyWriteExecute = false; # onnxruntime_pybind11 requires this.
ProcSubset = "all"; # Needs /proc/cpuinfo
CacheDirectory = "immich-ml";
CacheDirectoryMode = "0700";
# TODO gpu access
TemporaryFileSystem = "/:ro";
BindReadOnlyPaths = [
"/nix/store"
"-/etc/resolv.conf"
"-/etc/nsswitch.conf"
"-/etc/hosts"
"-/etc/localtime"
];
};
};
meta.maintainers = with maintainers; [ oddlama ];
};
}

View file

@ -1,21 +0,0 @@
{
pkgs,
config,
...
}:
{
fileSystems."/home/${config.ccr.username}/torrent" = {
device = "//sisko.fleet/torrent";
fsType = "cifs";
options =
let
credentials = pkgs.writeText "credentials" ''
username=guest
password=
'';
in
[
"credentials=${credentials},x-systemd.automount,noauto,x-systemd.idle-timeout=60,x-systemd.device-timeout=5s,x-systemd.mount-timeout=5s,uid=1000,gid=1000"
];
};
}

View file

@ -0,0 +1,27 @@
{
pkgs,
config,
...
}:
{
environment.systemPackages = with pkgs; [ nfs-utils ];
boot.supportedFilesystems = [ "nfs" ];
services.rpcbind.enable = true;
security.wrappers."mount.nfs" = {
setuid = true;
owner = "root";
group = "root";
source = "${pkgs.nfs-utils.out}/bin/mount.nfs";
};
fileSystems."/home/${config.ccr.username}/nas" = {
device = "sisko.fleet:/hd";
fsType = "nfs";
options = [
"x-systemd.automount"
"noauto"
"user"
];
};
}

View file

@ -4,8 +4,7 @@
enable = true; enable = true;
address = "0.0.0.0"; address = "0.0.0.0";
passwordFile = builtins.toFile "paperless-initial-password" "paperless"; passwordFile = builtins.toFile "paperless-initial-password" "paperless";
mediaDir = "/mnt/hd/paperless/media"; mediaDir = "/mnt/hd/paperless/";
consumptionDir = "/mnt/hd/paperless/consume";
settings = { settings = {
PAPERLESS_OCR_LANGUAGE = "ita+eng"; PAPERLESS_OCR_LANGUAGE = "ita+eng";
PAPERLESS_CONSUMER_IGNORE_PATTERN = builtins.toJSON [ PAPERLESS_CONSUMER_IGNORE_PATTERN = builtins.toJSON [
@ -15,11 +14,12 @@
PAPERLESS_OCR_USER_ARGS = builtins.toJSON { PAPERLESS_OCR_USER_ARGS = builtins.toJSON {
optimize = 1; optimize = 1;
pdfa_image_compression = "lossless"; pdfa_image_compression = "lossless";
invalidate_digital_signatures = true;
}; };
}; };
}; };
backup.paths = [ environment.persistence."/persist".directories = [
config.services.paperless.dataDir config.services.paperless.dataDir
]; ];
} }

View file

@ -25,10 +25,21 @@ in
host host
}".publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIICf9svRenC/PLKIL9nk6K/pxQgoiFC41wTNvoIncOxs"; }".publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIICf9svRenC/PLKIL9nk6K/pxQgoiFC41wTNvoIncOxs";
services.postgresqlBackup = {
enable = true;
backupAll = true;
location = "/var/backup/postgresql";
};
environment.persistence."/persist".directories = [
config.services.postgresqlBackup.location
];
services.restic.backups.sisko = { services.restic.backups.sisko = {
paths = [ paths = [
"/persist" "/persist"
"/mnt/hd/immich" "/mnt/hd/immich"
"/mnt/hd/paperless"
]; ];
exclude = [ " /persist/var/lib/containers" ]; exclude = [ " /persist/var/lib/containers" ];
passwordFile = config.age.secrets.SISKO_RESTIC_PASSWORD.path; passwordFile = config.age.secrets.SISKO_RESTIC_PASSWORD.path;

View file

@ -0,0 +1,20 @@
{
systemd.tmpfiles.rules = [
"d /export 770 nobody nogroup"
];
fileSystems."/export/hd" = {
device = "/mnt/hd";
options = [ "bind" ];
};
services.nfs.server = {
enable = true;
exports = ''
/export 10.100.0.1/24(rw,fsid=0,no_subtree_check)
/export/hd 10.100.0.1/24(rw,nohide,insecure,no_subtree_check,no_root_squash)
'';
};
networking.firewall.allowedTCPPorts = [ 2049 ];
}

View file

@ -47,13 +47,16 @@
proxyWebsockets = true; proxyWebsockets = true;
}; };
}; };
"paper.aciceri.dev" = {
# "jellyfin.aciceri.dev" = { forceSSL = true;
enableACME = true;
locations."/" = {
proxyPass = "http://localhost:${builtins.toString config.services.paperless.port}";
};
};
# "${config.services.nextcloud.hostName}" = {
# forceSSL = true; # forceSSL = true;
# enableACME = true; # enableACME = true;
# locations."/" = {
# proxyPass = "http://localhost:8096";
# };
# }; # };
# "sevenofnix.aciceri.dev" = { # "sevenofnix.aciceri.dev" = {
# forceSSL = true; # forceSSL = true;

View file

@ -4,12 +4,13 @@
syncthing = { syncthing = {
enable = true; enable = true;
guiAddress = "${config.networking.hostName}.fleet:8434"; guiAddress = "${config.networking.hostName}.fleet:8434";
user = config.ccr.username; # TODO Use the home-manager module instead of the following conditions
dataDir = "/home/${config.ccr.username}"; user = if config.networking.hostName == "sisko" then "syncthing" else "ccr";
dataDir = if config.networking.hostName == "sisko" then "/mnt/hd/syncthing" else "/home/ccr";
settings = { settings = {
options = { options = {
urAccepted = 1; # anonymous usage data report urAccepted = 1; # anonymous usage data report
globalAnnounceEnabled = false; # Only sync on the VPN globalAnnounceEnabled = false; # Only sync when connected to the VPN
}; };
devices = { devices = {
picard = { picard = {
@ -19,7 +20,7 @@
]; ];
}; };
sisko = { sisko = {
id = "L5RAQXR-6U3ANNK-UJJ5AVN-37VKQRB-UK6HXSU-NN3V6HF-JNZEVA5-NI6UEAP"; id = "5JYQLMP-KNBMSOE-I452UDU-UTKPXJI-K27X2DI-MSCSRCG-6V54Q6U-NVGXPQA";
addresses = [ addresses = [
"tcp://sisko.fleet" "tcp://sisko.fleet"
]; ];
@ -31,7 +32,7 @@
]; ];
}; };
oneplus8t = { oneplus8t = {
id = "76BJ2ZE-FPFDWUZ-3UZIENZ-TS6YBGG-EZSF6UE-GLHRBQ2-KTHTRMI-3JWNRAT"; id = "KMB2YRF-DGTWU24-SLITU23-5TN7BMQ-6PFAQQZ-CZ7J2QL-PIGVBTU-VRFRMQV";
addresses = [ addresses = [
"tcp://oneplus8t.fleet" "tcp://oneplus8t.fleet"
]; ];
@ -41,9 +42,9 @@
org = { org = {
path = path =
{ {
picard = "/home/${config.ccr.username}/org"; picard = "/home/ccr/org";
sisko = "/home/${config.ccr.username}/org"; sisko = "/mnt/hd/syncthing/org";
kirk = "/home/${config.ccr.username}/org"; kirk = "/home/ccr/org";
} }
.${config.networking.hostName}; .${config.networking.hostName};
devices = [ devices = [
@ -56,9 +57,9 @@
sync = { sync = {
path = path =
{ {
picard = "/home/${config.ccr.username}/sync"; picard = "/home/ccr/sync";
sisko = "/home/${config.ccr.username}/sync"; sisko = "/mnt/hd/syncthing/sync";
kirk = "/home/${config.ccr.username}/sync"; kirk = "/home/ccr/sync";
} }
.${config.networking.hostName}; .${config.networking.hostName};
devices = [ devices = [

View file

@ -54,10 +54,10 @@
(name: value: { (name: value: {
inherit name; inherit name;
value = pkgs.callPackage "${self}/packages/${name}" { value = pkgs.callPackage "${self}/packages/${name}" {
pkgsStable = inputs.nixpkgsStable.legacyPackages.${system};
dream2nix = inputs.dream2nix; dream2nix = inputs.dream2nix;
projectRoot = self.outPath; projectRoot = self.outPath;
packagePath = "packages/${name}"; packagePath = "packages/${name}";
inherit inputs;
}; };
}) })
(lib.filterAttrs (_: type: type == "directory") (builtins.readDir "${self}/packages")) (lib.filterAttrs (_: type: type == "directory") (builtins.readDir "${self}/packages"))

View file

@ -0,0 +1,35 @@
{
lib,
inputs,
pkgs,
...
}:
let
pkgs' = pkgs.extend (
lib.composeManyExtensions [
inputs.emacs-overlay.overlays.package
inputs.emacs-overlay.overlays.emacs
]
);
all-grammars = pkgs'.tree-sitter.withPlugins builtins.attrValues;
treesitGrammars = pkgs'.runCommand "treesit-grammars" { } ''
mkdir $out
for f in ${all-grammars}/*
do
cp $f $out/"libtree-sitter-$(basename $f)"
done
'';
emacsWithoutPackages = pkgs'.emacs-git.override {
withSQLite3 = true;
withWebP = true;
withPgtk = true;
};
emacs = (pkgs'.emacsPackagesFor emacsWithoutPackages).emacsWithPackages (
import ./packages.nix pkgs'
);
in
emacs.overrideAttrs {
passthru = {
inherit treesitGrammars;
};
}

108
packages/emacs/packages.nix Normal file
View file

@ -0,0 +1,108 @@
pkgs: epkgs:
let
inherit (epkgs) melpaPackages nongnuPackages elpaPackages;
# *Attrset* containig extra emacs packages from flake inputs
# *List* containing emacs packages from (M)ELPA
mainPackages =
# builtins.filter
# if an extra package has the same name then give precedence to it
# (package: ! builtins.elem package.pname (builtins.attrNames extraPackages))
(with melpaPackages; [
meow
meow-tree-sitter
dracula-theme
nord-theme
catppuccin-theme
modus-themes
# solaire-mode
nerd-icons
nerd-icons-completion
nerd-icons-ibuffer
nerd-icons-dired
ligature
treemacs-nerd-icons
eshell-syntax-highlighting
fish-completion # fish completion for eshell
eshell-prompt-extras
eshell-atuin
eshell-command-not-found
clipetty
sideline
consult-eglot
# sideline-flymake
rainbow-delimiters
vertico
marginalia
consult
orderless
embark
embark-consult
magit
magit-delta
magit-todos
difftastic
with-editor
diff-hl
corfu
cape
which-key
nix-mode
nix-ts-mode
agenix
zig-mode
unisonlang-mode
purescript-mode
dhall-mode
envrc
inheritenv
popper
paredit
yaml-mode
hl-todo
markdown-mode
haskell-mode
terraform-mode
diredfl
org-modern
org-roam
org-roam-ql
visual-fill-column
consult-org-roam
pass
password-store-otp
eldoc-box
go-translate
notmuch
consult-notmuch
poly-org
casual-calc
gptel
agenix
solidity-mode
# org-re-reveal # FIXME very not nice hash mismatch when building
# gptel # TODO uncomment when there will be a new release including GPT-4o
])
++ (with elpaPackages; [
delight
kind-icon
ef-themes
indent-bars
])
++ (with nongnuPackages; [
eat
corfu-terminal
haskell-ts-mode
]);
in
mainPackages
# ++ (builtins.attrValues extraPackages)
# Playing with EAF
++ [
# Disabled because pymupdf was broken
# (pkgs.callPackage ./eaf.nix {
# inherit (epkgs) melpaBuild;
# inherit (melpaPackages) ctable deferred epc s;
# })
]