Compare commits

..

No commits in common. "master" and "feat/mcentire-crowdsec" have entirely different histories.

54 changed files with 726 additions and 1529 deletions

74
.gitignore vendored
View file

@ -1,74 +0,0 @@
### Linux gitignore ###
*~
# temporary files which can be created if a process still has a handle open of a deleted file
.fuse_hidden*
# Metadata left by Dolphin file manager, which comes with KDE Plasma
.directory
# Linux trash folder which might appear on any partition or disk
.Trash-*
# .nfs files are created when an open file is removed but is still being accessed
.nfs*
# Log files created by default by the nohup command
nohup.out
### MacOS gitignore ###
# General
.DS_Store
__MACOSX/
.AppleDouble
.LSOverride
Icon[]
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
### Nix gitignore ###
# Ignore build outputs from performing a nix-build or `nix build` command
result
result-*
# Ignore automatically generated direnv output
.direnv
### Vim gitignore ###
# Swap
[._]*.s[a-v][a-z]
# comment out the next line if you don't need vector files
!*.svg
[._]*.sw[a-p]
[._]s[a-rt-v][a-z]
[._]ss[a-gi-z]
[._]sw[a-p]
# Session
Session.vim
Sessionx.vim
# Temporary
.netrwhist
*~
# Auto-generated tag files
tags
# Persistent undo
[._]*.un~

157
README.md
View file

@ -1,157 +0,0 @@
# nix-dotfiles
System and home configurations for my machines.
| Machine | Role | OS | Arch | System config tool | Home config tool |
| -------- | --------------- | ------ | ------- | --------------------------- | ---------------- |
| anderson | server | linux | x86_64 | dpkg/Docker (not this repo) | home-manager |
| bosephus | server | linux | x86_64 | NixOS | home-manager |
| mcentire | server | linux | x86_64 | NixOS | home-manager |
| corianne | MacBook | darwin | aarch64 | nix-darwin | home-manager |
| odyssey | workstation | linux | x86_64 | Ansible | home-manager |
## Quickstart
### Home dotfiles
> ![WARNING]
> Fedora systems will set this up automagically via Ansible. Follow the
[Fedora quickstart] instructions.
Ensure Nix is installed, with the `nix` command and flakes enabled. I try to use
the [Determinate Nix installer] (with upstream Nix) to install Nix with these
options turned on by default.
```bash
curl -fsSL https://install.determinate.systems/nix | sh -s -- install
```
Once Nix is installed, clone the repository to `~/.config/home-manager` and
initiate home-manager.
```bash
git clone https://code.millironx.com/millironx/nix-dotfiles.git ~/.config/home-manager
nix run home-manager -- switch --flake ~/.config/home-manager#$USER@$(hostname -s)
```
In the case that the host has not been assigned a configuration within this repo
yet, pick a hostname with the same system OS, arch, and role as the target
system to get temporary dotfiles up and running.
```bash
nix run home-manager -- switch --flake ~/.config/home-manager#millironx@anderson
```
Once an SSH (with or without GPG) key has been setup and added to the authorized
keys of the git server, switch the upstream to track an authorized (i.e.
read/write) version of the repo.
```bash
cd ~/.config/home-manager
git remote set-url origin git@code.millironx.com:millironx/nix-dotfiles.git
cd -
```
### NixOS
Switching to a flake-based config requires running as root. All of the following
commands are assumed to be running as root.
Ensure that the `nix` command and flakes are enabled.
```bash
sed -i '/^}/i nix.settings.experimental-features = [ "nix-command" "flakes" ];' /etc/nixos/configuration.nix
nixos-rebuild switch
```
> ![NOTE]
> To allow secret decryption in the system, the *machine*-specific SSH key must
> be added to the publicKeys attribute of all applicable secrets, and the
> `hardware-configuration.nix` file must be added to git. Copying arbitrary
> strings like SSH keys or disk UUIDs between systems can be painful, so it might
> be worth setting up the [home dotfiles] immediately after enabling flakes,
> then running these steps on the same machine to avoid typos. Alternatively, I
> might someday be smart enough to create an installer CD that automagically
> sets this up for me.
Get the machine-specific public SSH key.
```bash
cat /etc/ssh/ssh_host_ed25519_key.pub
```
On a separate machine, add the machine's SSH key to `./secrets.nix` and assign
it to any secrets it would need.
```nix
let
bosephus-host =
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIxTfeg+GZsfmG8TuEV1xW1gXknAIKzZ3UjZ3guRY+EW root@nixos";
bosephus-millironx =
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKaDPqRJHoqgY2pseh/mnhjaGWXprHk2s5I52LhHpHcF millironx@bosephus";
odyssey-millironx =
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIN9Aj7BtQp1Roa0tgopDrUo7g2am5WJ43lO1d1fDUz45 millironx@odyssey";
system-administrators = [
bosephus-millironx
odyssey-millironx
];
in {
"secrets/network-information.age".publicKeys = system-administrators
++ [ bosephus-host ];
}
```
Rekey the secrets, and push the updated secrets to the upstream repo.
```bash
nix run github:ryantm/agenix -- --rekey
git add secrets.nix secrets/*
git commit -m "added $NEW_HOST to secrets"
git push
```
Copy the target machine's `hardware-configuration.nix` file to this repo's
`./systems/linux/hardware-configuration/$NEW_HOST.nix`, and be sure to update
the configuration to import its own `hardware-configuration`.
```bash
cp /etc/nixos/hardware-configuration.nix ./systems/linux/hardware-configuration/$NEW_HOST.nix
```
```nix
{ config, pkgs, ... }: {
imports = [
./hardware-configuration/bosephus.nix
];
}
```
Commit and push the hardware configuration to the upstream repo.
```bash
git add systems/linux/*
git commit -m "added $NEW_HOST hardware configuration"
git push
```
Now switch to the flake by pulling and switching in one step.
```bash
nixos-rebuild switch --flake git+https://code.millironx.com/millironx/nix-dotfiles#$(hostname -s)
```
### Fedora
Fedora systems are managed using Ansible.
TODO
## Home settings
TODO
[determinate nix installer]: https://github.com/DeterminateSystems/nix-installer
[fedora quickstart]: #fedora
[home dotfiles]: #home-dotfiles

View file

@ -1,32 +0,0 @@
#!/usr/bin/env julia
using TOML: TOML
@show ENV["PATH"]
function remove_podman_secrets()
run(`podman secret rm --all`)
return nothing
end #function
function create_podman_secret(name, secret)
run(
addenv(
`podman secret create --env=true --replace $name SECRET`,
Dict("SECRET" => secret),
),
)
return nothing
end #function
function parse_toml_secrets(file)
foreach(f -> create_podman_secret(f[1], f[2]), TOML.parsefile(file))
return nothing
end #function
function main()
remove_podman_secrets()
foreach(parse_toml_secrets, ARGS)
return 0
end #function
main()

24
dotfiles/dolphinrc Normal file
View file

@ -0,0 +1,24 @@
MenuBar=Disabled
[DetailsMode]
PreviewSize=16
[General]
BrowseThroughArchives=true
EditableUrl=true
GlobalViewProps=false
ShowFullPath=true
ShowStatusBar=FullWidth
ShowZoomSlider=true
Version=202
[KFileDialog Settings]
Places Icons Auto-resize=false
Places Icons Static Size=22
[MainWindow]
MenuBar=Disabled
[PreviewSettings]
Plugins=audiothumbnail,avif,blenderthumbnail,comicbookthumbnail,cursorthumbnail,djvuthumbnail,ebookthumbnail,exrthumbnail,directorythumbnail,fontthumbnail,imagethumbnail,jpegthumbnail,jxl,kraorathumbnail,windowsexethumbnail,windowsimagethumbnail,mobithumbnail,opendocumentthumbnail,gsthumbnail,rawthumbnail,svgthumbnail,gdk-pixbuf-thumbnailer,ffmpegthumbs,gsf-office

9
dotfiles/konsolerc Normal file
View file

@ -0,0 +1,9 @@
[Desktop Entry]
DefaultProfile=My Default.profile
[MainWindow]
StatusBar=Disabled
ToolBarsMovable=Disabled
[UiSettings]
ColorScheme=Default

181
flake.lock generated
View file

