commit 47fdc6b188dbbc055c2dba7b9ea055458db9e85b
parent 0842b6f6a2644ec2a4a1c23c01c6323bf49141a2
Author: Silas Brack <silasbrack@gmail.com>
Date: Sun, 31 May 2026 21:33:20 +0200
feat: implement mailserver & others
Diffstat:
9 files changed, 258 insertions(+), 8 deletions(-)
diff --git a/flake.lock b/flake.lock
@@ -1,5 +1,21 @@
{
"nodes": {
+ "blobs": {
+ "flake": false,
+ "locked": {
+ "lastModified": 1604995301,
+ "narHash": "sha256-wcLzgLec6SGJA8fx1OEN1yV/Py5b+U5iyYpksUY/yLw=",
+ "owner": "simple-nixos-mailserver",
+ "repo": "blobs",
+ "rev": "2cccdf1ca48316f2cfd1c9a0017e8de5a7156265",
+ "type": "gitlab"
+ },
+ "original": {
+ "owner": "simple-nixos-mailserver",
+ "repo": "blobs",
+ "type": "gitlab"
+ }
+ },
"crane": {
"locked": {
"lastModified": 1776635034,
@@ -15,6 +31,22 @@
"type": "github"
}
},
+ "flake-compat": {
+ "flake": false,
+ "locked": {
+ "lastModified": 1767039857,
+ "narHash": "sha256-vNpUSpF5Nuw8xvDLj2KCwwksIbjua2LZCqhV1LNRDns=",
+ "owner": "edolstra",
+ "repo": "flake-compat",
+ "rev": "5edf11c44bc78a0d334f6334cdaf7d60d732daab",
+ "type": "github"
+ },
+ "original": {
+ "owner": "edolstra",
+ "repo": "flake-compat",
+ "type": "github"
+ }
+ },
"flake-utils": {
"inputs": {
"systems": "systems"
@@ -33,6 +65,54 @@
"type": "github"
}
},
+ "git-hooks": {
+ "inputs": {
+ "flake-compat": [
+ "nixos-mailserver",
+ "flake-compat"
+ ],
+ "gitignore": "gitignore",
+ "nixpkgs": [
+ "nixos-mailserver",
+ "nixpkgs"
+ ]
+ },
+ "locked": {
+ "lastModified": 1772893680,
+ "narHash": "sha256-JDqZMgxUTCq85ObSaFw0HhE+lvdOre1lx9iI6vYyOEs=",
+ "owner": "cachix",
+ "repo": "git-hooks.nix",
+ "rev": "8baab586afc9c9b57645a734c820e4ac0a604af9",
+ "type": "github"
+ },
+ "original": {
+ "owner": "cachix",
+ "repo": "git-hooks.nix",
+ "type": "github"
+ }
+ },
+ "gitignore": {
+ "inputs": {
+ "nixpkgs": [
+ "nixos-mailserver",
+ "git-hooks",
+ "nixpkgs"
+ ]
+ },
+ "locked": {
+ "lastModified": 1709087332,
+ "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=",
+ "owner": "hercules-ci",
+ "repo": "gitignore.nix",
+ "rev": "637db329424fd7e46cf4185293b9cc8c88c95394",
+ "type": "github"
+ },
+ "original": {
+ "owner": "hercules-ci",
+ "repo": "gitignore.nix",
+ "type": "github"
+ }
+ },
"home-manager": {
"inputs": {
"nixpkgs": [
@@ -73,6 +153,30 @@
"url": "ssh://git@github.com/silasbrack/mkv"
}
},
+ "nixos-mailserver": {
+ "inputs": {
+ "blobs": "blobs",
+ "flake-compat": "flake-compat",
+ "git-hooks": "git-hooks",
+ "nixpkgs": [
+ "nixpkgs"
+ ]
+ },
+ "locked": {
+ "lastModified": 1773912645,
+ "narHash": "sha256-QHzRqq6gh+t3F/QU9DkP7X63dDDcuIQmaDz12p7ANTg=",
+ "owner": "simple-nixos-mailserver",
+ "repo": "nixos-mailserver",
+ "rev": "25e6dbb8fca3b6e779c5a46fd03bd760b2165bb5",
+ "type": "gitlab"
+ },
+ "original": {
+ "owner": "simple-nixos-mailserver",
+ "ref": "nixos-25.11",
+ "repo": "nixos-mailserver",
+ "type": "gitlab"
+ }
+ },
"nixpkgs": {
"locked": {
"lastModified": 1772773019,
@@ -157,6 +261,7 @@
"inputs": {
"home-manager": "home-manager",
"mkv": "mkv",
+ "nixos-mailserver": "nixos-mailserver",
"nixpkgs": "nixpkgs_2",
"nixpkgs-unstable": "nixpkgs-unstable",
"simple-web-app": "simple-web-app",
diff --git a/flake.nix b/flake.nix
@@ -9,6 +9,10 @@
url = "git+ssh://forgejo@git.fnarglebeast.com/silas/website.git";
flake = false;
};
+ nixos-mailserver = {
+ url = "gitlab:simple-nixos-mailserver/nixos-mailserver/nixos-25.11";
+ inputs.nixpkgs.follows = "nixpkgs";
+ };
home-manager = {
url = "github:nix-community/home-manager/release-25.11";
inputs.nixpkgs.follows = "nixpkgs";
@@ -22,6 +26,7 @@
simple-web-app,
mkv,
website,
+ nixos-mailserver,
home-manager,
...
}:
@@ -42,6 +47,7 @@
inherit system;
modules = [
sops-nix.nixosModules.sops
+ nixos-mailserver.nixosModules.mailserver
(
inputs@{
pkgs,
diff --git a/hosts/gaia/configuration.nix b/hosts/gaia/configuration.nix
@@ -146,7 +146,7 @@
nixpkgs.config.allowUnfreePredicate =
pkg:
builtins.elem (lib.getName pkg) [
- # "claude-code"
+ "claude-code"
"spotify"
];
users.users.silas = {
@@ -167,7 +167,7 @@
# anki
# blender
# btop-rocm
- # claude-code
+ claude-code
# discord
# duckdb
# rocmPackages.clr
@@ -208,7 +208,7 @@
# gnumake
# hadolint
# inkscape
- # isync
+ isync
jq
keepassxc
# kitty
@@ -218,11 +218,11 @@
# localsend
# mosh
mpv
- # msmtp
+ msmtp
# ncdu
# nil
# nixfmt-rfc-style
- # notmuch
+ notmuch
# obs-studio
# obsidian
# pandoc
@@ -476,6 +476,7 @@
extensions = ps: with ps; [ pgvector ];
ensureDatabases = [
"paperless"
+ "daily_tracker_dev"
];
ensureUsers = [
{
diff --git a/hosts/gaia/home.nix b/hosts/gaia/home.nix
@@ -23,6 +23,7 @@
ep.magit
ep.expand-region
ep.hyperbole
+ ep.notmuch
(ep.treesit-grammars.with-grammars (g: [
g.tree-sitter-python
g.tree-sitter-rust
@@ -30,5 +31,72 @@
]))
];
};
+
+ programs.mbsync = {
+ enable = true;
+ extraConfig = ''
+ IMAPAccount silasbrack
+ Host mail.silasbrack.com
+ User silas@silasbrack.com
+ PassCmd "cat ~/.mail-password"
+ SSLType IMAPS
+
+ IMAPStore silasbrack-remote
+ Account silasbrack
+
+ MaildirStore silasbrack-local
+ Path ~/Mail/silasbrack/
+ Inbox ~/Mail/silasbrack/INBOX
+ SubFolders Verbatim
+
+ Channel silasbrack
+ Far :silasbrack-remote:
+ Near :silasbrack-local:
+ Patterns *
+ Create Both
+ Expunge Both
+ SyncState *
+ '';
+ };
+
+ programs.msmtp = {
+ enable = true;
+ extraConfig = ''
+ defaults
+ tls on
+
+ account silasbrack
+ host mail.silasbrack.com
+ port 465
+ tls_starttls off
+ auth on
+ user silas@silasbrack.com
+ from silas@silasbrack.com
+ passwordeval "cat ~/.mail-password"
+
+ account default : silasbrack
+ '';
+ };
+
+ accounts.email.maildirBasePath = "Mail";
+ accounts.email.accounts.silasbrack = {
+ primary = true;
+ address = "silas@silasbrack.com";
+ realName = "Silas Brack";
+ notmuch.enable = true;
+ };
+
+ programs.notmuch = {
+ enable = true;
+ new.tags = [ "new" ];
+ search.excludeTags = [ "deleted" "spam" ];
+ hooks.postNew = ''
+ notmuch tag +inbox -new -- tag:new AND folder:silasbrack/INBOX
+ notmuch tag +sent -new -- tag:new AND folder:silasbrack/Sent
+ notmuch tag +trash -new -- tag:new AND folder:silasbrack/Trash
+ notmuch tag +spam -new -- tag:new AND folder:silasbrack/Junk
+ notmuch tag -new -- tag:new
+ '';
+ };
};
}
diff --git a/hosts/gaia/init.el b/hosts/gaia/init.el
@@ -34,3 +34,16 @@
;; expand-region
(require 'expand-region)
(global-set-key (kbd "C-=") 'er/expand-region)
+
+;; notmuch
+(require 'notmuch)
+(setq mail-user-agent 'notmuch-user-agent)
+(setq notmuch-fcc-dirs "silasbrack/Sent")
+(setq notmuch-search-oldest-first nil)
+(setq message-send-mail-function 'message-send-mail-with-sendmail)
+(setq sendmail-program "msmtp")
+(setq message-sendmail-extra-arguments '("--read-envelope-from"))
+(setq message-sendmail-f-is-evil t)
+(setq user-mail-address "silas@silasbrack.com")
+(setq user-full-name "Silas Brack")
+(global-set-key (kbd "C-c m") 'notmuch)
diff --git a/hosts/helios.nix b/hosts/helios.nix
@@ -15,6 +15,7 @@
../modules/forgejo.nix
../modules/mkv.nix
../modules/simple-web-app.nix
+ ../modules/mailserver.nix
];
# Generic
@@ -123,6 +124,20 @@
traildepotPath = "${simple-web-app}/traildepot";
};
+ # Mail server
+ services.local.mailserver = {
+ enable = true;
+ fqdn = "mail.silasbrack.com";
+ domains = [ "silasbrack.com" ];
+ accounts = {
+ "silas@silasbrack.com" = {
+ hashedPasswordFile = config.sops.secrets.mail_silas_password_hash.path;
+ aliases = [ "postmaster@silasbrack.com" ];
+ };
+ };
+ };
+ sops.secrets.mail_silas_password_hash = { };
+
# Users
users.users = {
root.openssh.authorizedKeys.keys = [
diff --git a/modules/mailserver.nix b/modules/mailserver.nix
@@ -0,0 +1,41 @@
+{
+ config,
+ lib,
+ ...
+}:
+
+let
+ cfg = config.services.local.mailserver;
+in
+{
+ options.services.local.mailserver = {
+ enable = lib.mkEnableOption "Mail server";
+
+ fqdn = lib.mkOption {
+ type = lib.types.str;
+ description = "Fully qualified domain name of the mail server";
+ };
+
+ domains = lib.mkOption {
+ type = lib.types.listOf lib.types.str;
+ description = "Domains to receive mail for";
+ };
+
+ accounts = lib.mkOption {
+ type = lib.types.attrs;
+ default = { };
+ description = "Mail accounts configuration";
+ };
+ };
+
+ config = lib.mkIf cfg.enable {
+ mailserver = {
+ enable = true;
+ stateVersion = 5;
+ fqdn = cfg.fqdn;
+ domains = cfg.domains;
+ certificateScheme = "acme-nginx";
+ loginAccounts = cfg.accounts;
+ };
+ };
+}
diff --git a/modules/wireguard-client.nix b/modules/wireguard-client.nix
@@ -16,7 +16,7 @@ let
10.100.0.4 gaia
10.100.0.5 ipad
10.100.0.6 macbook
- 10.100.0.7 helios
+ 10.100.0.7 helios mail.silasbrack.com
10.100.0.8 xps
'';
in
diff --git a/secrets.yaml b/secrets.yaml
@@ -7,6 +7,7 @@ gaia_wireguard_private_key: ENC[AES256_GCM,data:pWIY9Hf4/diJsREVZQR1D9noV1vdQrWg
gaia_forgejo_runner_token: ENC[AES256_GCM,data:0XA6nSeBW4uh/cx/95xw0Ood/a68qD4Z/8d77rj4WEnHQPNDRKzBBKQZFqjk7ME=,iv:CcWsra/iSYH+edNixzxw6WNYHZUJO1SDXwJrho6i1DE=,tag:3tximHNd2TnTZLAPOghpMg==,type:str]
gaia_gitea_runner_ssh_key: ENC[AES256_GCM,data:CZZMptDd9s7WQsx9vnHv7tF1CJ9cKd+lnwuv75+a1p4OWxANObfIFtJOTDDHNL1+fk4/48/OOtODrgZlfeLxVPmmiPsvnSPOA1POhi6q97bpOWk/YWgViIWaf4UsLWDBq3/tngcJCa5JebrKuY+T7MaJn1Lw+C8h+91WiIWYSzOSMmZIoMDo5Y85cPqesFKxgmSlgTYbv5mJnFQ6pvhqTLHRpIPW5PNgRjeXsjJrblvs/edYtLyAVm6H0IYMiMa24+272f90l/2Vc16PXk6RCCFIIJ9HuGYGRBFMAD0bYPDo/ESTMn2u1MATGVCPKCLMjTxFglowfavZpb7xLk7EvZoWPQXx/+KcTV3+iVabOv0kI8OLZFGUPCs4B1MiIeCfSLEr4nop2SNm3C7pGFuKJ0EFGbnhdfBqBqVKZVkXJNGY023BHC3eXx57VatisHQAwLbs2qgSWvOetTAhw8E12QylFtq6mXWG3lZuhJxoiZ6QMI9Yjc/Fvxt4NBOjFtbF0iDRd0g0FSv+geQ7BNid,iv:ufcXcVm2NQRTmG7mYDPwCihm4SYkQ9llpoKtP73Z9Ik=,tag:g4RNNXCBZL0EluwGTYC7fA==,type:str]
gaia_forgejo_access_token: ENC[AES256_GCM,data:KpAnvpevOWgbNrEr+QynldkDqonVD90MuVDrBwG7mBClZxHfcps6aw==,iv:22zJ3/BBu5/aFMjgwhQ3lJH+lzOT53bozdAdrzMUGRI=,tag:Vic8ZfT43b0CfphZ/hP1DQ==,type:str]
+mail_silas_password_hash: ENC[AES256_GCM,data:E3NCQ5/eRfpbaOVpK0Ovk/vE+h1q5/z7rZrSelQy2Qc++a6nMYA/q7/YmfFT2R68JwOJMaeNMnOKjn1A,iv:sGgRC5SRRiziBd5TVO273uouyFhRqvB0e04xDhifSfM=,tag:U1QxtlVbDUw+nhFm+ZnxOA==,type:str]
sops:
age:
- recipient: age1s29qgfupgx7mqn9dk2rndp6xh63f5qan8vqa77ve603wd5m9a9rqkpq7re
@@ -45,7 +46,7 @@ sops:
ZGptR25vdWhEKzFxbE5qS051ZFF4RkEK1hGCQVE1E92nFSwSeYRRhxn2p3wBfhpS
TZMRsiXtUdlgnoa+IJcFKsbwaZspYnKCA93IEfOXy6TadcoNiFzypw==
-----END AGE ENCRYPTED FILE-----
- lastmodified: "2026-05-10T13:36:39Z"
- mac: ENC[AES256_GCM,data:4mM0xVE2VBP428DVAJJ5GkaE19QsIvj+pVc9UI9U90dnjUT6VkZlVxLdYgnbE1yuVhzGIksrwFEQYvr1hporQUWaL2cdF/CYPf8OHyM3xnpNa+U3xF0sb3TGz0YyF1QDBvpTF+avsSQNb+NAs4M0eEE9MWOuGmkROAHKnoK/2z0=,iv:6JH7nV2SYii1ojmMp6KMtzi0dkB6a9xWlfItZOQ9jS8=,tag:yaFwI4KJX+P7tPQ2udSvUw==,type:str]
+ lastmodified: "2026-05-31T11:49:47Z"
+ mac: ENC[AES256_GCM,data:6X9/faApExW9xtorxSAODyVQwSCtDTel0iTtc7ZaeRgiRqQmpPTw8bZ4Zln51lWqUO3EOSRH6ge2btNrtQbPmZoBAmh0dntDvB6LsvJn3NVDeq5vM3vV1dMDyjiXBMqxT99tYkTSK08j10QcCLRRkHIoj4wHIabg7INPCpNadnA=,iv:18yZmCDZzRf96i7L72ogvwCJ6rHvGhzvA0Pq07Jhy08=,tag:j8xTjdG7tndylumNkNVFGA==,type:str]
unencrypted_suffix: _unencrypted
version: 3.12.1