@ -14,11 +14,11 @@
"systems": "systems" "systems": "systems"
}, },
"locked": { "locked": {
"lastModified": 1762618334, "lastModified": 1754433428,
"narHash": "sha256-wyT7Pl6tMFbFrs8Lk/TlEs81N6L+VSybPfiIgzU8lbQ=", "narHash": "sha256-NA/FT2hVhKDftbHSwVnoRTFhes62+7dxZbxj5Gxvghs=",
"owner": "ryantm", "owner": "ryantm",
"repo": "agenix", "repo": "agenix",
"rev": "fcdea223397448d35d9b31f798479227e80183f6", "rev": "9edb1787864c4f59ae5074ad498b6272b3ec308d",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -27,6 +27,27 @@
"type": "github" "type": "github"
} }
}, },
"crowdsec": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1752497357,
"narHash": "sha256-9epXn1+T6U4Kfyw8B9zMzbERxDB3VfaPXhVebtai6CE=",
"ref": "refs/heads/main",
"rev": "84db7dcea77f7f477d79e69e35fb0bb560232667",
"revCount": 42,
"type": "git",
"url": "https://codeberg.org/kampka/nix-flake-crowdsec.git"
},
"original": {
"type": "git",
"url": "https://codeberg.org/kampka/nix-flake-crowdsec.git"
}
},
"flake-parts": { "flake-parts": {
"inputs": { "inputs": {
"nixpkgs-lib": [ "nixpkgs-lib": [
@ -48,6 +69,23 @@
"type": "github" "type": "github"
} }
}, },
"flake-utils": {
"inputs": {
"systems": "systems_2"
},
"locked": {
"lastModified": 1731533236,
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
"type": "github"
},
"original": {
"id": "flake-utils",
"type": "indirect"
}
},
"home-manager": { "home-manager": {
"inputs": { "inputs": {
"nixpkgs": [ "nixpkgs": [
@ -55,16 +93,16 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1764613336, "lastModified": 1756679287,
"narHash": "sha256-L979az28t/+SXvYw9qhOno5HLlDwkZOpz6LzCLnjmRM=", "narHash": "sha256-Xd1vOeY9ccDf5VtVK12yM0FS6qqvfUop8UQlxEB+gTQ=",
"owner": "nix-community", "owner": "nix-community",
"repo": "home-manager", "repo": "home-manager",
"rev": "f3902b5d8767985680875ad86d028371100faeb3", "rev": "07fc025fe10487dd80f2ec694f1cd790e752d0e8",
"type": "github" "type": "github"
}, },
"original": { "original": {
"owner": "nix-community", "owner": "nix-community",
"ref": "release-25.11", "ref": "release-25.05",
"repo": "home-manager", "repo": "home-manager",
"type": "github" "type": "github"
} }
@ -76,117 +114,59 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1764161084, "lastModified": 1757432263,
"narHash": "sha256-HN84sByg9FhJnojkGGDSrcjcbeioFWoNXfuyYfJ1kBE=", "narHash": "sha256-qHn+/0+IOz5cG68BZUwL9BV3EO/e9eNKCjH3+N7wMdI=",
"owner": "LnL7", "owner": "LnL7",
"repo": "nix-darwin", "repo": "nix-darwin",
"rev": "e95de00a471d07435e0527ff4db092c84998698e", "rev": "1fef4404de4d1596aa5ab2bd68078370e1b9dcdb",
"type": "github" "type": "github"
}, },
"original": { "original": {
"owner": "LnL7", "owner": "LnL7",
"ref": "nix-darwin-25.11", "ref": "nix-darwin-25.05",
"repo": "nix-darwin", "repo": "nix-darwin",
"type": "github" "type": "github"
} }
}, },
"nix-rosetta-builder": {
"inputs": {
"nixos-generators": "nixos-generators",
"nixpkgs": [
"nixpkgs-darwin"
]
},
"locked": {
"lastModified": 1756177999,
"narHash": "sha256-aSbB7/jrt7ujiJ55f2uGhOo+usGxVSkqbAMVgg2jDls=",
"owner": "cpick",
"repo": "nix-rosetta-builder",
"rev": "ebb7162a975074fb570a2c3ac02bc543ff2e9df4",
"type": "github"
},
"original": {
"owner": "cpick",
"repo": "nix-rosetta-builder",
"type": "github"
}
},
"nixlib": {
"locked": {
"lastModified": 1736643958,
"narHash": "sha256-tmpqTSWVRJVhpvfSN9KXBvKEXplrwKnSZNAoNPf/S/s=",
"owner": "nix-community",
"repo": "nixpkgs.lib",
"rev": "1418bc28a52126761c02dd3d89b2d8ca0f521181",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "nixpkgs.lib",
"type": "github"
}
},
"nixos-generators": {
"inputs": {
"nixlib": "nixlib",
"nixpkgs": [
"nix-rosetta-builder",
"nixpkgs"
]
},
"locked": {
"lastModified": 1737057290,
"narHash": "sha256-3Pe0yKlCc7EOeq1X/aJVDH0CtNL+tIBm49vpepwL1MQ=",
"owner": "nix-community",
"repo": "nixos-generators",
"rev": "d002ce9b6e7eb467cd1c6bb9aef9c35d191b5453",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "nixos-generators",
"type": "github"
}
},
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1764522689, "lastModified": 1757545623,
"narHash": "sha256-SqUuBFjhl/kpDiVaKLQBoD8TLD+/cTUzzgVFoaHrkqY=", "narHash": "sha256-mCxPABZ6jRjUQx3bPP4vjA68ETbPLNz9V2pk9tO7pRQ=",
"owner": "nixos", "owner": "nixos",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "8bb5646e0bed5dbd3ab08c7a7cc15b75ab4e1d0f", "rev": "8cd5ce828d5d1d16feff37340171a98fc3bf6526",
"type": "github" "type": "github"
}, },
"original": { "original": {
"owner": "nixos", "owner": "nixos",
"ref": "nixos-25.11", "ref": "nixos-25.05",
"repo": "nixpkgs", "repo": "nixpkgs",
"type": "github" "type": "github"
} }
}, },
"nixpkgs-darwin": { "nixpkgs-darwin": {
"locked": { "locked": {
"lastModified": 1764572236, "lastModified": 1757590060,
"narHash": "sha256-hLp6T/vKdrBQolpbN3EhJOKTXZYxJZPzpnoZz+fEGlE=", "narHash": "sha256-EWwwdKLMZALkgHFyKW7rmyhxECO74+N+ZO5xTDnY/5c=",
"owner": "nixos", "owner": "nixos",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "b0924ea1889b366de6bb0018a9db70b2c43a15f8", "rev": "0ef228213045d2cdb5a169a95d63ded38670b293",
"type": "github" "type": "github"
}, },
"original": { "original": {
"owner": "nixos", "owner": "nixos",
"ref": "nixpkgs-25.11-darwin", "ref": "nixpkgs-25.05-darwin",
"repo": "nixpkgs", "repo": "nixpkgs",
"type": "github" "type": "github"
} }
}, },
"nixpkgs-unstable": { "nixpkgs-unstable": {
"locked": { "locked": {
"lastModified": 1764642553, "lastModified": 1757034884,
"narHash": "sha256-mvbFFzVBhVK1FjyPHZGMAKpNiqkr7k++xIwy+p/NQvA=", "narHash": "sha256-PgLSZDBEWUHpfTRfFyklmiiLBE1i1aGCtz4eRA3POao=",
"owner": "nixos", "owner": "nixos",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "f720de59066162ee879adcc8c79e15c51fe6bfb4", "rev": "ca77296380960cd497a765102eeb1356eb80fed0",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -204,11 +184,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1764683664, "lastModified": 1757647720,
"narHash": "sha256-Mr5HKf/bjAJ8H7/H0qJSk2BEV/OILkDIFKrGK0dUVUk=", "narHash": "sha256-qf/utP3d1qBDl5R4yWUCt7E7CHTkw2NY8BEsS7lJ0dc=",
"owner": "nix-community", "owner": "nix-community",
"repo": "NUR", "repo": "NUR",
"rev": "b8b40e258cf4c959b06b7322648c87674633629b", "rev": "ef767aa25f9f917fe25d3848051f0e54ae42349f",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -227,11 +207,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1763909441, "lastModified": 1756632588,
"narHash": "sha256-56LwV51TX/FhgX+5LCG6akQ5KrOWuKgcJa+eUsRMxsc=", "narHash": "sha256-ydam6eggXf3ZwRutyCABwSbMAlX+5lW6w1SVZQ+kfSo=",
"owner": "nix-community", "owner": "nix-community",
"repo": "plasma-manager", "repo": "plasma-manager",
"rev": "b24ed4b272256dfc1cc2291f89a9821d5f9e14b4", "rev": "d47428e5390d6a5a8f764808a4db15929347cd77",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -242,11 +222,11 @@
}, },
"quadlet-nix": { "quadlet-nix": {
"locked": { "locked": {
"lastModified": 1763141753, "lastModified": 1754008153,
"narHash": "sha256-XAHkOkLEWbRQZ6t/SowwOukrUfIneNQOC/UEQlTaPBU=", "narHash": "sha256-MYT1mDtSkiVg343agxgBFsnuNU3xS8vRy399JXX1Vw0=",
"owner": "SEIAROTg", "owner": "SEIAROTg",
"repo": "quadlet-nix", "repo": "quadlet-nix",
"rev": "211b5c626cf9ea91403b510e2ac5ca03a7194566", "rev": "1b2d27d460d8c7e4da5ba44ede463b427160b5c4",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -258,9 +238,9 @@
"root": { "root": {
"inputs": { "inputs": {
"agenix": "agenix", "agenix": "agenix",
"crowdsec": "crowdsec",
"home-manager": "home-manager", "home-manager": "home-manager",
"nix-darwin": "nix-darwin", "nix-darwin": "nix-darwin",
"nix-rosetta-builder": "nix-rosetta-builder",
"nixpkgs": "nixpkgs", "nixpkgs": "nixpkgs",
"nixpkgs-darwin": "nixpkgs-darwin", "nixpkgs-darwin": "nixpkgs-darwin",
"nixpkgs-unstable": "nixpkgs-unstable", "nixpkgs-unstable": "nixpkgs-unstable",
@ -278,11 +258,11 @@
}, },
"locked": { "locked": {
"dir": "pkgs/firefox-addons", "dir": "pkgs/firefox-addons",
"lastModified": 1764648280, "lastModified": 1757591399,
"narHash": "sha256-xniOnxIx/qhm+maO4mb9BZ7FytcUhNeTm1Y/QBjNf8o=", "narHash": "sha256-OlvNzfsqDok0y5PDY+2dK5T53GsxAdm1YGdYHjxAiHM=",
"owner": "rycee", "owner": "rycee",
"repo": "nur-expressions", "repo": "nur-expressions",
"rev": "119826bd51ad1a8012e0585f3a073571a35a812e", "rev": "b7d4f61ce9db44ba82859e15f6e1c175959948e3",
"type": "gitlab" "type": "gitlab"
}, },
"original": { "original": {
@ -306,6 +286,21 @@
"repo": "default", "repo": "default",
"type": "github" "type": "github"
} }
},
"systems_2": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
} }
}, },
"root": "root", "root": "root",

View file

@ -3,8 +3,8 @@
inputs = { inputs = {
# Specify the source of Home Manager and Nixpkgs. # Specify the source of Home Manager and Nixpkgs.
nixpkgs.url = "github:nixos/nixpkgs/nixos-25.11"; nixpkgs.url = "github:nixos/nixpkgs/nixos-25.05";
nixpkgs-darwin.url = "github:nixos/nixpkgs/nixpkgs-25.11-darwin"; nixpkgs-darwin.url = "github:nixos/nixpkgs/nixpkgs-25.05-darwin";
nixpkgs-unstable.url = "github:nixos/nixpkgs/nixpkgs-unstable"; nixpkgs-unstable.url = "github:nixos/nixpkgs/nixpkgs-unstable";
# Inputs for both darwin and linux systems # Inputs for both darwin and linux systems
@ -17,7 +17,7 @@
}; };
}; };
home-manager = { home-manager = {
url = "github:nix-community/home-manager/release-25.11"; url = "github:nix-community/home-manager/release-25.05";
inputs.nixpkgs.follows = "nixpkgs"; inputs.nixpkgs.follows = "nixpkgs";
}; };
nur = { nur = {
@ -30,6 +30,10 @@
}; };
# Linux-specific inputs # Linux-specific inputs
crowdsec = {
url = "git+https://codeberg.org/kampka/nix-flake-crowdsec.git";
inputs.nixpkgs.follows = "nixpkgs";
};
plasma-manager = { plasma-manager = {
url = "github:nix-community/plasma-manager"; url = "github:nix-community/plasma-manager";
inputs = { inputs = {
@ -41,28 +45,23 @@
# Darwin-specific inputs # Darwin-specific inputs
nix-darwin = { nix-darwin = {
url = "github:LnL7/nix-darwin/nix-darwin-25.11"; url = "github:LnL7/nix-darwin/nix-darwin-25.05";
inputs.nixpkgs.follows = "nixpkgs-darwin";
};
nix-rosetta-builder = {
url = "github:cpick/nix-rosetta-builder";
inputs.nixpkgs.follows = "nixpkgs-darwin"; inputs.nixpkgs.follows = "nixpkgs-darwin";
}; };
}; };
outputs = { self, nix-darwin, nixpkgs, nixpkgs-darwin, nixpkgs-unstable outputs = { self, nix-darwin, nixpkgs, nixpkgs-darwin, nixpkgs-unstable
, home-manager, agenix, rycee-nurpkgs, nur, plasma-manager, quadlet-nix , home-manager, agenix, rycee-nurpkgs, nur, crowdsec, plasma-manager
, nix-rosetta-builder, ... }: , quadlet-nix, ... }:
let let
mkHomeConfiguration = { hostname, arch ? "x86_64", os ? "linux" mkHomeConfiguration = { hostname, arch ? "x86_64", os ? "linux"
, desktop ? false, extraModules ? [ ] }: , desktop ? false, extraModules ? [ ] }:
let let
system = "${arch}-${os}"; system = "${arch}-${os}";
syspkg = if os == "darwin" then nixpkgs-darwin else nixpkgs; pkgs = import nixpkgs {
pkgs = import syspkg {
inherit system; inherit system;
config.allowUnfree = true; config.allowUnfree = true;
overlays = [ nur.overlays.default agenix.overlays.default ]; overlays = [ nur.overlays.default ];
}; };
pkgs-unstable = import nixpkgs-unstable { pkgs-unstable = import nixpkgs-unstable {
inherit system; inherit system;
@ -81,7 +80,7 @@
] ++ (if desktop then [ ./homes/desktop.nix ] else [ ]) ] ++ (if desktop then [ ./homes/desktop.nix ] else [ ])
++ (if (desktop && os == "linux") then [ ++ (if (desktop && os == "linux") then [
./homes/linux-desktop.nix ./homes/linux-desktop.nix
plasma-manager.homeModules.plasma-manager plasma-manager.homeManagerModules.plasma-manager
] else ] else
[ ]) ++ extraModules; [ ]) ++ extraModules;
extraSpecialArgs = { extraSpecialArgs = {
@ -102,12 +101,16 @@
"millironx@anderson" = mkHomeConfiguration { hostname = "anderson"; }; "millironx@anderson" = mkHomeConfiguration { hostname = "anderson"; };
"millironx@mcentire" = mkHomeConfiguration { hostname = "mcentire"; };
"millironx@bosephus" = mkHomeConfiguration { hostname = "bosephus"; }; "millironx@bosephus" = mkHomeConfiguration { hostname = "bosephus"; };
"tchristensen@beocat" = mkHomeConfiguration { hostname = "beocat"; }; "tchristensen@beocat" = mkHomeConfiguration { hostname = "beocat"; };
"millironx@harmony" = mkHomeConfiguration {
hostname = "harmony";
arch = "aarch64";
desktop = true;
};
"millironx@odyssey" = mkHomeConfiguration { "millironx@odyssey" = mkHomeConfiguration {
hostname = "odyssey"; hostname = "odyssey";
desktop = true; desktop = true;
@ -123,11 +126,8 @@
}; };
agenix = agenix; agenix = agenix;
}; };
modules = [ modules =
./systems/darwin/corianne.nix [ ./systems/darwin/corianne.nix agenix.darwinModules.default ];
agenix.darwinModules.default
nix-rosetta-builder.darwinModules.default
];
}; };
nixosConfigurations = { nixosConfigurations = {
@ -142,14 +142,13 @@
"mcentire" = nixpkgs.lib.nixosSystem { "mcentire" = nixpkgs.lib.nixosSystem {
system = "x86_64-linux"; system = "x86_64-linux";
specialArgs = {
home-manager-quadlet-nix = quadlet-nix.homeManagerModules.quadlet;
};
modules = [ modules = [
./systems/linux/mcentire.nix ./systems/linux/mcentire.nix
agenix.nixosModules.default agenix.nixosModules.default
home-manager.nixosModules.home-manager
quadlet-nix.nixosModules.quadlet quadlet-nix.nixosModules.quadlet
crowdsec.nixosModules.crowdsec
crowdsec.nixosModules.crowdsec-firewall-bouncer
{ nixpkgs.overlays = [ crowdsec.overlays.default ]; }
]; ];
}; };
}; };

View file

@ -1,4 +1,13 @@
{ config, lib, pkgs, pkgs-unstable, custom-pkgs, ... }: { { config, lib, pkgs, pkgs-unstable, ... }:
let
runic_version = "1.5.0";
runic = pkgs.fetchFromGitHub {
owner = "fredrikekre";
repo = "Runic.jl";
rev = "v${runic_version}";
hash = "sha256-y+kiBA94vUMHH0fEEBg7+c9PEgzjGqh6nCuSRnawhQI=";
};
in {
imports = [ imports = [
./../programs/shells.nix ./../programs/shells.nix
./../programs/bat.nix ./../programs/bat.nix
@ -11,8 +20,17 @@
]; ];
home = { home = {
stateVersion = "23.11"; stateVersion = "23.11";
file = {
".local/bin/runic" = {
source = runic + "/bin/runic";
executable = true;
};
".local/bin/git-runic" = {
source = runic + "/bin/git-runic";
executable = true;
};
};
packages = with pkgs; [ packages = with pkgs; [
agenix
btop btop
cowsay cowsay
figlet figlet
@ -26,7 +44,7 @@
jq jq
julia-bin julia-bin
lynx lynx
mamba-cpp micromamba
most most
nextflow nextflow
p7zip p7zip
@ -34,8 +52,6 @@
pipx pipx
python3 python3
zulu17 zulu17
custom-pkgs.jlfmt
custom-pkgs.runic
]; ];
sessionVariables = { sessionVariables = {
PAGER = "most"; PAGER = "most";
@ -67,9 +83,24 @@
"tailscale set --exit-node=$(tailscale exit-node suggest | awk '{print $4}' | head -n1)"; "tailscale set --exit-node=$(tailscale exit-node suggest | awk '{print $4}' | head -n1)";
# tsed - TailScale Exit node Disconnect # tsed - TailScale Exit node Disconnect
tsed = "tailscale set --exit-node="; tsed = "tailscale set --exit-node=";
micromamba = "mamba";
}; };
sessionPath = [ "$HOME/.local/bin" ]; sessionPath = [ "$HOME/.local/bin" ];
activation = {
recordHmGitHash = lib.hm.dag.entryAfter [ "writeBoundary" ] ''
cd "$HOME/.config/home-manager" || exit 1
if [ -z "$(${pkgs.git}/bin/git status --porcelain --untracked-files=no)" ]; then
run echo "$(${pkgs.git}/bin/git rev-parse HEAD)" | tee $HOME/.cache/hm-git-hash
else
run echo '*' | tee $HOME/.cache/hm-git-hash
fi
'';
installRunic = lib.hm.dag.entryAfter [ "writeBoundary" ] ''
run ${pkgs.julia-bin}/bin/julia --project=@runic --startup-file=no -e 'using Pkg; Pkg.add(name="Runic", version="${runic_version}")'
'';
installJuliaFormatter = lib.hm.dag.entryAfter [ "writeBoundary" ] ''
run ${pkgs.julia-bin}/bin/julia --project=@JuliaFormatter --startup-file=no -e 'using Pkg; Pkg.add(name="JuliaFormatter", version="2.1.6")'
'';
};
}; };
programs = { programs = {
home-manager.enable = true; home-manager.enable = true;

View file

@ -7,7 +7,7 @@
]; ];
home = { home = {
packages = with pkgs; [ packages = with pkgs; [
macpm asitop
pinentry_mac pinentry_mac
(pkgs.writeShellScriptBin "uq" '' (pkgs.writeShellScriptBin "uq" ''
xattr -rdv com.apple.quarantine "/Applications/$1.app" xattr -rdv com.apple.quarantine "/Applications/$1.app"
@ -34,6 +34,22 @@
launchd = { launchd = {
enable = true; enable = true;
agents = { agents = {
ollama = {
enable = true;
config = {
Label = "local.home-manager.ollama";
ProgramArguments = [ "${pkgs.ollama}/bin/ollama" "serve" ];
RunAtLoad = true;
KeepAlive = true;
StandardOutPath =
"${config.home.homeDirectory}/Library/Logs/ollama.log";
StandardErrorPath =
"${config.home.homeDirectory}/Library/Logs/ollama-error.log";
EnvironmentVariables = {
PATH = "${lib.makeBinPath [ pkgs.ollama ]}:$PATH";
};
};
};
freetube-sync = { freetube-sync = {
enable = true; enable = true;

View file

@ -2,6 +2,7 @@
imports = [ imports = [
./../programs/firefox.nix ./../programs/firefox.nix
./../programs/ghostty.nix
./../programs/zed.nix ./../programs/zed.nix
./../services/gpg-agent.nix ./../services/gpg-agent.nix
]; ];
@ -22,7 +23,7 @@
nil nil
nixd nixd
nixfmt-classic nixfmt-classic
nixos-rebuild ollama
quarto quarto
roboto-slab roboto-slab
shellcheck shellcheck

95
homes/harmony.nix Normal file
View file

@ -0,0 +1,95 @@
{ config, lib, pkgs, pkgs-unstable, ... }: {
# harmony is an Asahi Fedora box
# I don't use NixOS, so there are some programs that don't interact well with
# the base system (or won't even install) when installed from Nix.
# There is no uniform way to trigger dnf package installs from Nix, so I'm
# just going to list my packages here. I hope to create a custom script that
# mimics the ideas of a Brewfile someday
# TODO: Create a Brewfile equivalent for dnf
# dnf repos:
# https://github.com/terrapkg/packages?tab=readme-ov-file
# https://pkgs.tailscale.com/stable/fedora/tailscale.repo
# https://packagecloud.io/filips/FirefoxPWA
# copr repos:
# iucar/rstudio
# dnf packages:
# apptainer
# chromium
# firefoxpwa - The nix version installs an "immutable" runtime, which simply launches extra browser windows on non-NixOS
# inkscape
# kate
# kdiff3
# krita
# lutris
# musescore
# nextcloud-client
# nextcloud-client-dolphin
# obs-studio
# podman-compose
# podman-docker
# qownnotes
# qt
# rssguard
# rstudio-desktop
# steam
# supertuxkart
# tailscale
# thunderbird
# vlc
# vorta - The vorta package is aarch64 compatible, but you cannot see any icons, and it cannot access local ssh keys, so we have to use the dnf package instead
# yakuake
# zed
# zsh
# R
# https://downloads.sourceforge.net/project/mscorefonts2/rpms/msttcore-fonts-installer-2.6-1.noarch.rpm
home = {
username = "millironx";
homeDirectory = "/home/millironx";
# Signal desktop is not available in any other package repository for aarch64 linux
# Similarly, Bitwarden is non-functional in all other forms using a 16k page size
packages = with pkgs; [
trayscale
veracrypt
pkgs-unstable.signal-desktop
pkgs.bitwarden-desktop
];
};
programs = {
git = {
signing = {
key = "0x37A3041D1C8C4524!";
signByDefault = true;
};
};
};
services = {
gpg-agent = { sshKeys = [ "207D13371E19752A67AA2686C16354D9963821DB" ]; };
};
xdg = {
configFile = {
"nextflow.config".text = ''
params {
config_profile_description = 'harmony Asahi Linux local profile'
config_profile_contact = 'Thomas A. Christensen II <25492070+MillironX@users.noreply.github.com>'
config_profile_url = null
max_memory = 12.GB
max_cpus = 12
max_time = 7.d
}
apptainer {
enabled = true
autoMounts = true
}
process {
executor = 'local'
}
'';
};
};
}

View file

@ -113,12 +113,20 @@ in {
configFile = { configFile = {
"plasma-workspace/env/ZED_WINDOW_DECORATIONS.sh".text = "plasma-workspace/env/ZED_WINDOW_DECORATIONS.sh".text =
"export ZED_WINDOW_DECORATIONS=server"; "export ZED_WINDOW_DECORATIONS=server";
"dolphinrc".source =
mkOutOfStoreSymlink "${home-manager-repo}/dotfiles/dolphinrc";
"konsolerc".source =
mkOutOfStoreSymlink "${home-manager-repo}/dotfiles/konsolerc";
"onedrive/config".text = '' "onedrive/config".text = ''
force_session_upload = "true" force_session_upload = "true"
delay_inotify_processing = "true" delay_inotify_processing = "true"
''; '';
"yakuakerc".source =
mkOutOfStoreSymlink "${home-manager-repo}/dotfiles/yakuakerc";
}; };
dataFile = { dataFile = {
"konsole/My Default.profile".source =
mkOutOfStoreSymlink "${home-manager-repo}/dotfiles/MyDefault.profile";
"kio/servicemenus/kate.desktop".source = ./../dotfiles/kate.desktop; "kio/servicemenus/kate.desktop".source = ./../dotfiles/kate.desktop;
"kio/servicemenus/vlc.desktop".source = ./../dotfiles/vlc.desktop; "kio/servicemenus/vlc.desktop".source = ./../dotfiles/vlc.desktop;
"kio/servicemenus/word-to-pdf.desktop".source = "kio/servicemenus/word-to-pdf.desktop".source =

View file

@ -1,8 +0,0 @@
{ ... }: {
home = {
username = "millironx";
homeDirectory = "/home/millironx";
};
programs = { };
services = { };
}

View file

@ -3,13 +3,20 @@ ungrouped:
hosts: hosts:
localhost: localhost:
ansible_connection: local ansible_connection: local
harmony:
ansible_connection: local
odyssey: odyssey:
ansible_connection: local ansible_connection: local
asahi:
hosts:
harmony:
amd64: amd64:
hosts: hosts:
odyssey: odyssey:
fedora: fedora:
hosts: hosts:
harmony:
odyssey: odyssey:

View file

@ -1,167 +0,0 @@
# Warning to my future self: This module was "vibe coded" by Claude Sonnet 4.5.
# I originally had a hand-written module in here that did the secrets
# translation as the root user. I knew that I would want to support multiple
# secrets files and multiple users, and thought I should be able to create an
# arbitrary number of them, similar to how you can have an arbitrary number
# of `programs.firefox.profiles`. Unfortunately, even after looking at lots of
# example modules, I could not figure out the syntax (Nix has a serious lack
# of minimum working examples), so I broke down and asked Claude to rewrite it
# for me. Based on everything that I read, this seems to be exactly what I asked
# for.
#
# Here is the prompt I used to get here:
# [@Per service isolation on NixOS with Traefik](zed:///agent/thread/94cb8a22-ff0c-4772-a1a1-018b0107f334?name=Per+service+isolation+on+NixOS+with+Traefik)
#
# [@podman-secrets.nix](file:///home/millironx/.config/home-manager/modules/podman-secrets.nix)
#
# I originally wrote this module assuming that I would use Podman as root for
# all containers. I would like to fix the module to have as many secrets files
# processed as needed with services setup for each secret that is run as the
# appropriate user. Ideally, the syntax for working with secrets to be
# translated would be
#
# ```nix
# { config, ... }: {
# age.secrets = {
# "caddy.toml" = {
# file = ./../secrets/caddy.toml.age;
# owner = "caddy";
# group = "caddy";
# };
#
# "authentik.toml" = {
# file = ./../secrets/authentik.toml.age;
# owner = "authentik";
# group = "authentik";
# };
#
# "freshrss.toml" = {
# file = ./../secrets/freshrss.toml.age;
# owner = "freshrss";
# group = "freshrss";
# };
# };
#
# millironx.podman-secrets = with config; {
# caddy = {
# user = "caddy";
# secrets-files = [
# ./../not-really-secret.toml
# age.secrets."caddy.toml".path
# ];
# };
# authentik = {
# user = "authentik";
# secrets-files = [
# age.secrets."authentik.toml".path
# ];
# };
# freshrss = {
# user = "freshrss";
# secrets-files = [
# age.secrets."freshrss.toml".path
# ];
# };
# };
# }
# ```
#
# Can you help me rewrite the module to accomplish this?
#
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.millironx.podman-secrets;
secret-translator = pkgs.writeScriptBin "secret-translator"
(builtins.readFile ./../bin/secret-translator.jl);
# Submodule type for each service's secrets configuration
serviceSecretsType = types.submodule ({ config, name, ... }: {
options = {
user = mkOption {
type = types.str;
description = "User account to run the secrets translation service as";
example = "caddy";
};
secrets-files = mkOption {
type = types.listOf (types.either types.path types.string);
description =
"List of TOML files containing secrets to translate to Podman secrets";
example = literalExpression ''
[
"/run/agenix/caddy.toml"
./../not-really-secret.toml
]
'';
default = [ ];
};
ref = mkOption {
type = types.str;
description =
"Reference to the systemd service name for declaring dependencies";
readOnly = true;
default = "podman-secrets-${name}.service";
example = "podman-secrets-caddy.service";
};
};
});
# Generate a systemd user service for each configured service
mkSecretsService = name: serviceCfg:
nameValuePair "podman-secrets-${name}" {
description = "Podman secrets converter service for ${name}";
wantedBy = [ "default.target" ];
unitConfig.ConditionUser = "${serviceCfg.user}";
serviceConfig = {
Type = "oneshot";
ProtectProc = "invisible";
ExecStart = "${secret-translator}/bin/secret-translator ${
lib.concatStringsSep " " serviceCfg.secrets-files
}";
Environment = "PATH=/run/wrappers/bin:${
lib.makeBinPath (with pkgs; [ shadow podman julia-lts-bin ])
}";
};
};
# Filter out services with no secrets-files
enabledServices =
lib.filterAttrs (name: serviceCfg: serviceCfg.secrets-files != [ ]) cfg;
in {
options.millironx.podman-secrets = mkOption {
type = types.attrsOf serviceSecretsType;
description = ''
Per-service Podman secrets configuration.
Each attribute creates a separate systemd user service that translates TOML secrets
files into Podman secrets.
'';
example = literalExpression ''
{
caddy = {
user = "caddy";
secrets-files = [
./../not-really-secret.toml
config.age.secrets."caddy.toml".path
];
};
authentik = {
user = "authentik";
secrets-files = [
config.age.secrets."authentik.toml".path
];
};
}
'';
default = { };
};
config = mkIf (enabledServices != { }) {
systemd.user.services = lib.mapAttrs' mkSecretsService enabledServices;
};
}

View file

@ -1,7 +1,6 @@
{ pkgs, ... }: { pkgs, ... }:
with pkgs; {
ark = callPackage ./ark.nix { }; {
jlfmt = callPackage ./jlfmt.nix { }; ark = pkgs.callPackage ./ark.nix { };
runic = callPackage ./runic.nix { }; sc4pac = pkgs.callPackage ./sc4pac.nix { };
sc4pac = callPackage ./sc4pac.nix { };
} }

View file

@ -1,12 +0,0 @@
{ pkgs, ... }:
with pkgs;
let
juliaWithPkgs = julia-bin.withPackages.override { setDefaultDepot = false; }
[ "JuliaFormatter" ];
depotWithPkgs = runCommand "getDepot" { } ''
${juliaWithPkgs}/bin/julia -e 'println(first(DEPOT_PATH))' | tee $out
'';
in writeShellScriptBin "jlfmt" ''
export JULIA_DEPOT_PATH=$(< ${depotWithPkgs})
exec ${juliaWithPkgs}/bin/julia --startup-file=no -e 'using JuliaFormatter; print(format_text(String(read(stdin))));' -- "$@"
''

View file

@ -1,12 +0,0 @@
{ pkgs, ... }:
with pkgs;
let
juliaWithRunic =
julia-bin.withPackages.override { setDefaultDepot = false; } [ "Runic" ];
depotWithRunic = runCommand "getRunicDepot" { } ''
${juliaWithRunic}/bin/julia -e 'println(first(DEPOT_PATH))' | tee $out
'';
in writeShellScriptBin "runic" ''
export JULIA_DEPOT_PATH=$(< ${depotWithRunic})
exec ${juliaWithRunic}/bin/julia --startup-file=no -e 'using Runic; exit(Runic.main(ARGS))' -- "$@"
''

View file

@ -11,7 +11,7 @@
mode: "755" mode: "755"
- name: Create Firefox DNS policy - name: Create Firefox DNS policy
ansible.builtin.template: ansible.builtin.template:
src: "policies.json" src: "{{ playbook_dir }}/../templates/policies.json"
dest: /etc/firefox/policies/policies.json dest: /etc/firefox/policies/policies.json
mode: "644" mode: "644"

View file

@ -45,8 +45,7 @@
register: home_manager_exists register: home_manager_exists
- name: Init home-manager - name: Init home-manager
ansible.builtin.shell: | ansible.builtin.shell: |
/nix/var/nix/profiles/default/bin/nix run home-manager -- switch \ /nix/var/nix/profiles/default/bin/nix run home-manager -- switch --flake git+https://code.millironx.com/millironx/nix-dotfiles#{{ ansible_user_id }}@{{ ansible_hostname }}
--flake git+https://code.millironx.com/millironx/nix-dotfiles#{{ ansible_user_id }}@{{ ansible_hostname }}
when: not home_manager_exists.stat.exists when: not home_manager_exists.stat.exists
register: home_manager_init register: home_manager_init
changed_when: home_manager_init.rc == 0 changed_when: home_manager_init.rc == 0

View file

@ -1,33 +1,78 @@
--- ---
- name: Configure dnf packages # These are repos and packages that are useless or unavailable on Asahi Linux,
# or have completely separate install procedures.
- name: Configure amd64-specific dnf packages
hosts: amd64
become: true
tasks:
- name: Install x86-specific dnf packages
ansible.builtin.dnf:
name:
- libdvdcss
- mkvtoolnix
- mpv
- protontricks
- x264
- x264-libs
state: present
- name: Install VeraCrypt
ansible.builtin.dnf:
name: https://launchpad.net/veracrypt/trunk/1.26.20/+download/veracrypt-1.26.20-Fedora-40-x86_64.rpm
state: present
disable_gpg_check: true
- name: Configure amd64-specific Flatpaks
hosts: amd64
become: false
tasks:
- name: Install x86-specific Flatpaks
community.general.flatpak:
name:
- com.bitwarden.desktop
- com.slack.Slack
- dev.deedles.Trayscale
- org.signal.Signal
state: latest
method: user
remote: flathub
- name: Configure Asahi Linux-specific dnf packages
hosts: asahi
become: true
tasks:
- name: Install aarch64-specific dnf packages
ansible.builtin.dnf:
name:
- veracrypt
- name: Configure common (all arch) dnf packages
hosts: fedora hosts: fedora
become: true become: true
tasks: tasks:
- name: Install dnf packages - name: Install common (all arch) dnf packages
ansible.builtin.dnf: ansible.builtin.dnf:
name: name:
- chromium - chromium
- firefoxpwa
- fontconfig-devel - fontconfig-devel
- freetype-devel - freetype-devel
- fribidi-devel - fribidi-devel
- ghostty
- inkscape - inkscape
- jq - jq
- kate - kate
- kdenlive - kdenlive
- kdiff3 - kdiff3
- krita - krita
- libdvdcss
- libjpeg-devel - libjpeg-devel
- libpng-devel - libpng-devel
- libtiff-devel - libtiff-devel
- libwebp-devel - libwebp-devel
- mkvtoolnix - musescore
- mpv
- nextcloud-client - nextcloud-client
- nextcloud-client-dolphin - nextcloud-client-dolphin
- obs-studio - obs-studio
- onedrive - onedrive
- protontricks
- qownnotes - qownnotes
- qt - qt
- rssguard - rssguard
@ -37,8 +82,6 @@
- thunderbird - thunderbird
- vlc - vlc
- vorta - vorta
- x264
- x264-libs
- yakuake - yakuake
- zed - zed
- zsh - zsh
@ -49,11 +92,6 @@
name: https://downloads.sourceforge.net/project/mscorefonts2/rpms/msttcore-fonts-installer-2.6-1.noarch.rpm name: https://downloads.sourceforge.net/project/mscorefonts2/rpms/msttcore-fonts-installer-2.6-1.noarch.rpm
state: present state: present
disable_gpg_check: true disable_gpg_check: true
- name: Install VeraCrypt
ansible.builtin.dnf:
name: https://launchpad.net/veracrypt/trunk/1.26.20/+download/veracrypt-1.26.20-Fedora-40-x86_64.rpm
state: present
disable_gpg_check: true
- name: Install rig (R installation manager) - name: Install rig (R installation manager)
ansible.builtin.dnf: ansible.builtin.dnf:
name: https://github.com/r-lib/rig/releases/download/latest/r-rig-latest-1.{{ ansible_architecture }}.rpm name: https://github.com/r-lib/rig/releases/download/latest/r-rig-latest-1.{{ ansible_architecture }}.rpm
@ -71,24 +109,21 @@
name: "*" name: "*"
state: latest # noqa: package-latest state: latest # noqa: package-latest
- name: Configure Flatpaks - name: Configure common (all arch) Flatpaks
hosts: fedora hosts: fedora
become: false become: false
tasks: tasks:
- name: Install Flatpaks - name: Install common (all arch) Flatpaks
community.general.flatpak: community.general.flatpak:
name: name:
- com.bitwarden.desktop
- com.github.tchx84.Flatseal - com.github.tchx84.Flatseal
- com.logseq.Logseq - com.logseq.Logseq
- com.slack.Slack
- dev.deedles.Trayscale
- io.freetubeapp.FreeTube - io.freetubeapp.FreeTube
- io.github.alainm23.planify - io.github.alainm23.planify
- io.github.dweymouth.supersonic - io.github.dweymouth.supersonic
- io.openrct2.OpenRCT2 - io.openrct2.OpenRCT2
- org.signal.Signal
- org.zulip.Zulip - org.zulip.Zulip
- net.ankiweb.Anki
state: latest state: latest
method: user method: user
remote: flathub remote: flathub

View file

@ -1,6 +1,6 @@
--- ---
- name: Configure dnf package repositories - name: Configure amd64-specific package repositories
hosts: fedora hosts: amd64
become: true become: true
tasks: tasks:
- name: Install RPM Fusion free repository - name: Install RPM Fusion free repository
@ -20,6 +20,31 @@
- name: Install Zotero COPR repository - name: Install Zotero COPR repository
community.general.copr: community.general.copr:
name: "mozes/zotero7" name: "mozes/zotero7"
# Asahi Linux comes with its own strange version of RPMFusion installed, so
# RPMFusion is installed only on amd64 systems. In addition, VeraCrypt and
# Zotero *are* available via COPR, but from different repos than their amd64
# counterparts.
# Also, Asahi has its own version string, so we have to manually specify the
# chroot for COPR repos added via Ansible. This is handled automatically when
# using `dnf copr enable ...`, but not via Ansible.
- name: Configure Asahi Linux-specific package repositories
hosts: asahi
become: true
tasks:
- name: Install Zotero COPR repository
community.general.copr:
name: "isaksamsten/Zotero"
chroot: "fedora-{{ ansible_distribution_major_version }}-aarch64"
- name: Install VeraCrypt COPR repository
community.general.copr:
name: "architektapx/veracrypt"
chroot: "fedora-{{ ansible_distribution_major_version }}-aarch64"
- name: Configure common (all arch) package repositories
hosts: fedora
become: true
tasks:
- name: Install Tailscale repo - name: Install Tailscale repo
ansible.builtin.yum_repository: ansible.builtin.yum_repository:
name: tailscale-stable name: tailscale-stable
@ -28,6 +53,14 @@
enabled: true enabled: true
gpgcheck: true gpgcheck: true
gpgkey: https://pkgs.tailscale.com/stable/fedora/repo.gpg gpgkey: https://pkgs.tailscale.com/stable/fedora/repo.gpg
- name: Install FirefoxPWA repository
ansible.builtin.yum_repository:
name: firefoxpwa
description: FirefoxPWA repository
baseurl: https://packagecloud.io/filips/FirefoxPWA/fedora/$releasever/$basearch
gpgcheck: true
gpgkey: https://packagecloud.io/filips/FirefoxPWA/gpgkey
enabled: true
# Note that I still have to specify the chroot for COPR repos b/c of Asahi # Note that I still have to specify the chroot for COPR repos b/c of Asahi
- name: Install RStudio copr repository - name: Install RStudio copr repository
community.general.copr: community.general.copr:
@ -77,7 +110,7 @@
register: terra_priority register: terra_priority
changed_when: terra_priority.rc != 0 changed_when: terra_priority.rc != 0
- name: Configure Flatpack remotes - name: Configure Flathub remote
hosts: fedora hosts: fedora
become: false become: false
tasks: tasks:

View file

@ -1,4 +1,4 @@
{ pkgs, firefox-addons, buildFirefoxXpiAddon, lib, ... }: { { firefox-addons, buildFirefoxXpiAddon, lib, ... }: {
programs.firefox = { programs.firefox = {
enable = true; enable = true;
package = package =
@ -31,49 +31,49 @@
}; };
}; };
containersForce = true; containersForce = true;
extensions.packages = with firefox-addons; extensions.packages = with firefox-addons; [
[ bitwarden
bitwarden multi-account-containers
multi-account-containers floccus
libredirect libredirect
old-reddit-redirect old-reddit-redirect
ublock-origin plasma-integration
user-agent-string-switcher pwas-for-firefox
zotero-connector ublock-origin
(buildFirefoxXpiAddon rec { user-agent-string-switcher
pname = "always_in_container"; web-archives
version = "1.0.7"; zotero-connector
addonId = "{a1e9543e-5f73-4763-b376-04e53fd12cbd}"; (buildFirefoxXpiAddon rec {
url = pname = "always_in_container";
"https://addons.mozilla.org/firefox/downloads/file/4032840/${pname}-${version}.xpi"; version = "1.0.7";
sha256 = "sha256-bLxjL2P6Sd06q98MSHYRTNigtcjGwn/C2r4ANWCqKrw="; addonId = "{a1e9543e-5f73-4763-b376-04e53fd12cbd}";
meta = with lib; { url =
homepage = "https://github.com/tiansh/always-in-container"; "https://addons.mozilla.org/firefox/downloads/file/4032840/${pname}-${version}.xpi";
description = sha256 = "sha256-bLxjL2P6Sd06q98MSHYRTNigtcjGwn/C2r4ANWCqKrw=";
"Chose a container every time you try to open a page out of a container"; meta = with lib; {
license = licenses.mpl20; homepage = "https://github.com/tiansh/always-in-container";
platforms = platforms.all; description =
}; "Chose a container every time you try to open a page out of a container";
}) license = licenses.mpl20;
(buildFirefoxXpiAddon rec { platforms = platforms.all;
pname = "open_with"; };
version = "7.2.6"; })
addonId = "openwith@darktrojan.net"; (buildFirefoxXpiAddon rec {
url = pname = "open_with";
"https://addons.mozilla.org/firefox/downloads/file/3831723/${pname}-${version}.xpi"; version = "7.2.6";
sha256 = "sha256-f9eGhLxg4UyVn4o5e4DRkraLWzj11SGto/GOwsJa9kg="; addonId = "openwith@darktrojan.net";
meta = with lib; { url =
homepage = "https://darktrojan.github.io/openwith/"; "https://addons.mozilla.org/firefox/downloads/file/3831723/${pname}-${version}.xpi";
description = sha256 = "sha256-f9eGhLxg4UyVn4o5e4DRkraLWzj11SGto/GOwsJa9kg=";
"Quickly test out your web pages in Chrome, Edge, Safari, or Opera. Open With opens the current page in your other browsers with just two clicks."; meta = with lib; {
license = licenses.mpl20; homepage = "https://darktrojan.github.io/openwith/";
platforms = platforms.all; description =
}; "Quickly test out your web pages in Chrome, Edge, Safari, or Opera. Open With opens the current page in your other browsers with just two clicks.";
}) license = licenses.mpl20;
] ++ (if pkgs.stdenv.hostPlatform.isDarwin then platforms = platforms.all;
[ ] };
else })
[ plasma-integration ]); ];
search = { search = {
default = "Kagi"; default = "Kagi";
privateDefault = "Milliron X Search"; privateDefault = "Milliron X Search";
@ -246,6 +246,7 @@
"floccus_handmadeideas_org-browser-action" "floccus_handmadeideas_org-browser-action"
"7esoorv3_alefvanoon_anonaddy_me-browser-action" "7esoorv3_alefvanoon_anonaddy_me-browser-action"
"plasma-browser-integration_kde_org-browser-action" "plasma-browser-integration_kde_org-browser-action"
"firefoxpwa_filips_si-browser-action"
"_d07ccf11-c0cd-4938-a265-2a4d6ad01189_-browser-action" # Web Archives "_d07ccf11-c0cd-4938-a265-2a4d6ad01189_-browser-action" # Web Archives
"openwith_darktrojan_net-browser-action" "openwith_darktrojan_net-browser-action"
"zotero_chnm_gmu_edu-browser-action" "zotero_chnm_gmu_edu-browser-action"
@ -281,6 +282,7 @@
"floccus_handmadeideas_org-browser-action" "floccus_handmadeideas_org-browser-action"
"7esoorv3_alefvanoon_anonaddy_me-browser-action" "7esoorv3_alefvanoon_anonaddy_me-browser-action"
"plasma-browser-integration_kde_org-browser-action" "plasma-browser-integration_kde_org-browser-action"
"firefoxpwa_filips_si-browser-action"
"ublock0_raymondhill_net-browser-action" "ublock0_raymondhill_net-browser-action"
"_d07ccf11-c0cd-4938-a265-2a4d6ad01189_-browser-action" "_d07ccf11-c0cd-4938-a265-2a4d6ad01189_-browser-action"
"zotero_chnm_gmu_edu-browser-action" "zotero_chnm_gmu_edu-browser-action"

19
programs/ghostty.nix Normal file
View file

@ -0,0 +1,19 @@
{ pkgs, ... }: {
programs.ghostty =
let modifierKey = if pkgs.stdenv.isDarwin then "cmd" else "ctrl";
in {
enable = true;
package = null;
enableBashIntegration = true;
enableZshIntegration = true;
settings = {
quick-terminal-position = "top";
quick-terminal-screen = "main";
quick-terminal-autohide = true;
quick-terminal-size = "50%,50%";
keybind = "global:${modifierKey}+backquote=toggle_quick_terminal";
macos-hidden = "always";
linux-cgroup = "always";
};
};
}

View file

@ -1,11 +1,9 @@
{ ... }: { { ... }: {
programs.git = { programs.git = {
enable = true; enable = true;
settings = { userName = "Thomas A. Christensen II";
user = { userEmail = "25492070+MillironX@users.noreply.github.com";
name = "Thomas A. Christensen II"; extraConfig = {
email = "25492070+MillironX@users.noreply.github.com";
};
core = { editor = "nvim"; }; core = { editor = "nvim"; };
credential = { helper = "store"; }; credential = { helper = "store"; };
color = { ui = "auto"; }; color = { ui = "auto"; };
@ -42,11 +40,6 @@
}; };
merge = { conflictstyle = "zdiff3"; }; merge = { conflictstyle = "zdiff3"; };
pull = { rebase = true; }; pull = { rebase = true; };
"url \"ssh://git@github.com/\"".insteadOf = "https://github.com/";
"url \"ssh://git@gitlab.com/\"".insteadOf = "https://gitlab.com/";
"url \"ssh://git@codeberg.com/\"".insteadOf = "https://codeberg.com/";
"url \"ssh://git@code.millironx.com/\"".insteadOf =
"https://code.millironx.com/";
}; };
}; };
} }

View file

@ -1,23 +0,0 @@
{ ... }: {
programs.konsole = {
enable = true;
defaultProfile = "millironx";
profiles.millironx = {
colorScheme = "Breeze";
font = {
name = "MesloLGS NF";
size = 10;
};
extraConfig = {
"Cursor Options".CursorShape = 1;
General.RemoteTabTitleFormat = "[SSH] %H";
"Interaction Options" = {
CopyTextAsHTML = false;
MouseWheelZoomEnabled = false;
UnderlineFilesEnabled = true;
};
"Terminal Features".BlinkingCursorEnabled = true;
};
};
};
}

View file

@ -1,177 +1,7 @@
{ config, ... }: { { config, ... }: {
imports = [ ./konsole.nix ./yakuake.nix ];
programs.plasma = { programs.plasma = {
enable = true; enable = true;
overrideConfig = true;
shortcuts = { yakuake.toggle-window-state = "Ctrl+`"; };
configFile = {
dolphinrc = {
DetailsMode.PreviewSize = 16;
General = {
BrowseThroughArchives = true;
EditableUrl = true;
GlobalViewProps = false;
ShowFullPath = true;
ShowStatusBar = "FullWidth";
ShowZoomSlider = true;
};
"KFileDialog Settings" = {
"Places Icons Auto-resize" = false;
"Places Icons Static Size" = 22;
};
PreviewSettings.Plugins =
"audiothumbnail,avif,blenderthumbnail,comicbookthumbnail,cursorthumbnail,djvuthumbnail,ebookthumbnail,exrthumbnail,directorythumbnail,fontthumbnail,imagethumbnail,jpegthumbnail,jxl,kraorathumbnail,windowsexethumbnail,windowsimagethumbnail,mobithumbnail,opendocumentthumbnail,gsthumbnail,rawthumbnail,svgthumbnail,gdk-pixbuf-thumbnailer,ffmpegthumbs,gsf-office";
};
};
input.mice = [{
enable = true;
name = "Logitech M705";
vendorId = "046d";
productId = "406d";
naturalScroll = true;
}];
kwin = {
cornerBarrier = false;
titlebarButtons = {
left = [ "close" "minimize" "maximize" ];
right = [ "help" ];
};
virtualDesktops = {
number = 2;
rows = 1;
};
};
panels = [
### Screen 0 panels ###
##### Top: Fedora menu | App switcher | Menu bar | CPU monitor | Memory monitor | Network monitor | System tray | clock #####
{
location = "top";
floating = true;
height = 27;
lengthMode = "fill";
opacity = "adaptive";
hiding = "normalpanel";
screen = 0;
widgets = [
{ kickoff = { icon = "fedora-logo-icon"; }; }
"org.kde.plasma.marginsseparator"
"org.kde.plasma.windowlist"
"org.kde.plasma.appmenu"
"org.kde.plasma.panelspacer"
{
name = "org.kde.plasma.systemmonitor.cpu";
config = {
Appearance.chartFace = "org.kde.ksysguard.barchart";
Sensors.highPrioritySensorIds = "[${
builtins.concatStringsSep "," (builtins.genList
(i: ''"cpu/cpu${builtins.toString i}/usage"'') 12)
}]";
};
}
{
name = "org.kde.plasma.systemmonitor.memory";
config.Appearance.chartFace = "org.kde.ksysguard.horizontalbars";
}
{
name = "org.kde.plasma.systemmonitor.net";
config = {
Appearance.chartFace = "org.kde.ksysguard.horizontalbars";
Sensors.highPrioritySensorIds =
''[ "network/all/download","network/all/upload" ]'';
# These are the values needed to make the network indicator
# actually useful, but it appears that plasma-manager doesn't
# support nesting this deep yet. Disable for now.
# "org.kde.ksysguard.horizontalbars".General = {
# rangeAuto = false;
# rangeFromMultiplier = 1048576;
# rangeFromUnit = 202;
# rangeToMultiplier = 1048576;
# rangeToUnit = 202;
# };
};
}
{ systemTray = { }; }
{ digitalClock = { }; }
];
}
##### Bottom: Virtual desktop pager | Full-name taskbar w/ pins | Downloads folder | Trash folder
{
location = "bottom";
floating = true;
height = 44;
lengthMode = "fill";
opacity = "adaptive";
hiding = "normalpanel";
screen = 0;
widgets = [
"org.kde.plasma.pager"
{
iconTasks = {
iconsOnly = false;
behavior.showTasks.onlyInCurrentScreen = true;
launchers = [
"applications:systemsettings.desktop"
"applications:org.kde.discover.desktop"
"preferred://filemanager"
"preferred://browser"
"applications:net.thunderbird.Thunderbird.desktop"
"applications:io.github.alainm23.planify.desktop"
"applications:dev.zed.Zed.desktop"
"applications:com.logseq.Logseq.desktop"
"applications:net.lutris.Lutris.desktop"
];
};
}
"org.kde.plasma.panelspacer"
"org.kde.plasma.marginsseparator"
{
name = "org.kde.plasma.folder";
config = {
General.url = "file://${config.home.homeDirectory}/Downloads";
};
}
"org.kde.plasma.trash"
];
}
### Screen 1 panels ###
##### Top: App switcher | Menu bar #####
{
location = "top";
floating = true;
height = 27;
lengthMode = "fill";
opacity = "adaptive";
hiding = "normalpanel";
screen = 1;
widgets = [ "org.kde.plasma.windowlist" "org.kde.plasma.appmenu" ];
}
##### Bottom: Virtual desktop pager | Full-name taskbar w/o pins #####
{
location = "bottom";
floating = true;
height = 44;
lengthMode = "fill";
opacity = "adaptive";
hiding = "normalpanel";
screen = 1;
widgets = [
"org.kde.plasma.pager"
{
iconTasks = {
iconsOnly = false;
behavior.showTasks.onlyInCurrentScreen = true;
launchers = [ ];
};
}
];
}
];
powerdevil.AC.autoSuspend.action = "nothing";
workspace = { workspace = {
lookAndFeel = "org.kde.breezedark.desktop";
wallpaperFillMode = "preserveAspectCrop"; wallpaperFillMode = "preserveAspectCrop";
wallpaperSlideShow = { wallpaperSlideShow = {
interval = 86400; interval = 86400;

View file

@ -1,7 +1,7 @@
{ pkgs, ... }: { pkgs, ... }:
let let
conda_init = shell: '' conda_init = shell: ''
eval "$(${pkgs.mamba-cpp}/bin/mamba shell hook --shell ${shell})" eval "$(${pkgs.micromamba}/bin/micromamba shell hook --shell ${shell})"
''; '';
nd_bash_function = '' nd_bash_function = ''
@ -10,53 +10,11 @@ let
} }
''; '';
sourceCodeDirectory =
if pkgs.stdenv.hostPlatform.isDarwin then "~/Developer" else "~/src";
clone_bash_function = ''
unalias gc
function gc() {
REPO_ID="$(echo "''${1}" | \
awk '{
sub(/^git@/, "");
sub(/^https:\/\//, "");
sub (/:/, "/");
sub(/\.git$/, "");
print
}')"
git clone "https://''${REPO_ID}.git" ${sourceCodeDirectory}/''${REPO_ID}
cd ${sourceCodeDirectory}/''${REPO_ID}
}
'';
repo_init_function = ''
function rinit() {
REPO_ID="$(echo "''${1}" | \
awk '{
sub(/^git@/, "");
sub(/^https:\/\//, "");
sub (/:/, "/");
sub(/\.git$/, "");
print
}')"
mkdir -p ${sourceCodeDirectory}/''${REPO_ID}
cd ${sourceCodeDirectory}/''${REPO_ID}
git init
git remote add origin git@''${REPO_ID/\//:}.git
}
'';
shell_functions = shell:
(conda_init shell) + nd_bash_function + clone_bash_function
+ repo_init_function;
in { in {
programs = { programs = {
bash = { bash = {
enable = true; enable = true;
initExtra = shell_functions "bash" + '' initExtra = conda_init "bash" + nd_bash_function + ''
export PS1="[\[\e[32m\]\u\[\e[m\]@\[\e[33m\]\h\[\e[m\] \[\e[34m\]\W\[\e[m\]] \\$ " export PS1="[\[\e[32m\]\u\[\e[m\]@\[\e[33m\]\h\[\e[m\] \[\e[34m\]\W\[\e[m\]] \\$ "
''; '';
}; };
@ -79,7 +37,7 @@ in {
"zsh-users/zsh-completions" "zsh-users/zsh-completions"
]; ];
}; };
initContent = shell_functions "zsh"; initContent = conda_init "zsh" + nd_bash_function;
}; };
}; };
} }

View file

@ -25,10 +25,6 @@
bundleIdentifier = "org.mozilla.thunderbird"; bundleIdentifier = "org.mozilla.thunderbird";
action = "launchOrActivateApp"; action = "launchOrActivateApp";
} }
{
bundleIdentifier = "com.microsoft.Outlook";
action = "launchOrActivateApp";
}
{ {
bundleIdentifier = "dev.zed.Zed"; bundleIdentifier = "dev.zed.Zed";
action = "launchOrActivateApp"; action = "launchOrActivateApp";
@ -38,13 +34,11 @@
action = "launchOrActivateApp"; action = "launchOrActivateApp";
} }
{ {
# Instinct dashboard
bundleIdentifier = bundleIdentifier =
"com.apple.Safari.WebApp.2F51A6D0-087A-438F-92D3-A73FE09CB4CC"; "com.apple.Safari.WebApp.2F51A6D0-087A-438F-92D3-A73FE09CB4CC";
action = "launchOrActivateApp"; action = "launchOrActivateApp";
} }
{ {
# Carestream
bundleIdentifier = bundleIdentifier =
"com.apple.Safari.WebApp.5EC6478E-03A6-4147-8A4D-6EF3DE3F06D3"; "com.apple.Safari.WebApp.5EC6478E-03A6-4147-8A4D-6EF3DE3F06D3";
action = "launchOrActivateApp"; action = "launchOrActivateApp";

View file

@ -1,19 +0,0 @@
# Note: this file uses the lower-level `programs.plasma.configFile` syntax
# since plasma-manager does not yet support a high-level module for Yakuake
{ ... }: {
programs.plasma.configFile.yakuakerc = {
"Desktop Entry".DefaultProfile = "millironx.profile";
Shortcuts = {
next-session = "Shift+Right; Ctrl+Shift+Tab";
previous-terminal = "none";
};
Window = {
DynamicTabTitles = true;
Height = 60;
Screen = 1;
ShowSystrayIcon = false;
Width = 60;
};
Dialogs.FirstRun = false;
};
}

View file

@ -1,4 +1,4 @@
{ pkgs, ... }: { { ... }: {
programs.zed-editor = { programs.zed-editor = {
enable = true; enable = true;
extensions = [ extensions = [
@ -23,27 +23,27 @@
use_modifier_to_send = true; use_modifier_to_send = true;
default_model = { default_model = {
provider = "zed.dev"; provider = "zed.dev";
model = "claude-sonnet-4-5"; model = "claude-3-7-sonnet";
}; };
default_profile = "minimal"; default_profile = "minimal";
}; };
buffer_font_family = "FiraCode Nerd Font"; buffer_font_family = "FiraCode Nerd Font";
buffer_font_size = 11; buffer_font_size = 11;
features = { edit_prediction_provider = "zed"; }; features = { edit_prediction_provider = "zed"; };
git_hosting_providers = [
{
provider = "forgejo";
base_url = "https://code.millironx.com";
name = "Milliron X Code";
}
{
provider = "forgejo";
base_url = "https://codeberg.org";
name = "Codeberg";
}
];
languages = { languages = {
Julia = { formatter = { external = { command = "jlfmt"; }; }; }; Julia = {
formatter = {
external = {
command = "julia";
arguments = [
"--project=@JuliaFormatter"
"--startup-file=no"
"-e"
"using JuliaFormatter; print(format_text(String(read(stdin))));"
];
};
};
};
LaTeX = { LaTeX = {
formatter = { formatter = {
external = { external = {
@ -52,30 +52,13 @@
}; };
}; };
}; };
Nix = {
formatter.external.command = "${pkgs.nixfmt-classic}/bin/nixfmt";
};
}; };
lsp = { lsp = {
nil = { settings.nix.flake.autoArchive = true; }; nil = {
texlab = { initialization_options.formatting.command = [ "nixfmt" ];
settings = { settings.nix.flake.autoArchive = true;
build = {
onSave = false;
forwardSearchAfter = true;
};
forwardSearch = if pkgs.stdenv.hostPlatform.isDarwin then {
executable =
"/Applications/Skim.app/Contents/SharedSupport/displayline";
args = [ "-r" "-g" "%l" "%p" "%f" ];
} else {
executable = "/usr/bin/okular";
args = [ "--unique" "file:%p#src:%l%f" ];
};
};
}; };
tinymist = { tinymist = {
initialization_options = { preview.background.enabled = true; };
settings = { settings = {
exportPdf = "onSave"; exportPdf = "onSave";
outputPath = "$root/$name"; outputPath = "$root/$name";
@ -98,28 +81,5 @@
ui_font_size = 16; ui_font_size = 16;
wrap_guides = [ 80 92 120 ]; wrap_guides = [ 80 92 120 ];
}; };
userTasks = [
{
label = "latexmk (project)";
command = "latexmk";
args = [ "-synctex=1" "-pdf" "-recorder" ];
cwd = "$ZED_DIRNAME";
tags = [ "latex-build" ];
}
{
label = "Open Typst preview";
command = "${
if pkgs.stdenv.hostPlatform.isDarwin then "open" else "xdg-open"
} http://127.0.0.1:23635/";
use_new_terminal = true;
allow_concurrent_runs = false;
reveal = "never";
reveal_target = "dock";
hide = "always";
shell = "system";
show_summary = true;
show_command = true;
}
];
}; };
} }

View file

@ -6,36 +6,25 @@ let
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIxTfeg+GZsfmG8TuEV1xW1gXknAIKzZ3UjZ3guRY+EW root@nixos"; "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIxTfeg+GZsfmG8TuEV1xW1gXknAIKzZ3UjZ3guRY+EW root@nixos";
bosephus-millironx = bosephus-millironx =
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKaDPqRJHoqgY2pseh/mnhjaGWXprHk2s5I52LhHpHcF millironx@bosephus"; "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKaDPqRJHoqgY2pseh/mnhjaGWXprHk2s5I52LhHpHcF millironx@bosephus";
corianne-host = odyssey-millironx =
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHKKkucebeb1GcerOZAAs5GQsgTS8kXw5W41b9Fy9+hp root@corianne.local"; "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIN9Aj7BtQp1Roa0tgopDrUo7g2am5WJ43lO1d1fDUz45 millironx@odyssey";
corianne-millironx = corianne-millironx =
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOgL2lO9RJBdQYANoxGyWXcNKi5/NZkRHHo/rNqaYMc/ millironx@corianne"; "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOgL2lO9RJBdQYANoxGyWXcNKi5/NZkRHHo/rNqaYMc/ millironx@corianne";
mcentire-host = harmony-millironx =
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINT51tQgsKzTIQc9WSQj01h/gPRvAD3k9jRhXppY7xmd root@nixos"; "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFBYxsCkw+ObDzIvU8z/rSlYcQx0JIt1bCVxKcDxeNNZ millironx@harmony";
mcentire-millironx =
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOdC6eNx2nBi3PWK/n4GJMbVf+NlQJv13aUqxse/h1kL millironx@mcentire";
odyssey-millironx =
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKM5Q2zl3b91j+foqcVeQT+wb5DFEp+MbgotTTaKqZZi millironx@odyssey";
system-administrators = [ system-administrators = [
anderson-millironx anderson-millironx
bosephus-millironx bosephus-millironx
corianne-millironx
mcentire-millironx
odyssey-millironx odyssey-millironx
corianne-millironx
harmony-millironx
]; ];
in { in {
"secrets/ansible-vault-password.age".publicKeys = system-administrators;
"secrets/authentik.toml.age".publicKeys = system-administrators
++ [ mcentire-host ];
"secrets/borgmatic-passphrase.age".publicKeys = system-administrators
++ [ mcentire-host ];
"secrets/borgmatic-ssh-config.age".publicKeys = system-administrators
++ [ mcentire-host ];
"secrets/darwin-policies-json.age".publicKeys = system-administrators
++ [ corianne-host ];
"secrets/freshrss.toml.age".publicKeys = system-administrators
++ [ mcentire-host ];
"secrets/network-information.age".publicKeys = system-administrators "secrets/network-information.age".publicKeys = system-administrators
++ [ bosephus-host ]; ++ [ bosephus-host ];
"secrets/pihole.age".publicKeys = system-administrators ++ [ bosephus-host ];
"secrets/ansible-vault-password.age".publicKeys = system-administrators;
"secrets/darwin-policies-json.age".publicKeys = system-administrators;
} }

View file

@ -1,13 +1,13 @@
age-encryption.org/v1 age-encryption.org/v1
-> ssh-ed25519 il3lzQ SoqTRahd/xUVe/pDmQI3jT5X0lTxOwhy8hct20fwil4 -> ssh-ed25519 il3lzQ Ni2CHjeijXHfF62cUqVTm8MAOn6rRg8UrhqN6xvdkyk
UuYpQa5GpDALKQEMbSLnH2rp3kgPL+4zJZbJirkB5Q0 DsT0Ysx88FlCLeRzoOGctX7KqatX9/UCr5WdtdLJAf4
-> ssh-ed25519 1g/xww gYtrdwJlp1pxcZ7l+RvawRScFOh/ami3yJobbKPyLiM -> ssh-ed25519 1g/xww jRn91F29sISMyi41anAlzVCzt1t1DnUqxtryqkTQPlM
oQ8dwIUIakV8WVvuknn87BYcmEBBT+UI3BxImd71/jE cysgZLQR0YhiJYXBl59DjKbm+N8FnjA46wkQtnAzBFA
-> ssh-ed25519 dbKeHw KVdpRmBWRYd5NC3UK3e8Em3XefnVxxc9KfXzbksvTC0 -> ssh-ed25519 +kBihw t6wlSnDKGgSzGhNJnryXVbDR40DATaV3fHovtI/u7zo
fwyK7ORnCadX8Czs0WKW3ZDa1jeu7Wjba7Vm9nKqCBs zOyCZtzfLKeer9K6SMpfTxn6El4HB7gQFQqLOxIYB5U
-> ssh-ed25519 3qPtug nEHt7lMjUrcehOGjtznFd+u60OdfG1dPJr/+aFP4OXY -> ssh-ed25519 dbKeHw cn+8WTwis58bYm2pfEra6LeLvzEZ8GhZrOEeN+kkhCM
DRtfbYQZJhMIfj1yHgpTdSU15z0Ld5/2wl0ATPN0W6w fnlUAj8JtG8+r7Cj8xYUgF+JM6Pwqawn4sGI1LOeN78
-> ssh-ed25519 FRQvIA a4zYtpYFVGSum3wD850lM/wEcokckt4mlDRsrb6QrHY -> ssh-ed25519 Svnssw zmDBR8TdRZ9NzNhwPYRN6c8naTxAkULyUZpKgk7Gshk
QF75fGiiKZc1oHSOdy+QvPnQ1hhaLRebaE7i0keZxHw 0XCwpegEIlGXhnzLLUtmciKQiYiZRgnSOSvCcYeXXk8
--- 8Y5Q9SlGWEzp9jeVcoHFrv3PR+eDyhOPFXIFRxZ1Koo --- D/lZ36n5sVste2NWfdOx8/klPh0CTmMjVQN74KIqDRY
ÜJáżŢóóDlv\é÷÷lĹu$­0 ëë±{<7B>Ďri·śP7ĎĄPÚ锓…Äkr(ÇPˇĹHWË]r ĚĆŮr¸ ]%得ヌC}<7D>鶲ネモ"vホネ#<23>アェ釋「tュ、ワ_Q;^*!サ+<+ア瑁詞ネdBラ/Kメ<4B>`

Binary file not shown.

Binary file not shown.

View file

@ -1,16 +0,0 @@
age-encryption.org/v1
-> ssh-ed25519 il3lzQ 6815+wa1JjdVUu8U8vaRah8kVsjR3Fsot99HKWl4SSA
hPYVZuN1yTKQYFIQvIRdmr400aijXleA22Bxh1nXKdc
-> ssh-ed25519 1g/xww BPxSszlrjPTVJC+YOvo1N6ICUUdL7WKZX3DeambLjBY
JkJQFPGtxDtqeGj8Z1YtVq/pm6hgYKwzMW9MbtkvOwY
-> ssh-ed25519 dbKeHw PU5cZrKCkAvUjI/opkboDQKSWJ8osAOQiI62f9lbfRY
ef1z6ZcBASkhC4fdcN+RRE7q28rB/DN8CVdoNVkC81I
-> ssh-ed25519 3qPtug RWCMZiLQlS+bOOIBRGmoECOfqEI2uoaWCq/4ZuI86B4
VOaLaH1VYnGoQzjb8seU6wVviB94W3qCdYW9Wf4PGIc
-> ssh-ed25519 FRQvIA LCDwNMNVtTcI3GHPR2mcgtR16yAYYwLplI2nKUwNxDQ
vwXBIDlK7FBOsKt48Zv8mp0WFA/TKH2UJxtUxZoxzMI
-> ssh-ed25519 +C0WRg a/R13LfNMjVjJxt2TtTHaqZOhsYJLY+HUGMHEDnkZWo
T2EfeE/a4W4bFvauynfh+4aJL1jhCpO8iBG2aTmKtyM
--- o7tyupfhbxh3dwSrBLsYX0V61va0USlqxheNsdBjVuQ
A ¤q,:_™aɪ”)Àžj“¹<E2809C>1ß2 #¼¾²t,G®£*«
X¿TÛ³(óŽòœ5øtúñ6‡¡ÕB“çš©èã8à0Í Ú*Ïâìèi׆Ðì^ÇÓߨ@ÔaóB@̃Ž+oF¯…<C2AF>ü÷w‰÷²¼åPciˆ2Ü7Y¨SXd²“<C2B2> ÚØg±É̲ñJ

Binary file not shown.

Binary file not shown.

Binary file not shown.

15
secrets/pihole.age Normal file
View file

@ -0,0 +1,15 @@
age-encryption.org/v1
-> ssh-ed25519 il3lzQ Q+/uqZhUWs5pb5T1ocD+/qTSo4DJbd/W1exruQ34zAE
8HFRvEblGVrkoVaqAl/Af6wrDn6A+3unZIMBipEkwgA
-> ssh-ed25519 1g/xww PqXxTvLaF6ZlcVov81VrVH130jFh2iGmHPRtBYV4ME4
1VBknQzaNZyoz2wvgKX+IZGaOEnJ1xGvxPYxq10ar/U
-> ssh-ed25519 +kBihw QXNxY9OQeIM98OqmHoa/S2kMZqSX+ndgxGyCJpHJ+gg
b3DmfUswyPQ09sp57v3QMNEF/Ka3w9Qj2s1kGUSinmQ
-> ssh-ed25519 dbKeHw 5GzjKgjUX5e6Net7voWBykC17zRcdSFDFbDsSwp5FAU
GwTEg3YR9HdcQHPg+XjP2Lg1BpcWA4VunbZSBdxVaYU
-> ssh-ed25519 Svnssw imRjD5CJu/jOac3t/APHbYBnsyJVQdebR6K52A6GdwM
n+Q9kEEkYRBuEzWlSwbjJNsjF8uKloeUEWYxHa29B4U
-> ssh-ed25519 jb0ALQ 4qbGIofHcyhJVfL24peGqqzg0tFdxbWBHJFenwehIAI
Ta3ye4quyHvvE+2CGZwYvQMwWfdrLIdqADLvJYhllPY
--- 3hbht7PYqFafVmcQWQwv3q2gUXM8HXajtmAaMnrh59s
 +óX9ð¾84R2ƒª,òôˆ±¨(#42ì#*, bùôŒç¦½Hã_z…œ…¨Í }ß… ®ëxñ7Aœ!<21>)ê)vÊž¨¨•W-÷¿eX-G‡<´¸@~â¢èEÜk¬?kGlQK¬4c&*JöÂå9V_Ä0Õö µzÓ+ɰ¯CÞ ÑžÀQ°ò«ÃGô§XÁ˜k¡g*£æð -Àjƒö¼l½ú3¿‰1ÏJš´üM·OE†[=S

View file

@ -1,8 +1,10 @@
$ANSIBLE_VAULT;1.1;AES256 $ANSIBLE_VAULT;1.1;AES256
33613635643765623937663135313833396162343134383466343966333964386364356134663264 65366137313461383534313965646333656565353061336361363661613033393264353661346337
3137633339396462633431316634623834646437646162360a626564313831373761636161656232 3838653162383134393463323631613439373663396363380a633339396236363962313333343465
35316566336232666336646231356665366633303530623961666465366163306166623336656364 31623961393532666136616438633734366261353866383264323730383432326635626637343739
3835353035333031620a633332376237336530343134623832363534383761616564616138363766 3235313062623637380a386235316437396534353261383832643165316565386263396664363962
30306361383462353361636161636335313461313835663362393839623735313738316465656537 62393364333335373631356161373263313930343565626433383539373030363662353630633933
66396635323432376530346532353238346139376261366237343763373535623364633731323830 63336333613965653635313637336437653139616564313861336332323739653865383531356233
333730373965613131336166626230333263 31373530343766343131346663656566363038643230343462336332323135323337353539303763
33366638393064323431323636346161343936643062323861313766613264336465326132333631
33306666383561653965303539313366653030663330393363363565333439383133

6
secrets_harmony.enc Normal file
View file

@ -0,0 +1,6 @@
$ANSIBLE_VAULT;1.1;AES256
38383539613238613864336630316433666436623334313334393762396536663530336264306661
3338616565316138616666343862366638643134343931320a633366363539326461346636373738
66393138653463663536313065623332383166386332303564323939336630333163623637386434
6538393966633731660a616437356233643234363562366433663437383439326161353330356331
63346432663036353332303266343361346266396437396131376531303265356233

View file

@ -1,9 +1,6 @@
$ANSIBLE_VAULT;1.1;AES256 $ANSIBLE_VAULT;1.1;AES256
61363033383536303833366237323662663236313163663033306138383162383062643830616466 30343638643335363463653231623566623961613534323261393639623865633964653634333562
6531636430613462646161343939343363663533373737340a613433363666353432383463356439 3838613035393661656362383736313561366466396439390a383162366362643364636335613664
33656266633131336565613433653062656563656637656464346232656238646339303961373265 39646137666437353762363764373562393736626530333336626261366232383063633732623238
6639643637303433380a393163366331373964353261383662656664643031626432366231346332 6531633638366335640a363461383535646663316533386137323966326237373836363561323462
34303964346137616233343930333331306363326332383465653163386539306430303965316437 66646635383137333834363165666365366235333734646364616637383363666239
30343333373565623431653436653832356366343937653136346535316166383262623730343831
62376532346237323465653261316339353034323633623632313630666531373839633665333637
34356162356565396564

View file

@ -1,205 +0,0 @@
{ config, pkgs, home-manager-quadlet-nix, ... }:
let
user = "authentik";
state-directory = "/var/lib/authentik";
port = "9000";
in {
# Secrets are translated in the system NixOS scope, but performed by the user,
# via a user systemd unit, so are available in the user's Podman secrets
age.secrets = {
"authentik.toml" = {
file = ./../secrets/authentik.toml.age;
owner = "${user}";
};
};
millironx.podman-secrets.authentik = {
user = "${user}";
secrets-files = [ config.age.secrets."authentik.toml".path ];
};
# Podman, unlike Docker apparently, does not automatically create mount points
# within folders, so every mounted folder needs to be specified here.
systemd.tmpfiles.rules = [
"d ${state-directory} 1775 ${user} ${user} -"
"d ${state-directory}/database 1775 ${user} ${user} -"
"d ${state-directory}/media 1775 ${user} ${user} -"
"d ${state-directory}/certs 1775 ${user} ${user} -"
"d ${state-directory}/custom-templates 1775 ${user} ${user} -"
];
services.caddy.virtualHosts."auth.millironx.com".extraConfig = ''
reverse_proxy http://127.0.0.1:${port}
'';
services.borgmatic.configurations."${config.networking.hostName}" = {
source_directories = [
"${state-directory}/media"
"${state-directory}/certs"
"${state-directory}/custom-templates"
];
postgresql_databases = [{
name = "authentik";
psql_command =
"/run/wrappers/bin/sudo -iu ${user} ${pkgs.podman}/bin/podman exec authentik-db psql --username=${user}";
pg_dump_command =
"/run/wrappers/bin/sudo -iu ${user} ${pkgs.podman}/bin/podman exec authentik-db pg_dump --username=${user}";
pg_restore_command =
"/run/wrappers/bin/sudo -iu ${user} ${pkgs.podman}/bin/podman exec authentik-db pg_restore --username=${user}";
}];
};
# Create a dedicated user for this service
users.users."${user}" = {
# Group is technically not mandatory, but NixOS complains that an unset
# group is "unsafe." The group has to be declared below
group = "${user}";
# System users don't have a shell. For security purposes, that *would* be
# superior, but that means that, e.g. borgmatic can't `sudo` into the
# account to access Podman commands
isNormalUser = true;
# A home directory is mandatory in order to allow systemd user units to be
# created and run
home = "${state-directory}";
createHome = true;
# Settings for running containers while a login shell is not active
linger = true;
autoSubUidGidRange = true;
};
users.groups."${user}" = { };
home-manager.users."${user}" = { config, osConfig, ... }: {
imports = [ home-manager-quadlet-nix ];
home.stateVersion = "25.05";
virtualisation.quadlet = {
containers = {
authentik-db = {
autoStart = true;
containerConfig = {
image = "docker.io/library/postgres:16";
environments = {
POSTGRES_DB = "${user}";
POSTGRES_USER = "${user}";
};
secrets = [
# POSTGRES_PASSWORD is used by the container to setup the
# database, PGPASSWORD is used by pg_dump to authenticate for
# backup purposes
"AUTHENTIK_POSTGRESQL__PASSWORD,type=env,target=POSTGRES_PASSWORD"
"AUTHENTIK_POSTGRESQL__PASSWORD,type=env,target=PGPASSWORD"
];
# Double dollarsigns are required for use of *container* environment
# variables, Nix escaping creates the weird $\${} syntax
healthCmd = "pg_isready -d $\${POSTGRES_DB} -U $\${POSTGRES_USER}";
healthInterval = "30s";
healthRetries = 5;
healthStartPeriod = "20s";
# Volumes have to be bound with the :U label to allow for username
# remapping in rootless containers. :Z/:z is not needed b/c NixOS
# does not support SELinux
volumes =
[ "${state-directory}/database:/var/lib/postgresql/data:U" ];
# A network is actually required for these containers to talk to one
# another. I suppose the more idiomatic way would be for these
# related containers to be in a "pod," but I'm still struggling
# learning the idiosyncrasies of quadlet/rootless/podman, so we'll
# stick with this for now
networks =
[ config.virtualisation.quadlet.networks.authentik-net.ref ];
};
# Allowing this container to start before the secrets are translated
# will lead to errors. Use osConfig b/c secrets are declared in the
# system NixOS scope, even though it is a user process.
unitConfig.Requires =
[ osConfig.millironx.podman-secrets.authentik.ref ];
unitConfig.After =
[ osConfig.millironx.podman-secrets.authentik.ref ];
};
authentik-worker = {
autoStart = true;
containerConfig = {
image = "ghcr.io/goauthentik/server:2025.10.2";
environments = {
AUTHENTIK_POSTGRESQL__HOST = "authentik-db";
AUTHENTIK_POSTGRESQL__NAME = "${user}";
AUTHENTIK_POSTGRESQL__USER = "${user}";
};
exec = "worker";
secrets = [
"AUTHENTIK_POSTGRESQL__PASSWORD,type=env"
"AUTHENTIK_SECRET_KEY,type=env"
];
volumes = [
"${state-directory}/media:/media:U"
"${state-directory}/custom-templates:/templates:U"
"${state-directory}/certs:/certs:U"
];
networks =
[ config.virtualisation.quadlet.networks.authentik-net.ref ];
};
unitConfig.Requires =
[ config.virtualisation.quadlet.containers.authentik-db.ref ];
unitConfig.After =
[ config.virtualisation.quadlet.containers.authentik-db.ref ];
};
authentik = {
autoStart = true;
containerConfig = {
image = "ghcr.io/goauthentik/server:2025.10.2";
environments = {
AUTHENTIK_POSTGRESQL__HOST = "authentik-db";
AUTHENTIK_POSTGRESQL__NAME = "${user}";
AUTHENTIK_POSTGRESQL__USER = "${user}";
};
exec = "server";
secrets = [
"AUTHENTIK_POSTGRESQL__PASSWORD,type=env"
"AUTHENTIK_SECRET_KEY,type=env"
"AUTHENTIK_EMAIL__HOST,type=env"
"AUTHENTIK_EMAIL__PORT,type=env"
"AUTHENTIK_EMAIL__USERNAME,type=env"
"AUTHENTIK_EMAIL__PASSWORD,type=env"
"AUTHENTIK_EMAIL__USE_SSL,type=env"
"AUTHENTIK_EMAIL__FROM,type=env"
];
# Change from Traefik: publish ports to localhost only via 127.0.0.1
# and then reverse proxy to that port. Authentik does not appear to
# have a way to configure the port, so we will use the default of
# 9000 for non-secured traffic.
publishPorts = [ "127.0.0.1:${port}:${port}" ];
volumes = [
"${state-directory}/media:/media:U"
"${state-directory}/custom-templates:/templates:U"
];
networks =
[ config.virtualisation.quadlet.networks.authentik-net.ref ];
};
unitConfig.Requires =
[ config.virtualisation.quadlet.containers.authentik-db.ref ];
unitConfig.After =
[ config.virtualisation.quadlet.containers.authentik-db.ref ];
};
};
networks.authentik-net = { };
# One of the main advantages of using Quadlet
autoUpdate.enable = true;
};
};
}

View file

@ -1,35 +0,0 @@
{ pkgs, config, ... }: {
# We don't want to expose the location where borg backups are going, so we
# will setup an encrypted ssh config that references the host/username
# combo as simply 'borgserver'
age.secrets = {
borgmatic-ssh-config = { file = ./../secrets/borgmatic-ssh-config.age; };
borgmatic-passphrase = { file = ./../secrets/borgmatic-passphrase.age; };
};
services.borgmatic = {
enable = true;
# This is the bare-bones way to get Borgmatic up and running. Other services
# are expected to declare their stateful directories by adding to
# `services.borgmatic.configurations."${config.networking.hostName}".source_directories`
# and to add their databases to
# `services.borgmatic.configurations."${config.networking.hostName}".[mariadb|postgresql|etc]_databases`
configurations."${config.networking.hostName}" = {
source_directories = [ "/home" "/root" ];
repositories = [{
label = "${config.networking.hostName}-default";
path = "ssh://borgserver/./repo";
}];
ssh_command =
"${pkgs.openssh}/bin/ssh -F ${config.age.secrets.borgmatic-ssh-config.path}";
encryption_passcommand =
"${pkgs.coreutils}/bin/cat ${config.age.secrets.borgmatic-passphrase.path}";
keep_daily = 7;
keep_weekly = 4;
keep_monthly = 6;
};
};
}

View file

@ -1,49 +1,90 @@
{ pkgs, config, ... }: { { pkgs, config, ... }:
let
crowdsec-url = "127.0.0.1:2763";
firewall-bouncer-name = "fw-bouncer";
# Although this key can be reproduced by anyone who actually cares to, the
# Crowdsec API will not be exposed to the outside world, so keeping this key
# super secret really isn't that important to me. Still make it look random
# so that hungry botnets can't just slurp up the password in plaintext.
firewall-bouncer-key = builtins.hashString "sha256"
"${config.networking.hostName}-crowdsec-bouncer-salt";
toMultiYAML = items:
pkgs.lib.concatMapStrings (item:
''
---
'' + (pkgs.lib.generators.toYAML { } item) + "\n") items;
in {
services = { services = {
crowdsec = { crowdsec = {
enable = true; enable = true;
localConfig = {
acquisitions = [
{
source = "journalctl";
journalctl_filter = [ "_SYSTEMD_UNIT=sshd.service" ];
labels.type = "syslog";
}
{
filenames = [ "/var/log/auth.log" ];
labels.type = "syslog";
}
{
filenames = [ "/var/log/syslog" "/var/log/kern.log" ];
labels.type = "syslog";
}
];
};
hub = {
collections = [
"crowdsecurity/base-http-scenarios"
"crowdsecurity/http-cve"
"crowdsecurity/http-dos"
"crowdsecurity/iptables"
"crowdsecurity/linux"
"crowdsecurity/sshd"
"crowdsecurity/whitelist-good-actors"
];
};
settings = { settings = {
general = { api.server.enable = true; }; api.server = { listen_uri = crowdsec-url; };
# See https://github.com/NixOS/nixpkgs/issues/445342 allowLocalJournalAccess = true;
lapi.credentialsFile = "/var/lib/crowdsec/lapi-credentials.yaml"; crowdsec_service.acquisition_path = pkgs.writeText "acquisitions.yaml"
(toMultiYAML [
{
source = "journalctl";
journalctl_filter = [ "_SYSTEMD_UNIT=sshd.service" ];
labels.type = "syslog";
}
{
filenames = [ "/var/log/auth.log" ];
labels.type = "syslog";
}
{
filenames = [ "/var/log/syslog" "/var/log/kern.log" ];
labels.type = "syslog";
}
]);
}; };
autoUpdateService = true;
}; };
crowdsec-firewall-bouncer = { crowdsec-firewall-bouncer = {
enable = true; enable = true;
registerBouncer.enable = true; settings = {
api_url = firewall-bouncer-name;
api_key = firewall-bouncer-key;
};
}; };
}; };
systemd.tmpfiles.rules = let cfg = config.services.crowdsec; systemd.services.crowdsec.serviceConfig = {
in [ "d /var/lib/crowdsec 0755 ${cfg.user} ${cfg.group}" ]; ExecStartPre = let
bouncer-script = pkgs.writeScriptBin "register-bouncer" ''
#!${pkgs.runtimeShell}
set -eu
set -o pipefail
if ! cscli bouncers list | grep -q "${firewall-bouncer-name}"; then
cscli bouncers add "${firewall-bouncer-name}" --key "${firewall-bouncer-key}"
fi
'';
collection-check = collection: ''
if ! cscli collections list | grep -q "${collection}"; then
cscli collections install "${collection}"
fi
'';
collections = [
"crowdsecurity/base-http-scenarios"
"crowdsecurity/http-cve"
"crowdsecurity/http-dos"
"crowdsecurity/iptables"
"crowdsecurity/linux"
"crowdsecurity/sshd"
"crowdsecurity/whitelist-good-actors"
];
collection-script = pkgs.writeScriptBin "install-collections" ''
#!${pkgs.runtimeShell}
set -eu
set -o pipefail
${pkgs.lib.concatMapStrings collection-check collections}
'';
in [
"${bouncer-script}/bin/register-bouncer"
"${collection-script}/bin/install-collections"
];
};
} }

View file

@ -1,146 +0,0 @@
{ config, pkgs, home-manager-quadlet-nix, ... }:
let
user = "freshrss";
port = "37374";
stateDirectory = "/var/lib/freshrss";
serviceContainer = "freshrss";
stateSubDir = subDir: "${stateDirectory}/${subDir}";
createTmpfilesRule = subDir: "d ${stateSubDir subDir} 1755 ${user} ${user}";
dbDirectories = [ "database" ];
serviceDirectories = [ "data" "extensions" ];
in {
age.secrets = {
"freshrss.toml" = {
file = ./../secrets/freshrss.toml.age;
owner = "${user}";
};
};
millironx.podman-secrets.freshrss = {
user = "${user}";
secrets-files = [ config.age.secrets."freshrss.toml".path ];
};
services.caddy.virtualHosts."feeds.millironx.com".extraConfig = ''
reverse_proxy http://127.0.0.1:${port} {
header_up X-Forwarded-Port 443
}
'';
systemd.tmpfiles.rules = builtins.map createTmpfilesRule
([ stateDirectory ] ++ dbDirectories ++ serviceDirectories);
services.borgmatic.configurations."${config.networking.hostName}" = {
source_directories = builtins.map stateSubDir dbDirectories;
postgresql_databases = [{
name = serviceContainer;
psql_command =
"/run/wrappers/bin/sudo -iu ${user} ${pkgs.podman}/bin/podman exec ${serviceContainer}-db psql --username=${user}";
pg_dump_command =
"/run/wrappers/bin/sudo -iu ${user} ${pkgs.podman}/bin/podman exec ${serviceContainer}-db pg_dump --username=${user}";
pg_restore_command =
"/run/wrappers/bin/sudo -iu ${user} ${pkgs.podman}/bin/podman exec ${serviceContainer}-db pg_restore --username=${user}";
}];
};
users.users."${user}" = {
group = "${user}";
isNormalUser = true;
home = "${stateDirectory}";
createHome = true;
linger = true;
autoSubUidGidRange = true;
};
users.groups."${user}" = { };
home-manager.users."${user}" = { config, osConfig, ... }: {
imports = [ home-manager-quadlet-nix ];
home.stateVersion = "25.05";
virtualisation.quadlet = let
inherit (config.virtualisation.quadlet) containers;
inherit (config.virtualisation.quadlet) networks;
secrets = osConfig.millironx.podman-secrets.freshrss;
in {
containers = {
"${serviceContainer}-db" = {
autoStart = true;
containerConfig = {
image = "docker.io/library/postgres:16";
environments = {
POSTGRES_DB = "${user}";
POSTGRES_USER = "${user}";
};
secrets = [
"POSTGRES_PASSWORD,type=env"
"POSTGRES_PASSWORD,type=env,target=PGPASSWORD"
];
healthCmd = "pg_isready -d $\${POSTGRES_DB} -U $\${POSTGRES_USER}";
healthInterval = "30s";
healthRetries = 5;
healthStartPeriod = "20s";
volumes =
[ "${stateDirectory}/database:/var/lib/postgresql/data:U" ];
networks = [ networks."${serviceContainer}".ref ];
};
unitConfig.Requires = [ secrets.ref ];
unitConfig.After = [ secrets.ref ];
};
"${serviceContainer}" = {
autoStart = true;
containerConfig = {
image = "docker.io/freshrss/freshrss:1";
# Required to allow the container to talk to the host ports, in
# other words, to resolve Authentik correctly
addHosts = [ "auth.millironx.com:host-gateway" ];
environments = {
TZ = osConfig.time.timeZone;
CRON_MIN = "2,32";
LISTEN = "0.0.0.0:${port}";
TRUSTED_PROXY = "172.16.0.1/12 192.168.0.1/16";
OIDC_ENABLED = "1";
OIDC_PROVIDER_METADATA_URL =
"https://auth.millironx.com/application/o/freshrss/.well-known/openid-configuration";
OIDC_REMOTE_USER_CLAIM = "preferred_username";
OIDC_SCOPES = "openid email profile";
OIDC_X_FORWARDED_HEADERS =
"X-Forwarded-Host X-Forwarded-Port X-Forwarded-Proto";
};
secrets = [
"OIDC_CLIENT_ID,type=env"
"OIDC_CLIENT_SECRET,type=env"
"OIDC_CLIENT_CRYPTO_KEY,type=env"
];
healthCmd = "cli/health.php";
healthTimeout = "10s";
healthStartPeriod = "60s";
healthStartupInterval = "11s";
healthInterval = "75s";
healthRetries = 3;
networks = [ networks."${serviceContainer}".ref ];
publishPorts = [ "127.0.0.1:${port}:${port}" ];
volumes = [
"${stateDirectory}/data:/var/www/FreshRSS/data:U"
"${stateDirectory}/extensions:/var/www/FreshRSS/extensions:U"
];
};
unitConfig.Requires =
[ secrets.ref containers."${serviceContainer}-db".ref ];
unitConfig.After =
[ secrets.ref containers."${serviceContainer}-db".ref ];
};
};
networks."${serviceContainer}" = { };
autoUpdate.enable = true;
autoEscape = true;
};
};
}

50
services/nixos-update.nix Normal file
View file

@ -0,0 +1,50 @@
# This file is designed to be used in the imports
{ pkgs, config, ... }:
{
environment.systemPackages = [
(pkgs.writeScriptBin "update-nixos" ''
#!${pkgs.bash}/bin/bash
echo "Requesting system update..."
${pkgs.systemd}/bin/systemctl start nixos-update.service
'')
];
# Service to update NixOS configuration from git repo
systemd.services."nixos-update" = {
description = "Update NixOS configuration from git repository";
path = with pkgs; [ coreutils ];
script = ''
# Rebuild the system directly from the remote flake
${pkgs.nixos-rebuild}/bin/nixos-rebuild switch --flake git+https://code.millironx.com/millironx/nix-dotfiles.git#${config.networking.hostName}
'';
serviceConfig = {
Type = "oneshot";
User = "root";
};
};
# Timer to run the update service daily at 3am
systemd.timers."nixos-update" = {
wantedBy = [ "timers.target" ];
description = "Run NixOS update daily at 3am";
timerConfig = {
OnCalendar = "3:00";
Persistent = true;
Unit = "nixos-update.service";
};
};
# Polkit rule to allow non-root users to trigger the update
security.polkit.extraConfig = ''
polkit.addRule(function(action, subject) {
if (action.id == "org.freedesktop.systemd1.manage-units" &&
action.lookup("unit") == "nixos-update.service" &&
action.lookup("verb") == "start" &&
subject.isInGroup("wheel")) {
return polkit.Result.YES;
}
});
'';
}

27
services/pihole.nix Normal file
View file

@ -0,0 +1,27 @@
{ config, ... }:
{
age.secrets = {
pihole-credentials = {
file = ./../secrets/pihole.age;
owner = "root";
group = "root";
};
};
virtualisation = {
quadlet = {
containers = {
pihole = {
containerConfig = {
image = "docker.io/pihole/pihole:2025.06.2";
publishPorts =
[ "53:53/tcp" "53:53/udp" "80:80/tcp" "443:443/tcp" ];
environmentFiles = [ config.age.secrets.pihole-credentials.path ];
networks = [ "bridge" ];
dns = [ "127.0.0.1" "194.242.2.9" "9.9.9.9" ];
};
};
};
};
};
}

View file

@ -2,6 +2,7 @@
services.samba = { services.samba = {
enable = true; enable = true;
package = pkgs.sambaFull; package = pkgs.sambaFull;
securityType = "user";
openFirewall = true; openFirewall = true;
settings = { settings = {
global = { global = {

View file

@ -15,8 +15,6 @@ in {
rig-install rig-install
]; ];
age.secrets.firefox-policy.file = ./../../secrets/darwin-policies-json.age;
# Use a custom configuration.nix location. # Use a custom configuration.nix location.
# $ darwin-rebuild switch -I darwin-config=$HOME/.config/nixpkgs/darwin/configuration.nix # $ darwin-rebuild switch -I darwin-config=$HOME/.config/nixpkgs/darwin/configuration.nix
environment.darwinConfig = "$HOME/.config/home-manager/configuration.nix"; environment.darwinConfig = "$HOME/.config/home-manager/configuration.nix";
@ -26,27 +24,9 @@ in {
}; };
# Auto upgrade nix package and the daemon service. # Auto upgrade nix package and the daemon service.
nix = { nix.enable = true;
enable = true; #services.nix-daemon.tempDir = "/nix/tmp";
gc = { nix.package = pkgs.nix;
automatic = true;
interval = { Weekday = 1; };
options = ''
--delete-older-than 14d
'';
};
# Needed for rosetta-builder, see
# <https://github.com/cpick/nix-rosetta-builder/issues/40#issuecomment-3368602687>
# <https://github.com/cpick/nix-rosetta-builder/issues/37>
linux-builder = {
enable = true;
ephemeral = true;
};
extraOptions = ''
extra-platforms = x86_64-darwin
'';
};
nix-rosetta-builder.onDemand = true;
# Create /etc/zshrc that loads the nix-darwin environment. # Create /etc/zshrc that loads the nix-darwin environment.
programs.zsh.enable = true; # default shell on catalina programs.zsh.enable = true; # default shell on catalina
@ -91,12 +71,11 @@ in {
in [ in [
(sysApp "Firefox") (sysApp "Firefox")
(sysApp "Thunderbird") (sysApp "Thunderbird")
(sysApp "Microsoft Outlook")
(sysApp "Zed")
(sysApp "Logseq") (sysApp "Logseq")
(sysApp "Zed")
(sysApp "Steam") (sysApp "Steam")
(localApp "Instinct Dashboard") (chromeApp "Instinct Dashboard")
(localApp "Carestream") (chromeApp "Carestream")
]; ];
show-process-indicators = true; show-process-indicators = true;
show-recents = false; show-recents = false;
@ -151,11 +130,6 @@ in {
--user=${config.system.primaryUser} \ --user=${config.system.primaryUser} \
--set-home \ --set-home \
_rig-install ${r-version} _rig-install ${r-version}
echo "Applying custom defaults..."
/usr/bin/defaults import \
/Library/Preferences/org.mozilla.firefox \
${config.age.secrets.firefox-policy.path}
''; '';
nix.settings.experimental-features = [ "nix-command" "flakes" ]; nix.settings.experimental-features = [ "nix-command" "flakes" ];
@ -203,18 +177,23 @@ in {
"docker" "docker"
"docker-buildx" "docker-buildx"
"docker-credential-helper" "docker-credential-helper"
"firefoxpwa"
"mpv" "mpv"
]; ];
casks = [ casks = [
"alt-tab" "alt-tab"
"anki"
"db-browser-for-sqlite" "db-browser-for-sqlite"
"dolphin" "dolphin"
"firefox" "firefox"
"freetube" "freetube"
"ghostty"
"inkscape" "inkscape"
"iterm2" "iterm2"
"logi-options+"
"logseq" "logseq"
"macfuse" "macfuse"
"musescore"
"nextcloud" "nextcloud"
"openrct2" "openrct2"
"qownnotes" "qownnotes"
@ -222,7 +201,6 @@ in {
"rig" "rig"
"rstudio" "rstudio"
"signal" "signal"
"skim"
"slack" "slack"
"stats" "stats"
"steam" "steam"
@ -232,7 +210,6 @@ in {
"ungoogled-chromium" "ungoogled-chromium"
"veracrypt" "veracrypt"
"vlc" "vlc"
"vienna"
"vorta" "vorta"
"zed" "zed"
"zotero" "zotero"

View file

@ -8,7 +8,9 @@
imports = [ imports = [
./hardware-configuration/bosephus.nix ./hardware-configuration/bosephus.nix
./hardware-configuration/bosephus-external-drives.nix ./hardware-configuration/bosephus-external-drives.nix
./../../services/nixos-update.nix
./../../services/samba.nix ./../../services/samba.nix
./../../services/pihole.nix
]; ];
# Bootloader. # Bootloader.
@ -16,8 +18,8 @@
boot.loader.efi.canTouchEfiVariables = true; boot.loader.efi.canTouchEfiVariables = true;
# Ignore lid - so I can close without having the system go into sleep mode # Ignore lid - so I can close without having the system go into sleep mode
services.logind.settings.Login.HandleLidSwitch = "ignore"; services.logind.lidSwitch = "ignore";
services.logind.settings.Login.HandleLidSwitchDocked = "ignore"; services.logind.lidSwitchDocked = "ignore";
# Secrets # Secrets
age.secrets = { age.secrets = {

View file

@ -3,11 +3,8 @@
{ {
imports = [ # Include the results of the hardware scan. imports = [ # Include the results of the hardware scan.
./hardware-configuration/mcentire.nix ./hardware-configuration/mcentire.nix
./../../modules/podman-secrets.nix ./../../services/nixos-update.nix
./../../services/borgmatic.nix
./../../services/crowdsec.nix ./../../services/crowdsec.nix
./../../services/authentik.nix
./../../services/freshrss.nix
]; ];
# Use the GRUB 2 boot loader. # Use the GRUB 2 boot loader.
@ -18,7 +15,6 @@
useDHCP = false; useDHCP = false;
interfaces.eth0.useDHCP = true; interfaces.eth0.useDHCP = true;
hostName = "mcentire"; # Define your hostname. hostName = "mcentire"; # Define your hostname.
firewall.allowedTCPPorts = [ 80 443 ];
}; };
# Set your time zone. # Set your time zone.
@ -44,7 +40,7 @@
millironx = { millironx = {
isNormalUser = true; isNormalUser = true;
description = "Thomas A. Christensen II"; description = "Thomas A. Christensen II";
extraGroups = [ "adm" "wheel" ]; extraGroups = [ "wheel" ];
}; };
}; };
@ -58,15 +54,8 @@
services = { services = {
openssh.enable = true; openssh.enable = true;
tailscale.enable = true; tailscale.enable = true;
caddy.enable = true;
# Do not "enable" database services, but include the package configuration
# so that borgmatic does not freak out about unset variables
postgresql.package = pkgs.postgresql_17;
}; };
virtualisation.quadlet.enable = true;
system.stateVersion = "25.05"; # Did you read the comment? system.stateVersion = "25.05"; # Did you read the comment?
nix = { extraOptions = "experimental-features = nix-command flakes"; }; nix = { extraOptions = "experimental-features = nix-command flakes"; };
} }