Compare commits
No commits in common. "master" and "feat/mcentire-crowdsec" have entirely different histories.
master
...
feat/mcent
82 changed files with 927 additions and 2994 deletions
74
.gitignore
vendored
74
.gitignore
vendored
|
|
@ -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
157
README.md
|
|
@ -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
|
||||
|
|
@ -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()
|
||||
|
|
@ -1,157 +0,0 @@
|
|||
# Translated from <https://github.com/Chocobozzz/PeerTube/blob/v8.1.4/support/nginx/peertube>
|
||||
@api_resumable <<CEL
|
||||
path_regexp('^/api/v1/videos/(upload-resumable|([^/]+/source/replace-resumable))$')
|
||||
|| path_regexp('^/api/v1/users/[^/]+/imports/import-resumable$')
|
||||
CEL
|
||||
|
||||
handle @api_resumable {
|
||||
reverse_proxy http://127.0.0.1:{$MILLIRONX_PEERTUBE_PORT} {
|
||||
header_up X-Real-IP {remote_host}
|
||||
flush_interval -1
|
||||
}
|
||||
}
|
||||
|
||||
@api_video_upload_wrong_method <<CEL
|
||||
path_regexp('^/api/v1/videos/(upload|([^/]+/studio/edit))$')
|
||||
&& !method('POST', 'HEAD')
|
||||
CEL
|
||||
handle @api_video_upload_wrong_method {
|
||||
header Allow "POST,HEAD"
|
||||
respond 405
|
||||
}
|
||||
|
||||
@api_video_upload <<CEL
|
||||
path_regexp('^/api/v1/videos/(upload|([^/]+/studio/edit))$')
|
||||
&& method('POST', 'HEAD')
|
||||
CEL
|
||||
handle @api_video_upload {
|
||||
request_body {
|
||||
max_size 12G
|
||||
}
|
||||
|
||||
reverse_proxy http://127.0.0.1:{$MILLIRONX_PEERTUBE_PORT} {
|
||||
flush_interval -1
|
||||
header_up X-File-Maximum-Size 8G
|
||||
}
|
||||
}
|
||||
|
||||
@api_jobs path_regexp ^/api/v1/runners/jobs/[^/]+/(update|success)$
|
||||
handle @api_jobs {
|
||||
reverse_proxy http://127.0.0.1:{$MILLIRONX_PEERTUBE_PORT} {
|
||||
flush_interval -1
|
||||
}
|
||||
}
|
||||
|
||||
@api_videos path_regexp ^/api/v1/(videos|video-playlists|video-channels|users/me)
|
||||
handle @api_videos {
|
||||
request_body {
|
||||
max_size 12M
|
||||
}
|
||||
|
||||
reverse_proxy http://127.0.0.1:{$MILLIRONX_PEERTUBE_PORT} {
|
||||
header_up X-File-Maximum-Size 8M
|
||||
}
|
||||
}
|
||||
|
||||
@websocket_socket_plugins <<CEL
|
||||
path('/socket.io')
|
||||
|| path_regexp('^/plugins/[^/]+(/[^/]+)?/ws/')
|
||||
CEL
|
||||
handle @websocket_socket_plugins {
|
||||
reverse_proxy http://127.0.0.1:{$MILLIRONX_PEERTUBE_PORT} {
|
||||
header_up X-Real-IP {remote_host}
|
||||
}
|
||||
}
|
||||
|
||||
handle /tracker/socket {
|
||||
reverse_proxy http://127.0.0.1:{$MILLIRONX_PEERTUBE_PORT} {
|
||||
header_up X-Real-IP {remote_host}
|
||||
transport http {
|
||||
read_timeout 15m
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@client_overridable_assets {
|
||||
path_regexp asset_path ^/client/(assets/images/(default-playlist\.jpg|default-avatar-account\.png|default-avatar-account-48x48\.png|default-avatar-video-channel\.png|default-avatar-video-channel-48x48\.png))$
|
||||
}
|
||||
handle @client_overridable_assets {
|
||||
root * {$MILLIRONX_PEERTUBE_ASSETS_DIR}
|
||||
header Cache-Control "public, max-age=31536000, immutable"
|
||||
|
||||
@try_override file {
|
||||
try_files storage/client-overrides/{re.asset_path.1}
|
||||
}
|
||||
handle @try_override {
|
||||
rewrite * storage/client-overrides/{re.asset_path.1}
|
||||
file_server
|
||||
}
|
||||
|
||||
@try_dist file {
|
||||
try_files {re.asset_path.1}
|
||||
}
|
||||
handle @try_dist {
|
||||
rewrite * {re.asset_path.1}
|
||||
file_server
|
||||
}
|
||||
|
||||
reverse_proxy http://127.0.0.1:{$MILLIRONX_PEERTUBE_PORT}
|
||||
}
|
||||
|
||||
@client_static {
|
||||
path_regexp asset_path ^/client/(.*\.(js|css|png|svg|woff2|otf|ttf|woff|eot))$
|
||||
}
|
||||
handle @client_static {
|
||||
root * {$MILLIRONX_PEERTUBE_ASSETS_DIR}
|
||||
header Cache-Control "public, max-age=31536000, immutable"
|
||||
rewrite * {re.asset_path.1}
|
||||
file_server
|
||||
}
|
||||
|
||||
@static_downloads {
|
||||
path_regexp dl_path ^/static/(webseed|web-videos|redundancy|streaming-playlists)/
|
||||
}
|
||||
handle @static_downloads {
|
||||
@options method OPTIONS
|
||||
handle @options {
|
||||
header Access-Control-Allow-Origin "*"
|
||||
header Access-Control-Allow-Methods "GET,OPTIONS"
|
||||
header Access-Control-Allow-Headers "Range,DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type"
|
||||
header Access-Control-Max-Age 1728000
|
||||
header Content-Type 'text/plain; charset=UTF-8'
|
||||
header Content-Length 0
|
||||
|
||||
respond 204
|
||||
}
|
||||
|
||||
@get method GET
|
||||
handle @get {
|
||||
header Access-Control-Allow-Origin "*"
|
||||
header Access-Control-Allow-Methods "GET,OPTIONS"
|
||||
header Access-Control-Allow-Headers "Range,DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type"
|
||||
|
||||
uri path_regexp ^/static/webseed/(.*)$ /web-videos/$1
|
||||
uri path_regexp ^/static/(.*)$ /$1
|
||||
|
||||
root * {$MILLIRONX_PEERTUBE_STORAGE_DIR}
|
||||
|
||||
@file_exists file {
|
||||
try_files {path}
|
||||
}
|
||||
handle @file_exists {
|
||||
file_server
|
||||
}
|
||||
|
||||
reverse_proxy http://127.0.0.1:{$MILLIRONX_PEERTUBE_PORT} {
|
||||
header_up X-Real-IP {remote_host}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
request_body {
|
||||
max_size 100KB
|
||||
}
|
||||
|
||||
reverse_proxy http://127.0.0.1:{$MILLIRONX_PEERTUBE_PORT} {
|
||||
header_up X-Real-IP {remote_host}
|
||||
}
|
||||
24
dotfiles/dolphinrc
Normal file
24
dotfiles/dolphinrc
Normal 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
9
dotfiles/konsolerc
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
[Desktop Entry]
|
||||
DefaultProfile=My Default.profile
|
||||
|
||||
[MainWindow]
|
||||
StatusBar=Disabled
|
||||
ToolBarsMovable=Disabled
|
||||
|
||||
[UiSettings]
|
||||
ColorScheme=Default
|
||||
143
flake.lock
generated
143
flake.lock
generated
|
|
@ -14,11 +14,11 @@
|
|||
"systems": "systems"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1770165109,
|
||||
"narHash": "sha256-9VnK6Oqai65puVJ4WYtCTvlJeXxMzAp/69HhQuTdl/I=",
|
||||
"lastModified": 1754433428,
|
||||
"narHash": "sha256-NA/FT2hVhKDftbHSwVnoRTFhes62+7dxZbxj5Gxvghs=",
|
||||
"owner": "ryantm",
|
||||
"repo": "agenix",
|
||||
"rev": "b027ee29d959fda4b60b57566d64c98a202e0feb",
|
||||
"rev": "9edb1787864c4f59ae5074ad498b6272b3ec308d",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
@ -27,6 +27,27 @@
|
|||
"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": {
|
||||
"inputs": {
|
||||
"nixpkgs-lib": [
|
||||
|
|
@ -48,6 +69,23 @@
|
|||
"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": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
|
|
@ -55,16 +93,16 @@
|
|||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1772985280,
|
||||
"narHash": "sha256-FdrNykOoY9VStevU4zjSUdvsL9SzJTcXt4omdEDZDLk=",
|
||||
"lastModified": 1756679287,
|
||||
"narHash": "sha256-Xd1vOeY9ccDf5VtVK12yM0FS6qqvfUop8UQlxEB+gTQ=",
|
||||
"owner": "nix-community",
|
||||
"repo": "home-manager",
|
||||
"rev": "8f736f007139d7f70752657dff6a401a585d6cbc",
|
||||
"rev": "07fc025fe10487dd80f2ec694f1cd790e752d0e8",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-community",
|
||||
"ref": "release-25.11",
|
||||
"ref": "release-25.05",
|
||||
"repo": "home-manager",
|
||||
"type": "github"
|
||||
}
|
||||
|
|
@ -76,79 +114,59 @@
|
|||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1772129556,
|
||||
"narHash": "sha256-Utk0zd8STPsUJPyjabhzPc5BpPodLTXrwkpXBHYnpeg=",
|
||||
"lastModified": 1757432263,
|
||||
"narHash": "sha256-qHn+/0+IOz5cG68BZUwL9BV3EO/e9eNKCjH3+N7wMdI=",
|
||||
"owner": "LnL7",
|
||||
"repo": "nix-darwin",
|
||||
"rev": "ebec37af18215214173c98cf6356d0aca24a2585",
|
||||
"rev": "1fef4404de4d1596aa5ab2bd68078370e1b9dcdb",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "LnL7",
|
||||
"ref": "nix-darwin-25.11",
|
||||
"ref": "nix-darwin-25.05",
|
||||
"repo": "nix-darwin",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nix-rosetta-builder": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"nixpkgs-darwin"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1770491098,
|
||||
"narHash": "sha256-ZfhynJqgV3A9hEivcgOEZa+TZnJPc26lIUjzKsSchgI=",
|
||||
"owner": "cpick",
|
||||
"repo": "nix-rosetta-builder",
|
||||
"rev": "50e6070082e0b4fbaf67dd8f346892a1a9ed685c",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "cpick",
|
||||
"repo": "nix-rosetta-builder",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1772822230,
|
||||
"narHash": "sha256-yf3iYLGbGVlIthlQIk5/4/EQDZNNEmuqKZkQssMljuw=",
|
||||
"lastModified": 1757545623,
|
||||
"narHash": "sha256-mCxPABZ6jRjUQx3bPP4vjA68ETbPLNz9V2pk9tO7pRQ=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "71caefce12ba78d84fe618cf61644dce01cf3a96",
|
||||
"rev": "8cd5ce828d5d1d16feff37340171a98fc3bf6526",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nixos",
|
||||
"ref": "nixos-25.11",
|
||||
"ref": "nixos-25.05",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs-darwin": {
|
||||
"locked": {
|
||||
"lastModified": 1766129819,
|
||||
"narHash": "sha256-crNRwvsbH2XSV8IwBjX6Tm+uWmYwhYyRuNVJ9/ZwlmA=",
|
||||
"lastModified": 1757590060,
|
||||
"narHash": "sha256-EWwwdKLMZALkgHFyKW7rmyhxECO74+N+ZO5xTDnY/5c=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "eedcb27bf99430e51f83d896cd1149b828290d20",
|
||||
"rev": "0ef228213045d2cdb5a169a95d63ded38670b293",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nixos",
|
||||
"ref": "nixpkgs-25.05-darwin",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "eedcb27bf99430e51f83d896cd1149b828290d20",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs-unstable": {
|
||||
"locked": {
|
||||
"lastModified": 1772771118,
|
||||
"narHash": "sha256-xWzaTvmmACR/SRWtABgI/Z97lcqwJAeoSd5QW1KdK1s=",
|
||||
"lastModified": 1757034884,
|
||||
"narHash": "sha256-PgLSZDBEWUHpfTRfFyklmiiLBE1i1aGCtz4eRA3POao=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "e38213b91d3786389a446dfce4ff5a8aaf6012f2",
|
||||
"rev": "ca77296380960cd497a765102eeb1356eb80fed0",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
@ -166,11 +184,11 @@
|
|||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1773029295,
|
||||
"narHash": "sha256-xmHhVHbaA5hR3dCEoGwqAgL6HTTJ0KEMRUTLdJuVtGM=",
|
||||
"lastModified": 1757647720,
|
||||
"narHash": "sha256-qf/utP3d1qBDl5R4yWUCt7E7CHTkw2NY8BEsS7lJ0dc=",
|
||||
"owner": "nix-community",
|
||||
"repo": "NUR",
|
||||
"rev": "bf45b24de2134f1488f7a6c135f4b0420ccec6fe",
|
||||
"rev": "ef767aa25f9f917fe25d3848051f0e54ae42349f",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
@ -189,11 +207,11 @@
|
|||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1772361940,
|
||||
"narHash": "sha256-B1Cz+ydL1iaOnGlwOFld/C8lBECPtzhiy/pP93/CuyY=",
|
||||
"lastModified": 1756632588,
|
||||
"narHash": "sha256-ydam6eggXf3ZwRutyCABwSbMAlX+5lW6w1SVZQ+kfSo=",
|
||||
"owner": "nix-community",
|
||||
"repo": "plasma-manager",
|
||||
"rev": "a4b33606111c9c5dcd10009042bb710307174f51",
|
||||
"rev": "d47428e5390d6a5a8f764808a4db15929347cd77",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
@ -204,11 +222,11 @@
|
|||
},
|
||||
"quadlet-nix": {
|
||||
"locked": {
|
||||
"lastModified": 1770606362,
|
||||
"narHash": "sha256-6pOOPOQr4rtgShBtkLkSDTql5rRqcUgTRz8O+axK2eM=",
|
||||
"lastModified": 1754008153,
|
||||
"narHash": "sha256-MYT1mDtSkiVg343agxgBFsnuNU3xS8vRy399JXX1Vw0=",
|
||||
"owner": "SEIAROTg",
|
||||
"repo": "quadlet-nix",
|
||||
"rev": "f4ae60350ea6015b6560cbd0e1f11f7e195c993d",
|
||||
"rev": "1b2d27d460d8c7e4da5ba44ede463b427160b5c4",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
@ -220,9 +238,9 @@
|
|||
"root": {
|
||||
"inputs": {
|
||||
"agenix": "agenix",
|
||||
"crowdsec": "crowdsec",
|
||||
"home-manager": "home-manager",
|
||||
"nix-darwin": "nix-darwin",
|
||||
"nix-rosetta-builder": "nix-rosetta-builder",
|
||||
"nixpkgs": "nixpkgs",
|
||||
"nixpkgs-darwin": "nixpkgs-darwin",
|
||||
"nixpkgs-unstable": "nixpkgs-unstable",
|
||||
|
|
@ -240,11 +258,11 @@
|
|||
},
|
||||
"locked": {
|
||||
"dir": "pkgs/firefox-addons",
|
||||
"lastModified": 1773028978,
|
||||
"narHash": "sha256-4BjOTYhHP8ljHShQyZ1gUIdwgSLjvaGN2ueKfqp6CQk=",
|
||||
"lastModified": 1757591399,
|
||||
"narHash": "sha256-OlvNzfsqDok0y5PDY+2dK5T53GsxAdm1YGdYHjxAiHM=",
|
||||
"owner": "rycee",
|
||||
"repo": "nur-expressions",
|
||||
"rev": "a6ed037ffc0b50a9bd0c92e20e31f270a03ca1e3",
|
||||
"rev": "b7d4f61ce9db44ba82859e15f6e1c175959948e3",
|
||||
"type": "gitlab"
|
||||
},
|
||||
"original": {
|
||||
|
|
@ -268,6 +286,21 @@
|
|||
"repo": "default",
|
||||
"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",
|
||||
|
|
|
|||
53
flake.nix
53
flake.nix
|
|
@ -3,10 +3,8 @@
|
|||
|
||||
inputs = {
|
||||
# Specify the source of Home Manager and Nixpkgs.
|
||||
nixpkgs.url = "github:nixos/nixpkgs/nixos-25.11";
|
||||
# Revert to a cached version of Julia for aarch64-darwin
|
||||
nixpkgs-darwin.url =
|
||||
"github:nixos/nixpkgs/eedcb27bf99430e51f83d896cd1149b828290d20";
|
||||
nixpkgs.url = "github:nixos/nixpkgs/nixos-25.05";
|
||||
nixpkgs-darwin.url = "github:nixos/nixpkgs/nixpkgs-25.05-darwin";
|
||||
nixpkgs-unstable.url = "github:nixos/nixpkgs/nixpkgs-unstable";
|
||||
|
||||
# Inputs for both darwin and linux systems
|
||||
|
|
@ -19,7 +17,7 @@
|
|||
};
|
||||
};
|
||||
home-manager = {
|
||||
url = "github:nix-community/home-manager/release-25.11";
|
||||
url = "github:nix-community/home-manager/release-25.05";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
nur = {
|
||||
|
|
@ -32,6 +30,10 @@
|
|||
};
|
||||
|
||||
# Linux-specific inputs
|
||||
crowdsec = {
|
||||
url = "git+https://codeberg.org/kampka/nix-flake-crowdsec.git";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
plasma-manager = {
|
||||
url = "github:nix-community/plasma-manager";
|
||||
inputs = {
|
||||
|
|
@ -43,28 +45,23 @@
|
|||
|
||||
# Darwin-specific inputs
|
||||
nix-darwin = {
|
||||
url = "github:LnL7/nix-darwin/nix-darwin-25.11";
|
||||
inputs.nixpkgs.follows = "nixpkgs-darwin";
|
||||
};
|
||||
nix-rosetta-builder = {
|
||||
url = "github:cpick/nix-rosetta-builder";
|
||||
url = "github:LnL7/nix-darwin/nix-darwin-25.05";
|
||||
inputs.nixpkgs.follows = "nixpkgs-darwin";
|
||||
};
|
||||
};
|
||||
|
||||
outputs = { self, nix-darwin, nixpkgs, nixpkgs-darwin, nixpkgs-unstable
|
||||
, home-manager, agenix, rycee-nurpkgs, nur, plasma-manager, quadlet-nix
|
||||
, nix-rosetta-builder, ... }:
|
||||
, home-manager, agenix, rycee-nurpkgs, nur, crowdsec, plasma-manager
|
||||
, quadlet-nix, ... }:
|
||||
let
|
||||
mkHomeConfiguration = { hostname, arch ? "x86_64", os ? "linux"
|
||||
, desktop ? false, extraModules ? [ ] }:
|
||||
let
|
||||
system = "${arch}-${os}";
|
||||
syspkg = if os == "darwin" then nixpkgs-darwin else nixpkgs;
|
||||
pkgs = import syspkg {
|
||||
pkgs = import nixpkgs {
|
||||
inherit system;
|
||||
config.allowUnfree = true;
|
||||
overlays = [ nur.overlays.default agenix.overlays.default ];
|
||||
overlays = [ nur.overlays.default ];
|
||||
};
|
||||
pkgs-unstable = import nixpkgs-unstable {
|
||||
inherit system;
|
||||
|
|
@ -83,8 +80,7 @@
|
|||
] ++ (if desktop then [ ./homes/desktop.nix ] else [ ])
|
||||
++ (if (desktop && os == "linux") then [
|
||||
./homes/linux-desktop.nix
|
||||
plasma-manager.homeModules.plasma-manager
|
||||
quadlet-nix.homeManagerModules.quadlet
|
||||
plasma-manager.homeManagerModules.plasma-manager
|
||||
] else
|
||||
[ ]) ++ extraModules;
|
||||
extraSpecialArgs = {
|
||||
|
|
@ -92,7 +88,6 @@
|
|||
inherit firefox-addons;
|
||||
inherit buildFirefoxXpiAddon;
|
||||
inherit custom-pkgs;
|
||||
inherit hostname;
|
||||
};
|
||||
};
|
||||
in {
|
||||
|
|
@ -106,12 +101,16 @@
|
|||
|
||||
"millironx@anderson" = mkHomeConfiguration { hostname = "anderson"; };
|
||||
|
||||
"millironx@mcentire" = mkHomeConfiguration { hostname = "mcentire"; };
|
||||
|
||||
"millironx@bosephus" = mkHomeConfiguration { hostname = "bosephus"; };
|
||||
|
||||
"tchristensen@beocat" = mkHomeConfiguration { hostname = "beocat"; };
|
||||
|
||||
"millironx@harmony" = mkHomeConfiguration {
|
||||
hostname = "harmony";
|
||||
arch = "aarch64";
|
||||
desktop = true;
|
||||
};
|
||||
|
||||
"millironx@odyssey" = mkHomeConfiguration {
|
||||
hostname = "odyssey";
|
||||
desktop = true;
|
||||
|
|
@ -127,11 +126,8 @@
|
|||
};
|
||||
agenix = agenix;
|
||||
};
|
||||
modules = [
|
||||
./systems/darwin/corianne.nix
|
||||
agenix.darwinModules.default
|
||||
nix-rosetta-builder.darwinModules.default
|
||||
];
|
||||
modules =
|
||||
[ ./systems/darwin/corianne.nix agenix.darwinModules.default ];
|
||||
};
|
||||
|
||||
nixosConfigurations = {
|
||||
|
|
@ -146,14 +142,13 @@
|
|||
|
||||
"mcentire" = nixpkgs.lib.nixosSystem {
|
||||
system = "x86_64-linux";
|
||||
specialArgs = {
|
||||
home-manager-quadlet-nix = quadlet-nix.homeManagerModules.quadlet;
|
||||
};
|
||||
modules = [
|
||||
./systems/linux/mcentire.nix
|
||||
agenix.nixosModules.default
|
||||
home-manager.nixosModules.home-manager
|
||||
quadlet-nix.nixosModules.quadlet
|
||||
crowdsec.nixosModules.crowdsec
|
||||
crowdsec.nixosModules.crowdsec-firewall-bouncer
|
||||
{ nixpkgs.overlays = [ crowdsec.overlays.default ]; }
|
||||
];
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,4 +1,13 @@
|
|||
{ config, 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 = [
|
||||
./../programs/shells.nix
|
||||
./../programs/bat.nix
|
||||
|
|
@ -6,15 +15,22 @@
|
|||
./../programs/git.nix
|
||||
./../programs/lsd.nix
|
||||
./../programs/neovim.nix
|
||||
./../programs/ssh.nix
|
||||
./../programs/starship.nix
|
||||
./../programs/tmux.nix
|
||||
./../programs/yt-dlp.nix
|
||||
];
|
||||
home = {
|
||||
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; [
|
||||
agenix
|
||||
btop
|
||||
cowsay
|
||||
figlet
|
||||
|
|
@ -28,12 +44,14 @@
|
|||
jq
|
||||
julia-bin
|
||||
lynx
|
||||
mamba-cpp
|
||||
micromamba
|
||||
most
|
||||
nextflow
|
||||
p7zip
|
||||
pdfgrep
|
||||
pipx
|
||||
python3
|
||||
zulu17
|
||||
];
|
||||
sessionVariables = {
|
||||
PAGER = "most";
|
||||
|
|
@ -65,10 +83,24 @@
|
|||
"tailscale set --exit-node=$(tailscale exit-node suggest | awk '{print $4}' | head -n1)";
|
||||
# tsed - TailScale Exit node Disconnect
|
||||
tsed = "tailscale set --exit-node=";
|
||||
micromamba = "mamba";
|
||||
conda = "mamba";
|
||||
};
|
||||
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 = {
|
||||
home-manager.enable = true;
|
||||
|
|
|
|||
110
homes/darwin.nix
110
homes/darwin.nix
|
|
@ -7,7 +7,7 @@
|
|||
];
|
||||
home = {
|
||||
packages = with pkgs; [
|
||||
macpm
|
||||
asitop
|
||||
pinentry_mac
|
||||
(pkgs.writeShellScriptBin "uq" ''
|
||||
xattr -rdv com.apple.quarantine "/Applications/$1.app"
|
||||
|
|
@ -31,6 +31,114 @@
|
|||
tailscale = "/Applications/Tailscale.app/Contents/MacOS/Tailscale";
|
||||
};
|
||||
};
|
||||
launchd = {
|
||||
enable = true;
|
||||
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 = {
|
||||
enable = true;
|
||||
config = {
|
||||
Label = "local.home-manager.freetube-sync";
|
||||
ProgramArguments = [
|
||||
"/bin/sh"
|
||||
"-c"
|
||||
''
|
||||
FREETUBE_CONFIG_DIR="$HOME/Library/Application Support/FreeTube"
|
||||
NEXTCLOUD_DIR="$HOME/Nextcloud/Settings/FreeTube"
|
||||
|
||||
# Create directories if they don't exist
|
||||
mkdir -p "$FREETUBE_CONFIG_DIR"
|
||||
mkdir -p "$NEXTCLOUD_DIR"
|
||||
|
||||
# List of database files to sync
|
||||
DB_FILES=("settings.db" "history.db" "playlists.db" "subscriptions.db" "profiles.db")
|
||||
|
||||
# Initial sync if Nextcloud has db files but FreeTube doesn't
|
||||
if [ -n "$(ls $NEXTCLOUD_DIR/*.db 2>/dev/null)" ] && [ ! -n "$(ls $FREETUBE_CONFIG_DIR/*.db 2>/dev/null)" ]; then
|
||||
echo "Performing initial sync from Nextcloud to FreeTube"
|
||||
for DB_FILE in "''${DB_FILES[@]}"; do
|
||||
if [ -f "$NEXTCLOUD_DIR/$DB_FILE" ]; then
|
||||
rsync -av --checksum "$NEXTCLOUD_DIR/$DB_FILE" "$FREETUBE_CONFIG_DIR/$DB_FILE"
|
||||
fi
|
||||
done
|
||||
# If Nextcloud is empty but FreeTube has db files, copy to Nextcloud
|
||||
elif [ ! -n "$(ls $NEXTCLOUD_DIR/*.db 2>/dev/null)" ] && [ -n "$(ls $FREETUBE_CONFIG_DIR/*.db 2>/dev/null)" ]; then
|
||||
echo "Performing initial sync from FreeTube to Nextcloud"
|
||||
for DB_FILE in "''${DB_FILES[@]}"; do
|
||||
if [ -f "$FREETUBE_CONFIG_DIR/$DB_FILE" ]; then
|
||||
rsync -av --checksum "$FREETUBE_CONFIG_DIR/$DB_FILE" "$NEXTCLOUD_DIR/$DB_FILE"
|
||||
fi
|
||||
done
|
||||
else
|
||||
# Process each database file individually
|
||||
for DB_FILE in "''${DB_FILES[@]}"; do
|
||||
FT_PATH="$FREETUBE_CONFIG_DIR/$DB_FILE"
|
||||
NC_PATH="$NEXTCLOUD_DIR/$DB_FILE"
|
||||
|
||||
# Skip if neither file exists
|
||||
if [ ! -f "$FT_PATH" ] && [ ! -f "$NC_PATH" ]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
# If only one exists, copy it
|
||||
if [ -f "$FT_PATH" ] && [ ! -f "$NC_PATH" ]; then
|
||||
echo "Copying $DB_FILE from FreeTube to Nextcloud"
|
||||
rsync -av "$FT_PATH" "$NC_PATH"
|
||||
continue
|
||||
fi
|
||||
|
||||
if [ ! -f "$FT_PATH" ] && [ -f "$NC_PATH" ]; then
|
||||
echo "Copying $DB_FILE from Nextcloud to FreeTube"
|
||||
rsync -av "$NC_PATH" "$FT_PATH"
|
||||
continue
|
||||
fi
|
||||
|
||||
# Both files exist, check which is newer
|
||||
if [ "$FT_PATH" -nt "$NC_PATH" ]; then
|
||||
echo "Syncing newer $DB_FILE from FreeTube to Nextcloud"
|
||||
rsync -av --update "$FT_PATH" "$NC_PATH"
|
||||
elif [ "$NC_PATH" -nt "$FT_PATH" ]; then
|
||||
echo "Syncing newer $DB_FILE from Nextcloud to FreeTube"
|
||||
rsync -av --update "$NC_PATH" "$FT_PATH"
|
||||
else
|
||||
# Same modification time, compare checksums
|
||||
echo "Verifying $DB_FILE with checksum comparison"
|
||||
rsync -av --checksum "$NC_PATH" "$FT_PATH"
|
||||
rsync -av --checksum "$FT_PATH" "$NC_PATH"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
''
|
||||
];
|
||||
RunAtLoad = true;
|
||||
StartInterval = 300; # Run every 5 minutes (300 seconds)
|
||||
StandardOutPath =
|
||||
"${config.home.homeDirectory}/Library/Logs/freetube-sync.log";
|
||||
StandardErrorPath =
|
||||
"${config.home.homeDirectory}/Library/Logs/freetube-sync-error.log";
|
||||
EnvironmentVariables = {
|
||||
PATH = "${lib.makeBinPath [ pkgs.rsync pkgs.findutils ]}:$PATH";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
programs = {
|
||||
bash = {
|
||||
profileExtra = ''
|
||||
|
|
|
|||
|
|
@ -2,9 +2,9 @@
|
|||
|
||||
imports = [
|
||||
./../programs/firefox.nix
|
||||
./../programs/ghostty.nix
|
||||
./../programs/zed.nix
|
||||
./../services/gpg-agent.nix
|
||||
./../services/syncthing.nix
|
||||
];
|
||||
|
||||
home = {
|
||||
|
|
@ -23,7 +23,7 @@
|
|||
nil
|
||||
nixd
|
||||
nixfmt-classic
|
||||
nixos-rebuild
|
||||
ollama
|
||||
quarto
|
||||
roboto-slab
|
||||
shellcheck
|
||||
|
|
@ -31,8 +31,6 @@
|
|||
woodpecker-cli
|
||||
(texlive.combine { inherit (texlive) scheme-basic latex-bin latexmk; })
|
||||
custom-pkgs.ark
|
||||
custom-pkgs.jlfmt
|
||||
custom-pkgs.runic
|
||||
];
|
||||
shellAliases = {
|
||||
code = "codium";
|
||||
|
|
|
|||
95
homes/harmony.nix
Normal file
95
homes/harmony.nix
Normal 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'
|
||||
}
|
||||
'';
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
@ -18,16 +18,115 @@ in {
|
|||
'';
|
||||
};
|
||||
};
|
||||
systemd.user = {
|
||||
services = {
|
||||
freetube-sync = {
|
||||
Unit = {
|
||||
Description = "Sync FreeTube settings with Nextcloud";
|
||||
After = [ "network.target" ];
|
||||
};
|
||||
Service = {
|
||||
Type = "oneshot";
|
||||
ExecStart = "${pkgs.writeShellScript "freetube-sync.sh" ''
|
||||
FREETUBE_CONFIG_DIR="$HOME/.var/app/io.freetubeapp.FreeTube/config/FreeTube"
|
||||
NEXTCLOUD_DIR="$HOME/Nextcloud/Settings/FreeTube"
|
||||
|
||||
# Create directories if they don't exist
|
||||
mkdir -p "$FREETUBE_CONFIG_DIR"
|
||||
mkdir -p "$NEXTCLOUD_DIR"
|
||||
|
||||
# List of database files to sync
|
||||
DB_FILES=("settings.db" "history.db" "playlists.db" "subscriptions.db" "profiles.db")
|
||||
|
||||
# Initial sync if Nextcloud has db files but FreeTube doesn't
|
||||
if [ -n "$(ls -A $NEXTCLOUD_DIR/*.db 2>/dev/null)" ] && [ ! -n "$(ls -A $FREETUBE_CONFIG_DIR/*.db 2>/dev/null)" ]; then
|
||||
echo "Performing initial sync from Nextcloud to FreeTube"
|
||||
for DB_FILE in "''${DB_FILES[@]}"; do
|
||||
if [ -f "$NEXTCLOUD_DIR/$DB_FILE" ]; then
|
||||
rsync -av --checksum "$NEXTCLOUD_DIR/$DB_FILE" "$FREETUBE_CONFIG_DIR/$DB_FILE"
|
||||
fi
|
||||
done
|
||||
# If Nextcloud is empty but FreeTube has db files, copy to Nextcloud
|
||||
elif [ ! -n "$(ls -A $NEXTCLOUD_DIR/*.db 2>/dev/null)" ] && [ -n "$(ls -A $FREETUBE_CONFIG_DIR/*.db 2>/dev/null)" ]; then
|
||||
echo "Performing initial sync from FreeTube to Nextcloud"
|
||||
for DB_FILE in "''${DB_FILES[@]}"; do
|
||||
if [ -f "$FREETUBE_CONFIG_DIR/$DB_FILE" ]; then
|
||||
rsync -av --checksum "$FREETUBE_CONFIG_DIR/$DB_FILE" "$NEXTCLOUD_DIR/$DB_FILE"
|
||||
fi
|
||||
done
|
||||
else
|
||||
# Process each database file individually
|
||||
for DB_FILE in "''${DB_FILES[@]}"; do
|
||||
FT_PATH="$FREETUBE_CONFIG_DIR/$DB_FILE"
|
||||
NC_PATH="$NEXTCLOUD_DIR/$DB_FILE"
|
||||
|
||||
# Skip if neither file exists
|
||||
if [ ! -f "$FT_PATH" ] && [ ! -f "$NC_PATH" ]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
# If only one exists, copy it
|
||||
if [ -f "$FT_PATH" ] && [ ! -f "$NC_PATH" ]; then
|
||||
echo "Copying $DB_FILE from FreeTube to Nextcloud"
|
||||
rsync -av "$FT_PATH" "$NC_PATH"
|
||||
continue
|
||||
fi
|
||||
|
||||
if [ ! -f "$FT_PATH" ] && [ -f "$NC_PATH" ]; then
|
||||
echo "Copying $DB_FILE from Nextcloud to FreeTube"
|
||||
rsync -av "$NC_PATH" "$FT_PATH"
|
||||
continue
|
||||
fi
|
||||
|
||||
# Both files exist, check which is newer
|
||||
if [ "$FT_PATH" -nt "$NC_PATH" ]; then
|
||||
echo "Syncing newer $DB_FILE from FreeTube to Nextcloud"
|
||||
rsync -av --update "$FT_PATH" "$NC_PATH"
|
||||
elif [ "$NC_PATH" -nt "$FT_PATH" ]; then
|
||||
echo "Syncing newer $DB_FILE from Nextcloud to FreeTube"
|
||||
rsync -av --update "$NC_PATH" "$FT_PATH"
|
||||
else
|
||||
# Same modification time, compare checksums
|
||||
echo "Verifying $DB_FILE with checksum comparison"
|
||||
rsync -av --checksum "$NC_PATH" "$FT_PATH"
|
||||
rsync -av --checksum "$FT_PATH" "$NC_PATH"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
''}";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
timers = {
|
||||
freetube-sync = {
|
||||
Unit = { Description = "Timer for FreeTube settings sync"; };
|
||||
Timer = {
|
||||
OnBootSec = "1m";
|
||||
OnUnitActiveSec = "5m";
|
||||
};
|
||||
Install = { WantedBy = [ "timers.target" ]; };
|
||||
};
|
||||
};
|
||||
};
|
||||
xdg = {
|
||||
configFile = {
|
||||
"plasma-workspace/env/ZED_WINDOW_DECORATIONS.sh".text =
|
||||
"export ZED_WINDOW_DECORATIONS=server";
|
||||
"dolphinrc".source =
|
||||
mkOutOfStoreSymlink "${home-manager-repo}/dotfiles/dolphinrc";
|
||||
"konsolerc".source =
|
||||
mkOutOfStoreSymlink "${home-manager-repo}/dotfiles/konsolerc";
|
||||
"onedrive/config".text = ''
|
||||
force_session_upload = "true"
|
||||
delay_inotify_processing = "true"
|
||||
'';
|
||||
"yakuakerc".source =
|
||||
mkOutOfStoreSymlink "${home-manager-repo}/dotfiles/yakuakerc";
|
||||
};
|
||||
dataFile = {
|
||||
"konsole/My Default.profile".source =
|
||||
mkOutOfStoreSymlink "${home-manager-repo}/dotfiles/MyDefault.profile";
|
||||
"kio/servicemenus/kate.desktop".source = ./../dotfiles/kate.desktop;
|
||||
"kio/servicemenus/vlc.desktop".source = ./../dotfiles/vlc.desktop;
|
||||
"kio/servicemenus/word-to-pdf.desktop".source =
|
||||
|
|
|
|||
|
|
@ -1,8 +0,0 @@
|
|||
{ ... }: {
|
||||
home = {
|
||||
username = "millironx";
|
||||
homeDirectory = "/home/millironx";
|
||||
};
|
||||
programs = { };
|
||||
services = { };
|
||||
}
|
||||
|
|
@ -14,23 +14,6 @@
|
|||
services = {
|
||||
gpg-agent = { sshKeys = [ "F72C07DBA3DC0903C3ABB55E8B460803FEC22640" ]; };
|
||||
};
|
||||
virtualisation.quadlet = {
|
||||
containers = {
|
||||
anythingllm = {
|
||||
autoStart = true;
|
||||
containerConfig = {
|
||||
image = "docker.io/mintplexlabs/anythingllm:latest";
|
||||
addHosts = [ "ollama.millironx.local:host-gateway" ];
|
||||
publishPorts = [ "3001:3001" ];
|
||||
volumes =
|
||||
[ "${config.xdg.dataHome}/anythingllm:/app/server/storage:Z" ];
|
||||
environments = { STORAGE_DIR = "/app/server/storage"; };
|
||||
|
||||
};
|
||||
};
|
||||
};
|
||||
autoUpdate.enable = true;
|
||||
};
|
||||
xdg = {
|
||||
configFile = {
|
||||
"nextflow.config".text = ''
|
||||
|
|
|
|||
|
|
@ -3,13 +3,20 @@ ungrouped:
|
|||
hosts:
|
||||
localhost:
|
||||
ansible_connection: local
|
||||
harmony:
|
||||
ansible_connection: local
|
||||
odyssey:
|
||||
ansible_connection: local
|
||||
|
||||
asahi:
|
||||
hosts:
|
||||
harmony:
|
||||
|
||||
amd64:
|
||||
hosts:
|
||||
odyssey:
|
||||
|
||||
fedora:
|
||||
hosts:
|
||||
harmony:
|
||||
odyssey:
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
}
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
{ pkgs, ... }:
|
||||
with pkgs; {
|
||||
ark = callPackage ./ark.nix { };
|
||||
jlfmt = callPackage ./jlfmt.nix { };
|
||||
runic = callPackage ./runic.nix { };
|
||||
sc4pac = callPackage ./sc4pac.nix { };
|
||||
|
||||
{
|
||||
ark = pkgs.callPackage ./ark.nix { };
|
||||
sc4pac = pkgs.callPackage ./sc4pac.nix { };
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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))));' -- "$@"
|
||||
''
|
||||
|
|
@ -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))' -- "$@"
|
||||
''
|
||||
|
|
@ -11,7 +11,7 @@
|
|||
mode: "755"
|
||||
- name: Create Firefox DNS policy
|
||||
ansible.builtin.template:
|
||||
src: "policies.json"
|
||||
src: "{{ playbook_dir }}/../templates/policies.json"
|
||||
dest: /etc/firefox/policies/policies.json
|
||||
mode: "644"
|
||||
|
||||
|
|
|
|||
|
|
@ -45,8 +45,7 @@
|
|||
register: home_manager_exists
|
||||
- name: Init home-manager
|
||||
ansible.builtin.shell: |
|
||||
/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 }}
|
||||
/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 }}
|
||||
when: not home_manager_exists.stat.exists
|
||||
register: home_manager_init
|
||||
changed_when: home_manager_init.rc == 0
|
||||
|
|
|
|||
|
|
@ -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
|
||||
become: true
|
||||
tasks:
|
||||
- name: Install dnf packages
|
||||
- name: Install common (all arch) dnf packages
|
||||
ansible.builtin.dnf:
|
||||
name:
|
||||
- chromium
|
||||
- firefoxpwa
|
||||
- fontconfig-devel
|
||||
- freetype-devel
|
||||
- fribidi-devel
|
||||
- ghostty
|
||||
- inkscape
|
||||
- jq
|
||||
- kate
|
||||
- kdenlive
|
||||
- kdiff3
|
||||
- krita
|
||||
- libdvdcss
|
||||
- libjpeg-devel
|
||||
- libpng-devel
|
||||
- libtiff-devel
|
||||
- libwebp-devel
|
||||
- mkvtoolnix
|
||||
- mpv
|
||||
- musescore
|
||||
- nextcloud-client
|
||||
- nextcloud-client-dolphin
|
||||
- obs-studio
|
||||
- onedrive
|
||||
- protontricks
|
||||
- qownnotes
|
||||
- qt
|
||||
- rssguard
|
||||
|
|
@ -37,8 +82,6 @@
|
|||
- thunderbird
|
||||
- vlc
|
||||
- vorta
|
||||
- x264
|
||||
- x264-libs
|
||||
- yakuake
|
||||
- zed
|
||||
- zsh
|
||||
|
|
@ -49,11 +92,6 @@
|
|||
name: https://downloads.sourceforge.net/project/mscorefonts2/rpms/msttcore-fonts-installer-2.6-1.noarch.rpm
|
||||
state: present
|
||||
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)
|
||||
ansible.builtin.dnf:
|
||||
name: https://github.com/r-lib/rig/releases/download/latest/r-rig-latest-1.{{ ansible_architecture }}.rpm
|
||||
|
|
@ -71,23 +109,21 @@
|
|||
name: "*"
|
||||
state: latest # noqa: package-latest
|
||||
|
||||
- name: Configure Flatpaks
|
||||
- name: Configure common (all arch) Flatpaks
|
||||
hosts: fedora
|
||||
become: false
|
||||
tasks:
|
||||
- name: Install Flatpaks
|
||||
- name: Install common (all arch) Flatpaks
|
||||
community.general.flatpak:
|
||||
name:
|
||||
- com.bitwarden.desktop
|
||||
- com.github.tchx84.Flatseal
|
||||
- com.logseq.Logseq
|
||||
- com.slack.Slack
|
||||
- dev.deedles.Trayscale
|
||||
- io.freetubeapp.FreeTube
|
||||
- io.github.alainm23.planify
|
||||
- io.github.dweymouth.supersonic
|
||||
- io.openrct2.OpenRCT2
|
||||
- org.signal.Signal
|
||||
- org.zulip.Zulip
|
||||
- net.ankiweb.Anki
|
||||
state: latest
|
||||
method: user
|
||||
remote: flathub
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
- name: Configure dnf package repositories
|
||||
hosts: fedora
|
||||
- name: Configure amd64-specific package repositories
|
||||
hosts: amd64
|
||||
become: true
|
||||
tasks:
|
||||
- name: Install RPM Fusion free repository
|
||||
|
|
@ -20,6 +20,31 @@
|
|||
- name: Install Zotero COPR repository
|
||||
community.general.copr:
|
||||
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
|
||||
ansible.builtin.yum_repository:
|
||||
name: tailscale-stable
|
||||
|
|
@ -28,6 +53,14 @@
|
|||
enabled: true
|
||||
gpgcheck: true
|
||||
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
|
||||
- name: Install RStudio copr repository
|
||||
community.general.copr:
|
||||
|
|
@ -77,7 +110,7 @@
|
|||
register: terra_priority
|
||||
changed_when: terra_priority.rc != 0
|
||||
|
||||
- name: Configure Flatpack remotes
|
||||
- name: Configure Flathub remote
|
||||
hosts: fedora
|
||||
become: false
|
||||
tasks:
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{ ... }: {
|
||||
defaults."AltTab" = {
|
||||
appearanceStyle = 0;
|
||||
nextWindowGesture = 3;
|
||||
nextWindowGesture = 1;
|
||||
screenRecordingPermissionSkipped = true;
|
||||
showFullscreenWindows = 0;
|
||||
showHiddenWindows = 1;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
{ pkgs, firefox-addons, buildFirefoxXpiAddon, lib, ... }: {
|
||||
{ firefox-addons, buildFirefoxXpiAddon, lib, ... }: {
|
||||
programs.firefox = {
|
||||
enable = true;
|
||||
package =
|
||||
|
|
@ -31,13 +31,17 @@
|
|||
};
|
||||
};
|
||||
containersForce = true;
|
||||
extensions.packages = with firefox-addons;
|
||||
[
|
||||
extensions.packages = with firefox-addons; [
|
||||
bitwarden
|
||||
multi-account-containers
|
||||
floccus
|
||||
libredirect
|
||||
old-reddit-redirect
|
||||
plasma-integration
|
||||
pwas-for-firefox
|
||||
ublock-origin
|
||||
user-agent-string-switcher
|
||||
web-archives
|
||||
zotero-connector
|
||||
(buildFirefoxXpiAddon rec {
|
||||
pname = "always_in_container";
|
||||
|
|
@ -69,10 +73,7 @@
|
|||
platforms = platforms.all;
|
||||
};
|
||||
})
|
||||
] ++ (if pkgs.stdenv.hostPlatform.isDarwin then
|
||||
[ ]
|
||||
else
|
||||
[ plasma-integration ]);
|
||||
];
|
||||
search = {
|
||||
default = "Kagi";
|
||||
privateDefault = "Milliron X Search";
|
||||
|
|
@ -245,6 +246,7 @@
|
|||
"floccus_handmadeideas_org-browser-action"
|
||||
"7esoorv3_alefvanoon_anonaddy_me-browser-action"
|
||||
"plasma-browser-integration_kde_org-browser-action"
|
||||
"firefoxpwa_filips_si-browser-action"
|
||||
"_d07ccf11-c0cd-4938-a265-2a4d6ad01189_-browser-action" # Web Archives
|
||||
"openwith_darktrojan_net-browser-action"
|
||||
"zotero_chnm_gmu_edu-browser-action"
|
||||
|
|
@ -280,6 +282,7 @@
|
|||
"floccus_handmadeideas_org-browser-action"
|
||||
"7esoorv3_alefvanoon_anonaddy_me-browser-action"
|
||||
"plasma-browser-integration_kde_org-browser-action"
|
||||
"firefoxpwa_filips_si-browser-action"
|
||||
"ublock0_raymondhill_net-browser-action"
|
||||
"_d07ccf11-c0cd-4938-a265-2a4d6ad01189_-browser-action"
|
||||
"zotero_chnm_gmu_edu-browser-action"
|
||||
|
|
|
|||
19
programs/ghostty.nix
Normal file
19
programs/ghostty.nix
Normal 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";
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
@ -1,16 +1,10 @@
|
|||
{ ... }: {
|
||||
programs.git = {
|
||||
enable = true;
|
||||
settings = {
|
||||
user = {
|
||||
name = "Thomas A. Christensen II";
|
||||
email = "25492070+MillironX@users.noreply.github.com";
|
||||
};
|
||||
core = {
|
||||
editor = "nvim";
|
||||
# git push/pull via ssh over ipv6 is slow, so force ipv4 instead
|
||||
sshCommand = "ssh -4";
|
||||
};
|
||||
userName = "Thomas A. Christensen II";
|
||||
userEmail = "25492070+MillironX@users.noreply.github.com";
|
||||
extraConfig = {
|
||||
core = { editor = "nvim"; };
|
||||
credential = { helper = "store"; };
|
||||
color = { ui = "auto"; };
|
||||
init = { defaultBranch = "master"; };
|
||||
|
|
@ -46,11 +40,6 @@
|
|||
};
|
||||
merge = { conflictstyle = "zdiff3"; };
|
||||
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/";
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
@ -1,178 +1,7 @@
|
|||
{ config, ... }: {
|
||||
|
||||
imports = [ ./konsole.nix ./yakuake.nix ];
|
||||
|
||||
programs.plasma = {
|
||||
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;
|
||||
ViewMode = 1;
|
||||
};
|
||||
"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"'') 32)
|
||||
}]";
|
||||
};
|
||||
}
|
||||
{
|
||||
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 = {
|
||||
lookAndFeel = "org.kde.breezedark.desktop";
|
||||
wallpaperFillMode = "preserveAspectCrop";
|
||||
wallpaperSlideShow = {
|
||||
interval = 86400;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{ pkgs, ... }:
|
||||
let
|
||||
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 = ''
|
||||
|
|
@ -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 {
|
||||
programs = {
|
||||
bash = {
|
||||
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\]] \\$ "
|
||||
'';
|
||||
};
|
||||
|
|
@ -79,7 +37,7 @@ in {
|
|||
"zsh-users/zsh-completions"
|
||||
];
|
||||
};
|
||||
initContent = shell_functions "zsh";
|
||||
initContent = conda_init "zsh" + nd_bash_function;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,73 +0,0 @@
|
|||
{ pkgs, lib, config, hostname, ... }:
|
||||
let
|
||||
sshIdPath = host: "~/.ssh/id_ed25519__${host}";
|
||||
tailnetConfig = host: { identityFile = sshIdPath host; };
|
||||
gitConfig = host: tailnetConfig host // { user = "git"; };
|
||||
tailnetHosts = [ "anderson" "mcentire" "bosephus" ];
|
||||
gitHosts = [ "github.com" "gitlab.com" "codeberg.org" "code.millironx.com" ];
|
||||
tailnetMatchBlocks =
|
||||
lib.genAttrs (lib.lists.remove hostname tailnetHosts) tailnetConfig;
|
||||
gitMatchBlocks = lib.genAttrs gitHosts gitConfig // {
|
||||
"code.millironx.com" = (gitConfig "code.millironx.com") // {
|
||||
proxyCommand = "ssh anderson -W localhost:2222";
|
||||
hostname = "code.millironx.com";
|
||||
};
|
||||
};
|
||||
in {
|
||||
programs.ssh = {
|
||||
enable = true;
|
||||
enableDefaultConfig = false;
|
||||
matchBlocks = {
|
||||
"*" = { identitiesOnly = true; };
|
||||
"aahz" = {
|
||||
hostname = "nistac-108-37.dhcp.ksu.edu";
|
||||
user = "tchristensen";
|
||||
identityFile = sshIdPath "aahz";
|
||||
};
|
||||
"skeeve" = {
|
||||
hostname = "129.130.108.157";
|
||||
user = "tchristensen";
|
||||
identityFile = sshIdPath "skeeve";
|
||||
};
|
||||
"ceres" = {
|
||||
hostname = "ceres.scinet.usda.gov";
|
||||
user = "thomas.christensen";
|
||||
identitiesOnly = false;
|
||||
serverAliveInterval = 20;
|
||||
serverAliveCountMax = 30;
|
||||
extraOptions = { TCPKeepAlive = "yes"; };
|
||||
};
|
||||
"atlas" = {
|
||||
hostname = "Atlas-login-1.hpc.msstate.edu";
|
||||
user = "thomas.christensen";
|
||||
identitiesOnly = false;
|
||||
serverAliveInterval = 20;
|
||||
serverAliveCountMax = 30;
|
||||
extraOptions = { TCPKeepAlive = "yes"; };
|
||||
};
|
||||
"atlas-dtn" = {
|
||||
hostname = "Atlas-dtn.hpc.msstate.edu";
|
||||
user = "thomas.christensen";
|
||||
identitiesOnly = false;
|
||||
};
|
||||
"code.millironx.com" = {
|
||||
proxyCommand = "ssh anderson -W localhost:2222";
|
||||
};
|
||||
} // tailnetMatchBlocks // gitMatchBlocks;
|
||||
};
|
||||
|
||||
home.packages = let
|
||||
# Answer no to overwrite questions
|
||||
keygen = host: ''
|
||||
yes "n" | \
|
||||
ssh-keygen \
|
||||
-t ed25519 \
|
||||
-f ~/.ssh/id_ed25519__${host} \
|
||||
-C "millironx@${hostname}" \
|
||||
-N ""
|
||||
'';
|
||||
in [
|
||||
(pkgs.writeShellScriptBin "ssh-bootstrap-keys"
|
||||
(builtins.concatStringsSep "\n" (map keygen (tailnetHosts ++ gitHosts))))
|
||||
];
|
||||
}
|
||||
|
|
@ -25,10 +25,6 @@
|
|||
bundleIdentifier = "org.mozilla.thunderbird";
|
||||
action = "launchOrActivateApp";
|
||||
}
|
||||
{
|
||||
bundleIdentifier = "com.microsoft.Outlook";
|
||||
action = "launchOrActivateApp";
|
||||
}
|
||||
{
|
||||
bundleIdentifier = "dev.zed.Zed";
|
||||
action = "launchOrActivateApp";
|
||||
|
|
@ -38,13 +34,11 @@
|
|||
action = "launchOrActivateApp";
|
||||
}
|
||||
{
|
||||
# Instinct dashboard
|
||||
bundleIdentifier =
|
||||
"com.apple.Safari.WebApp.2F51A6D0-087A-438F-92D3-A73FE09CB4CC";
|
||||
action = "launchOrActivateApp";
|
||||
}
|
||||
{
|
||||
# Carestream
|
||||
bundleIdentifier =
|
||||
"com.apple.Safari.WebApp.5EC6478E-03A6-4147-8A4D-6EF3DE3F06D3";
|
||||
action = "launchOrActivateApp";
|
||||
|
|
|
|||
|
|
@ -1,7 +0,0 @@
|
|||
{ pkgs, ... }: {
|
||||
programs.tmux = {
|
||||
enable = true;
|
||||
terminal = "tmux-256color";
|
||||
plugins = with pkgs.tmuxPlugins; [ tmux-powerline ];
|
||||
};
|
||||
}
|
||||
|
|
@ -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;
|
||||
};
|
||||
}
|
||||
|
|
@ -1,11 +1,9 @@
|
|||
{ pkgs, hostname, ... }: {
|
||||
{ ... }: {
|
||||
programs.zed-editor = {
|
||||
enable = true;
|
||||
package = null;
|
||||
extensions = [
|
||||
"basher"
|
||||
"clean-vscode-icons"
|
||||
"caddyfile"
|
||||
"clojure"
|
||||
"dockerfile"
|
||||
"earthfile"
|
||||
|
|
@ -25,74 +23,42 @@
|
|||
use_modifier_to_send = true;
|
||||
default_model = {
|
||||
provider = "zed.dev";
|
||||
model = "claude-sonnet-4-5";
|
||||
model = "claude-3-7-sonnet";
|
||||
};
|
||||
default_profile = "minimal";
|
||||
};
|
||||
buffer_font_family = "FiraCode Nerd Font";
|
||||
buffer_font_size = 11;
|
||||
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 = {
|
||||
Caddyfile = {
|
||||
tab_size = 2;
|
||||
formatter.external = {
|
||||
command = "${pkgs.caddy}/bin/caddy";
|
||||
arguments = [ "fmt" "-c" "-" ];
|
||||
Julia = {
|
||||
formatter = {
|
||||
external = {
|
||||
command = "julia";
|
||||
arguments = [
|
||||
"--project=@JuliaFormatter"
|
||||
"--startup-file=no"
|
||||
"-e"
|
||||
"using JuliaFormatter; print(format_text(String(read(stdin))));"
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
Julia = { formatter = { external = { command = "jlfmt"; }; }; };
|
||||
LaTeX = {
|
||||
formatter = {
|
||||
external = {
|
||||
command = let
|
||||
latexindent = (pkgs.texlive.combine {
|
||||
inherit (pkgs.texlive) scheme-minimal latexindent;
|
||||
});
|
||||
in "${latexindent}/bin/latexindent";
|
||||
arguments = [ "-m" "-tt" "-l" "-" ];
|
||||
command = "tex-fmt";
|
||||
arguments = [ "--stdin" ];
|
||||
};
|
||||
};
|
||||
};
|
||||
Nix = {
|
||||
formatter.external.command = "${pkgs.nixfmt-classic}/bin/nixfmt";
|
||||
};
|
||||
};
|
||||
lsp = {
|
||||
nil = { settings.nix.flake.autoArchive = true; };
|
||||
nixd = {
|
||||
settings.options.home-manager.expr = ''
|
||||
(builtins.getFlake (builtins.toString ~/.config/home-manager)).homeConfigurations."millironx@${hostname}".options'';
|
||||
};
|
||||
texlab = {
|
||||
settings.texlab = {
|
||||
build = {
|
||||
onSave = true;
|
||||
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" ];
|
||||
};
|
||||
};
|
||||
nil = {
|
||||
initialization_options.formatting.command = [ "nixfmt" ];
|
||||
settings.nix.flake.autoArchive = true;
|
||||
};
|
||||
tinymist = {
|
||||
initialization_options = { preview.background.enabled = true; };
|
||||
settings = {
|
||||
exportPdf = "onSave";
|
||||
outputPath = "$root/$name";
|
||||
|
|
@ -115,28 +81,5 @@
|
|||
ui_font_size = 16;
|
||||
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;
|
||||
}
|
||||
];
|
||||
};
|
||||
}
|
||||
|
|
|
|||
53
secrets.nix
53
secrets.nix
|
|
@ -6,58 +6,25 @@ let
|
|||
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIxTfeg+GZsfmG8TuEV1xW1gXknAIKzZ3UjZ3guRY+EW root@nixos";
|
||||
bosephus-millironx =
|
||||
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKaDPqRJHoqgY2pseh/mnhjaGWXprHk2s5I52LhHpHcF millironx@bosephus";
|
||||
corianne-host =
|
||||
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHKKkucebeb1GcerOZAAs5GQsgTS8kXw5W41b9Fy9+hp root@corianne.local";
|
||||
odyssey-millironx =
|
||||
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIN9Aj7BtQp1Roa0tgopDrUo7g2am5WJ43lO1d1fDUz45 millironx@odyssey";
|
||||
corianne-millironx =
|
||||
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOgL2lO9RJBdQYANoxGyWXcNKi5/NZkRHHo/rNqaYMc/ millironx@corianne";
|
||||
mcentire-host =
|
||||
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINT51tQgsKzTIQc9WSQj01h/gPRvAD3k9jRhXppY7xmd root@nixos";
|
||||
mcentire-millironx =
|
||||
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOdC6eNx2nBi3PWK/n4GJMbVf+NlQJv13aUqxse/h1kL millironx@mcentire";
|
||||
odyssey-millironx =
|
||||
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKM5Q2zl3b91j+foqcVeQT+wb5DFEp+MbgotTTaKqZZi millironx@odyssey";
|
||||
harmony-millironx =
|
||||
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFBYxsCkw+ObDzIvU8z/rSlYcQx0JIt1bCVxKcDxeNNZ millironx@harmony";
|
||||
|
||||
system-administrators = [
|
||||
anderson-millironx
|
||||
bosephus-millironx
|
||||
corianne-millironx
|
||||
mcentire-millironx
|
||||
odyssey-millironx
|
||||
corianne-millironx
|
||||
harmony-millironx
|
||||
];
|
||||
|
||||
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/fireflyiii.toml.age".publicKeys = system-administrators
|
||||
++ [ mcentire-host ];
|
||||
"secrets/freshrss.toml.age".publicKeys = system-administrators
|
||||
++ [ mcentire-host ];
|
||||
"secrets/immich.toml.age".publicKeys = system-administrators
|
||||
++ [ mcentire-host ];
|
||||
"secrets/millironx-books-s3.age".publicKeys = system-administrators
|
||||
++ [ mcentire-host ];
|
||||
"secrets/millironx-music-s3.age".publicKeys = system-administrators
|
||||
++ [ mcentire-host ];
|
||||
"secrets/millironx-photos-s3.age".publicKeys = system-administrators
|
||||
++ [ mcentire-host ];
|
||||
"secrets/navidrome.toml.age".publicKeys = system-administrators
|
||||
++ [ mcentire-host ];
|
||||
"secrets/network-information.age".publicKeys = system-administrators
|
||||
++ [ bosephus-host ];
|
||||
"secrets/peertube.toml.age".publicKeys = system-administrators
|
||||
++ [ mcentire-host ];
|
||||
"secrets/redis-immich-password.age".publicKeys = system-administrators
|
||||
++ [ mcentire-host ];
|
||||
"secrets/redis-password.age".publicKeys = system-administrators
|
||||
++ [ mcentire-host ];
|
||||
"secrets/redis-peertube-password.age".publicKeys = system-administrators
|
||||
++ [ mcentire-host ];
|
||||
"secrets/vaultwarden.toml.age".publicKeys = system-administrators
|
||||
++ [ mcentire-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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
age-encryption.org/v1
|
||||
-> ssh-ed25519 il3lzQ SoqTRahd/xUVe/pDmQI3jT5X0lTxOwhy8hct20fwil4
|
||||
UuYpQa5GpDALKQEMbSLnH2rp3kgPL+4zJZbJirkB5Q0
|
||||
-> ssh-ed25519 1g/xww gYtrdwJlp1pxcZ7l+RvawRScFOh/ami3yJobbKPyLiM
|
||||
oQ8dwIUIakV8WVvuknn87BYcmEBBT+UI3BxImd71/jE
|
||||
-> ssh-ed25519 dbKeHw KVdpRmBWRYd5NC3UK3e8Em3XefnVxxc9KfXzbksvTC0
|
||||
fwyK7ORnCadX8Czs0WKW3ZDa1jeu7Wjba7Vm9nKqCBs
|
||||
-> ssh-ed25519 3qPtug nEHt7lMjUrcehOGjtznFd+u60OdfG1dPJr/+aFP4OXY
|
||||
DRtfbYQZJhMIfj1yHgpTdSU15z0Ld5/2wl0ATPN0W6w
|
||||
-> ssh-ed25519 FRQvIA a4zYtpYFVGSum3wD850lM/wEcokckt4mlDRsrb6QrHY
|
||||
QF75fGiiKZc1oHSOdy+QvPnQ1hhaLRebaE7i0keZxHw
|
||||
--- 8Y5Q9SlGWEzp9jeVcoHFrv3PR+eDyhOPFXIFRxZ1Koo
|
||||
‘ÜJáżŢóóDlv\é÷÷lĹu$0 ëë±{<7B>Ďri·śP7ĎĄPÚ锓…Äkr(ÇPˇĹHWË]r ĚĆŮ‘r¸
|
||||
-> ssh-ed25519 il3lzQ Ni2CHjeijXHfF62cUqVTm8MAOn6rRg8UrhqN6xvdkyk
|
||||
DsT0Ysx88FlCLeRzoOGctX7KqatX9/UCr5WdtdLJAf4
|
||||
-> ssh-ed25519 1g/xww jRn91F29sISMyi41anAlzVCzt1t1DnUqxtryqkTQPlM
|
||||
cysgZLQR0YhiJYXBl59DjKbm+N8FnjA46wkQtnAzBFA
|
||||
-> ssh-ed25519 +kBihw t6wlSnDKGgSzGhNJnryXVbDR40DATaV3fHovtI/u7zo
|
||||
zOyCZtzfLKeer9K6SMpfTxn6El4HB7gQFQqLOxIYB5U
|
||||
-> ssh-ed25519 dbKeHw cn+8WTwis58bYm2pfEra6LeLvzEZ8GhZrOEeN+kkhCM
|
||||
fnlUAj8JtG8+r7Cj8xYUgF+JM6Pwqawn4sGI1LOeN78
|
||||
-> ssh-ed25519 Svnssw zmDBR8TdRZ9NzNhwPYRN6c8naTxAkULyUZpKgk7Gshk
|
||||
0XCwpegEIlGXhnzLLUtmciKQiYiZRgnSOSvCcYeXXk8
|
||||
--- D/lZ36n5sVste2NWfdOx8/klPh0CTmMjVQN74KIqDRY
|
||||
]%得ヌC}<7D>弡鶲ネモ"vホネ#<23>アェ釋「tュ、ワ_Q;^*!サ+<+ア瑁詞ネdBラ/Kメ<4B>ノ`
|
||||
Binary file not shown.
Binary file not shown.
|
|
@ -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Û³(óŽòœ‹Uß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.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
15
secrets/pihole.age
Normal file
15
secrets/pihole.age
Normal 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¬?kG‘lQK¬4c&*JöÂå9V_Ä0ÕöµzÓ+ɰ¯CÞ
ÑžÀQ°ò«ÃGô§XÁ˜k¡g*£æð
-Àjƒö¼‚l½ú3¿‰1ÏJš´üM·OE†[=S
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
age-encryption.org/v1
|
||||
-> ssh-ed25519 il3lzQ Lqt1JIJtjTwggbhuJj/vG65IbDyr4EK5pOOKtVqdN3s
|
||||
OBvcpByhzHT+0fmvLpgOCQaTbudVEdNmNDCJCf+9Tzs
|
||||
-> ssh-ed25519 1g/xww b1Gu6eFxAJwd+8P38D8HoEzApocQDwRpi7GF4tQH+zE
|
||||
Js9XmUUcV4gGHvE37j6I3J1vYf7gkxOLbfPC2al0EsU
|
||||
-> ssh-ed25519 dbKeHw nNL9fDDfudbcHOshRKuxtcaZzPNbTEY2z4jAQMNYQTE
|
||||
Xrz8nKSxzaEnAYeZTust6eZNhILs4dOutAaIfZ4wcGM
|
||||
-> ssh-ed25519 3qPtug JUTgaW4s68Gtr/kl+L4FLRldW97kk6vaALLSN3IgY24
|
||||
cyTIvPurE28TsE8Axl4x05OVcgEX7qA9X00B6u22/LI
|
||||
-> ssh-ed25519 FRQvIA CYliqiKWpSXwHWteHCyPxDjaVgx3tYH3+OXYtH+HAEM
|
||||
iSmpVhv01x+g/bN0TpbeN5210YsuAKTWdEJy34lulw4
|
||||
-> ssh-ed25519 +C0WRg 5O+LJgQPccaXNPvB/eRANbmq+4A5wNTGSwC6cCAecw4
|
||||
EkLahciiQbR8MmgJptjPVi2R/lMbXkZJYIFvOk4v5Vk
|
||||
--- EcIUdYgRgZ1xHmTX+O1ULpDJKloyFquwZqFLHj1Lf4E
|
||||
ô,y-]Ð@Ã5
|
||||
ôHÄÌíÎÚ€>á\ØÄ= O=,%õö»ZöÙ)q6É3©1ÔV¡l]µâ8wVè†s
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
age-encryption.org/v1
|
||||
-> ssh-ed25519 il3lzQ 9+/0+lCYWH9ibqY7J0OWz4fyTJYNvjwVEQQgByg8eXo
|
||||
DmqfjZYNCggmNwez+H0lNUmTuBUmlfOy9BMdo/mP1Ro
|
||||
-> ssh-ed25519 1g/xww 3v7NGaDyYM/Na5DTgbpv7DtVg6mqsgqd0xU7NmcDlTk
|
||||
7Z8yymlYYx3fO8CNMcosa6hLAZ7rlkDA6qPat2IWsS0
|
||||
-> ssh-ed25519 dbKeHw WlHTnJR3TyAuYm5NG+8WMXmeB3YUydDJ61SrWKMcByg
|
||||
k5dwVAuUKNjFeCY7/L28Kx0ZHBiPUxndEQp1UyOPxIY
|
||||
-> ssh-ed25519 3qPtug YU9PSuEkJElN4EOFQTrXBrRB/3g/MzSbLEU5mWIl6CA
|
||||
HBFN2uTQo0tr8cnJ2okFibZXVouaCy6WCq0+YK2jSYI
|
||||
-> ssh-ed25519 FRQvIA ufWjt9KuGr9Wx0kRbMK3WapeyOM9dnuy8nRpa9LcElY
|
||||
WGB7xIuZOEGNlycHAlg3sE1W766/rMZGIb17VL54uYs
|
||||
-> ssh-ed25519 +C0WRg 9jXj4AKdCK5RdQOOmDFu9/RapWvQQ6MieaHO7m/wKSc
|
||||
vkdJWxmBtjmLQXq1tvM+sAXAOKbJmTdM7klyiW1xW2g
|
||||
--- lEbO55yAuhIUejggLmlQ9UAZdw8DQVC+2EZ6Qs0Vn9Y
|
||||
8ÿ¸È/ÚW"F™ÓJ[DÉ<(O²9ª;‘lj³ô¯špM^À“Ò·•/Iù|9*…Øòx@ɪ±›˜¦Á›Ea¦:™Ký¥*<2A>íÌ~
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
age-encryption.org/v1
|
||||
-> ssh-ed25519 il3lzQ BiSY+7dxt1jVDqysxYAqAGAXQ0SurqZXGgGqvwaQEXM
|
||||
gzdbiaAo0nzVOIFEjAoKXSHfs+JQAbJWDJyrO4tt6gA
|
||||
-> ssh-ed25519 1g/xww 3MK5439qn8MkP5HrLLkY6a+IRGrjVIIe/OhaZ20THU0
|
||||
dKe0EkynB0G1zAZZ+VA/Of1x4Q6j+R4al7ohb6V6YWM
|
||||
-> ssh-ed25519 dbKeHw GfX7az0oBf1Nh6YQFqZuDrCZyK1Mm6w+SGAhQSGeJww
|
||||
kgLWmzfZjBnds1fjAVme6M7fKD4NrJZ3X6vJU7055ac
|
||||
-> ssh-ed25519 3qPtug zUXACZya4MCbMyPLR59VN4GRQSOokgORvDhL/ByWbHM
|
||||
dSfQVAGj8u+7L7GX53dWbs5rHo9H5TaVRwEFZUq3lJ8
|
||||
-> ssh-ed25519 FRQvIA z2qGPHCG5wGlmmngy2d4L8kHz7pO/F4YPAWwsNjWQEI
|
||||
HIVGXAxOfD7O8yP2piOfMLTM9EzWkHslCRFIf+XrXXY
|
||||
-> ssh-ed25519 +C0WRg BhTldebU1YLAxlQ5dYG/RuExT9czS//LUXwKg+Q7tT8
|
||||
mEiv5g1PWgdwZHrlqrv5wEVWvubsppUpryEwtdSNDXM
|
||||
--- 2+fbTKQ8yZPOKxLvYhFO2laMF/gq2cWs7byzYOcuohM
|
||||
™hçž ÕDVšÛ/0ØÀõ;î»<C3AE><C2BB>
îO§Ísý7ŽwrVáze¹½üÅÓ”WgŠ•°Ë<C2B0>‡ëüvl}í
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
age-encryption.org/v1
|
||||
-> ssh-ed25519 il3lzQ 8JViVLQl/Y6buE2yIo/k9QhictchYRpvx3U+TtV88RY
|
||||
bWMzX1yYCP11aGg85fCMEf3o00Ej6VeLrFW2fHyv9zo
|
||||
-> ssh-ed25519 1g/xww eDRfglbO+tyPhpXhl8RwI1f5CCocjtwpT5QqmpYab1I
|
||||
s45uOqmgA1TnCthPASC5cA2pEBzriCt69noN3aHZTvg
|
||||
-> ssh-ed25519 dbKeHw PPnqWUiu/mwpTxRkXvNwELP2F4cETQdVgXL/SgyTMAE
|
||||
mYQ1s393jTPVE5euXnq+a5Hs+nGG1VC3iaOb/6Mg6sQ
|
||||
-> ssh-ed25519 3qPtug z3Ho5K7RQD8mbFRq+rSC7mRDs5M/Kkb/O7z/ssdFLgg
|
||||
6qmYTtOz7l9nRl8yEReS2iI52H+bYxjMJfCFwYWOqn8
|
||||
-> ssh-ed25519 FRQvIA HZxjrRSKr+ofIai2x2tskTaBIrRfI+uJYISU0Wj0B1Y
|
||||
tKbposXaqYHF6H7dvoG1iYGT8FOIkX8EF0viCHUvjdQ
|
||||
-> ssh-ed25519 +C0WRg 9GCzNbR9ZbTypzrkXWVBAC3LiIwl8ypxpG+DcWy1QU4
|
||||
BlQrkO8GchHE5e275o4Hk5fmTv2RIUtW71tOS+sCsZA
|
||||
--- Fh46aD+7L1fe5O2BxitKMtJV+QgdbXhJ4O3a+0IhjP4
|
||||
"nmQ'*°y¸ç·Ë}TÅ¿]u‘k^@Yʪqjñ×ß8H#$Jþ¤NÕŒ…·OSЛÄ4<C384>½Lš@'%C<ÞŒ\ømƒ‰¨
|
||||
ªjI<>3ä‰0^"e'èû…:Ó;Ã6! våe‹â–œÈ
~˜hã@@†¥¿4ÈåêÒƒaçæõËìMÔMçœ(œ0÷œ¶yðq³î3˜\žØ£ô¨¾Ž
Ì2ÒÆ¿ºÎ¾0_¢ð€]ÌÛ¸<C39B>]nbR?ˆ@ƒD½?™RaEç©Ìòz-ŸÇÓimîÍq^SYå»)“Síðwqf=acKB¤Ng‹9¿®<C2BF>)‘Q9ØNÓ×aäëkHi+ŸúÕ~4&{l5Þ9Çùb̀¬3(žß(¼‘òy0¯,çÿV-»E:Ó}Ê¡È,´IBP ì¦<C3AC>&”jÕl¶C}ð˜àYy•QRûä<C3BB>PäÞ©Ñ„,uC§µ ž}‰UP<55>½”.Mø-D<>ïÀÑÀÒÈóLúùÆÖLywbÚ<0C>È2<C388>k$˜Å.Ë,c#ÎÇæÀ“±\ÅïbAfp*€K¸`-T6ØN³-ºhdŸoþ¡Î\1EÜãäd:¾«.‰á“?`³¿jt->ó17ïÍ:÷ˆa]aŽˆ6V8<56>:íô~[×›QùfõHÏ€{_ æ§Ú.Ž=ü_¬gÄm<îŒÏ<C592>³%«Ž‹'ÝS;{
ch›!Ið'äB)ÈóãÅ\®sÀacȲ â‚Ê:{Í2ÐH.+5Ã$†0÷”xO¦ÈëîÃ>ͲfÒ«~¼±JF HÂ3=*/~ý,§™nJ%È'¼ÛË45<07>†–G™ÒõO]ÿ94)y
|
||||
Å´‚.8<EFBFBD>$õ(–h«•³¥9<C2A5>–ú”¦ìÿÞûšRMyÆ‘•#âX©‹0Ç<30>Š]“ÿûÜD±Èl²(¾–ì1ˆM_Ëâ+!8ÛuŠtW‡ÊÚ=O«ÏÇWôÙí¹Ùð2*›¿cåÜ4Â\tSÆTÉ–_´žJÎP¤³”ú¯1VçLÿdŒ`“0ÊgF¶Êƒ÷‹‚&ÙYtÒ\kgþëE1BQ —D·–逃DõGMO¾4û!Jt<4A>C6>óÙd[êá1<>ú7<䙋’˜.I¨ô8Þnood¼¹ÍYâù}΢eBR»¯Ç2A°$^ÄOœˆÇ•bÎ>cˆ3-%â”z|€¹è;ar<§S.=„nÆ;
|
||||
|
|
@ -1,8 +1,10 @@
|
|||
$ANSIBLE_VAULT;1.1;AES256
|
||||
33613635643765623937663135313833396162343134383466343966333964386364356134663264
|
||||
3137633339396462633431316634623834646437646162360a626564313831373761636161656232
|
||||
35316566336232666336646231356665366633303530623961666465366163306166623336656364
|
||||
3835353035333031620a633332376237336530343134623832363534383761616564616138363766
|
||||
30306361383462353361636161636335313461313835663362393839623735313738316465656537
|
||||
66396635323432376530346532353238346139376261366237343763373535623364633731323830
|
||||
333730373965613131336166626230333263
|
||||
65366137313461383534313965646333656565353061336361363661613033393264353661346337
|
||||
3838653162383134393463323631613439373663396363380a633339396236363962313333343465
|
||||
31623961393532666136616438633734366261353866383264323730383432326635626637343739
|
||||
3235313062623637380a386235316437396534353261383832643165316565386263396664363962
|
||||
62393364333335373631356161373263313930343565626433383539373030363662353630633933
|
||||
63336333613965653635313637336437653139616564313861336332323739653865383531356233
|
||||
31373530343766343131346663656566363038643230343462336332323135323337353539303763
|
||||
33366638393064323431323636346161343936643062323861313766613264336465326132333631
|
||||
33306666383561653965303539313366653030663330393363363565333439383133
|
||||
|
|
|
|||
6
secrets_harmony.enc
Normal file
6
secrets_harmony.enc
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
$ANSIBLE_VAULT;1.1;AES256
|
||||
38383539613238613864336630316433666436623334313334393762396536663530336264306661
|
||||
3338616565316138616666343862366638643134343931320a633366363539326461346636373738
|
||||
66393138653463663536313065623332383166386332303564323939336630333163623637386434
|
||||
6538393966633731660a616437356233643234363562366433663437383439326161353330356331
|
||||
63346432663036353332303266343361346266396437396131376531303265356233
|
||||
|
|
@ -1,9 +1,6 @@
|
|||
$ANSIBLE_VAULT;1.1;AES256
|
||||
61363033383536303833366237323662663236313163663033306138383162383062643830616466
|
||||
6531636430613462646161343939343363663533373737340a613433363666353432383463356439
|
||||
33656266633131336565613433653062656563656637656464346232656238646339303961373265
|
||||
6639643637303433380a393163366331373964353261383662656664643031626432366231346332
|
||||
34303964346137616233343930333331306363326332383465653163386539306430303965316437
|
||||
30343333373565623431653436653832356366343937653136346535316166383262623730343831
|
||||
62376532346237323465653261316339353034323633623632313630666531373839633665333637
|
||||
34356162356565396564
|
||||
30343638643335363463653231623566623961613534323261393639623865633964653634333562
|
||||
3838613035393661656362383736313561366466396439390a383162366362643364636335613664
|
||||
39646137666437353762363764373562393736626530333336626261366232383063633732623238
|
||||
6531633638366335640a363461383535646663316533386137323966326237373836363561323462
|
||||
66646635383137333834363165666365366235333734646364616637383363666239
|
||||
|
|
|
|||
|
|
@ -1,81 +0,0 @@
|
|||
{ config, pkgs, home-manager-quadlet-nix, ... }:
|
||||
let
|
||||
user = "audiobookshelf";
|
||||
port = "28346";
|
||||
stateDirectory = "/var/lib/${user}";
|
||||
in {
|
||||
age.secrets = {
|
||||
millironx-books-s3-token.file = ./../secrets/millironx-books-s3.age;
|
||||
};
|
||||
|
||||
environment.systemPackages = [ pkgs.s3fs ];
|
||||
|
||||
fileSystems."millironx-books" = {
|
||||
device = "millironx-books";
|
||||
mountPoint = "/mount/s3/millironx-books";
|
||||
fsType = "fuse./run/current-system/sw/bin/s3fs";
|
||||
noCheck = true;
|
||||
options = [
|
||||
"_netdev"
|
||||
"allow_other"
|
||||
"use_path_request_style"
|
||||
"url=https://us-east-1.linodeobjects.com/"
|
||||
"passwd_file=${config.age.secrets.millironx-books-s3-token.path}"
|
||||
"uid=${user}"
|
||||
"gid=${user}"
|
||||
"umask=0022"
|
||||
];
|
||||
};
|
||||
|
||||
systemd.tmpfiles.rules =
|
||||
map (d: "d ${stateDirectory}/${d} 1775 ${user} ${user} -") [
|
||||
""
|
||||
"config"
|
||||
"metadata"
|
||||
];
|
||||
|
||||
services.borgmatic.configurations."${config.networking.hostName}" = {
|
||||
source_directories =
|
||||
map (d: "${stateDirectory}/${d}") [ "config" "metadata" ];
|
||||
};
|
||||
|
||||
services.caddy.virtualHosts."books.millironx.com".extraConfig = ''
|
||||
reverse_proxy http://127.0.0.1:${port}
|
||||
'';
|
||||
|
||||
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 = {
|
||||
autoUpdate.enable = true;
|
||||
containers.audiobookshelf = {
|
||||
autoStart = true;
|
||||
containerConfig = {
|
||||
image = "ghcr.io/advplyr/audiobookshelf:latest";
|
||||
environments = { TZ = "America/New_York"; };
|
||||
volumes = [
|
||||
"/mount/s3/millironx-books/audiobooks:/audiobooks:U"
|
||||
"/mount/s3/millironx-books/podcasts:/podcasts:U"
|
||||
"${stateDirectory}/config:/config:U"
|
||||
"${stateDirectory}/metadata:/metadata:U"
|
||||
];
|
||||
publishPorts = [ "127.0.0.1:${port}:80" ];
|
||||
addHosts = [ "auth.millironx.com:host-gateway" ];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -1,233 +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}/data 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}/data"
|
||||
"${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}" = { };
|
||||
|
||||
services.crowdsec = {
|
||||
localConfig.acquisitions = [{
|
||||
source = "journalctl";
|
||||
journalctl_filter = [ "_SYSTEMD_USER_UNIT=${user}.service" ];
|
||||
labels.type = "authentik";
|
||||
}];
|
||||
hub.collections = [ "firix/authentik" ];
|
||||
};
|
||||
|
||||
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:2026.2";
|
||||
environments = {
|
||||
AUTHENTIK_POSTGRESQL__HOST = "authentik-db";
|
||||
AUTHENTIK_POSTGRESQL__NAME = "${user}";
|
||||
AUTHENTIK_POSTGRESQL__USER = "${user}";
|
||||
AUTHENTIK_STORAGE__BACKEND = "s3";
|
||||
};
|
||||
exec = "worker";
|
||||
secrets = [
|
||||
"AUTHENTIK_POSTGRESQL__PASSWORD,type=env"
|
||||
"AUTHENTIK_SECRET_KEY,type=env"
|
||||
"AUTHENTIK_STORAGE__S3__ACCESS_KEY,type=env"
|
||||
"AUTHENTIK_STORAGE__S3__SECRET_KEY,type=env"
|
||||
"AUTHENTIK_STORAGE__S3__BUCKET_NAME,type=env"
|
||||
"AUTHENTIK_STORAGE__S3__REGION,type=env"
|
||||
"AUTHENTIK_STORAGE__S3__ENDPOINT,type=env"
|
||||
"AUTHENTIK_STORAGE__S3__CUSTOM_DOMAIN,type=env"
|
||||
];
|
||||
volumes = [
|
||||
# Remount media folder into new location based on
|
||||
# <https://docs.goauthentik.io/releases/2025.12/#storage-improvements>
|
||||
"${state-directory}/data:/data:U"
|
||||
"${state-directory}/media:/data/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:2026.2";
|
||||
environments = {
|
||||
AUTHENTIK_POSTGRESQL__HOST = "authentik-db";
|
||||
AUTHENTIK_POSTGRESQL__NAME = "${user}";
|
||||
AUTHENTIK_POSTGRESQL__USER = "${user}";
|
||||
AUTHENTIK_STORAGE__BACKEND = "s3";
|
||||
};
|
||||
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"
|
||||
"AUTHENTIK_STORAGE__S3__ACCESS_KEY,type=env"
|
||||
"AUTHENTIK_STORAGE__S3__SECRET_KEY,type=env"
|
||||
"AUTHENTIK_STORAGE__S3__BUCKET_NAME,type=env"
|
||||
"AUTHENTIK_STORAGE__S3__REGION,type=env"
|
||||
"AUTHENTIK_STORAGE__S3__ENDPOINT,type=env"
|
||||
"AUTHENTIK_STORAGE__S3__CUSTOM_DOMAIN,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;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
@ -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;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
{ config, ... }: {
|
||||
services.caddy = {
|
||||
enable = true;
|
||||
logFormat = "level INFO";
|
||||
};
|
||||
|
||||
services.crowdsec = {
|
||||
localConfig.acquisitions = [{
|
||||
filenames = [ "${config.services.caddy.logDir}/*.log" ];
|
||||
labels.type = "caddy";
|
||||
}];
|
||||
|
||||
hub.parsers = [ "crowdsecurity/caddy-logs" ];
|
||||
};
|
||||
}
|
||||
|
|
@ -1,9 +1,28 @@
|
|||
{ 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 = {
|
||||
crowdsec = {
|
||||
enable = true;
|
||||
localConfig = {
|
||||
acquisitions = [
|
||||
settings = {
|
||||
api.server = { listen_uri = crowdsec-url; };
|
||||
allowLocalJournalAccess = true;
|
||||
crowdsec_service.acquisition_path = pkgs.writeText "acquisitions.yaml"
|
||||
(toMultiYAML [
|
||||
{
|
||||
source = "journalctl";
|
||||
journalctl_filter = [ "_SYSTEMD_UNIT=sshd.service" ];
|
||||
|
|
@ -17,9 +36,36 @@
|
|||
filenames = [ "/var/log/syslog" "/var/log/kern.log" ];
|
||||
labels.type = "syslog";
|
||||
}
|
||||
];
|
||||
]);
|
||||
};
|
||||
hub = {
|
||||
};
|
||||
crowdsec-firewall-bouncer = {
|
||||
enable = true;
|
||||
settings = {
|
||||
api_url = firewall-bouncer-name;
|
||||
api_key = firewall-bouncer-key;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
systemd.services.crowdsec.serviceConfig = {
|
||||
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"
|
||||
|
|
@ -29,23 +75,16 @@
|
|||
"crowdsecurity/sshd"
|
||||
"crowdsecurity/whitelist-good-actors"
|
||||
];
|
||||
};
|
||||
settings = {
|
||||
general = { api.server.enable = true; };
|
||||
# See https://github.com/NixOS/nixpkgs/issues/445342
|
||||
lapi.credentialsFile = "/var/lib/crowdsec/lapi-credentials.yaml";
|
||||
};
|
||||
autoUpdateService = true;
|
||||
};
|
||||
collection-script = pkgs.writeScriptBin "install-collections" ''
|
||||
#!${pkgs.runtimeShell}
|
||||
set -eu
|
||||
set -o pipefail
|
||||
|
||||
crowdsec-firewall-bouncer = {
|
||||
enable = true;
|
||||
registerBouncer.enable = true;
|
||||
${pkgs.lib.concatMapStrings collection-check collections}
|
||||
'';
|
||||
in [
|
||||
"${bouncer-script}/bin/register-bouncer"
|
||||
"${collection-script}/bin/install-collections"
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
users.users."${config.services.crowdsec.user}".extraGroups = [ "adm" ];
|
||||
|
||||
systemd.tmpfiles.rules = let cfg = config.services.crowdsec;
|
||||
in [ "d /var/lib/crowdsec 0755 ${cfg.user} ${cfg.group}" ];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,164 +0,0 @@
|
|||
{ config, pkgs, home-manager-quadlet-nix, ... }:
|
||||
let
|
||||
user = "fireflyiii";
|
||||
port = "34733";
|
||||
containerPort = "8080";
|
||||
authentikPort = "9000";
|
||||
stateDirectory = "/var/lib/${user}";
|
||||
servicePaths = [ "upload" ];
|
||||
databasePaths = [ "database" ];
|
||||
in {
|
||||
age.secrets."fireflyiii.toml" = {
|
||||
file = ./../secrets/fireflyiii.toml.age;
|
||||
owner = user;
|
||||
};
|
||||
|
||||
millironx.podman-secrets.fireflyiii = {
|
||||
inherit user;
|
||||
secrets-files = [ config.age.secrets."fireflyiii.toml".path ];
|
||||
};
|
||||
|
||||
systemd.tmpfiles.rules =
|
||||
map (d: "d ${stateDirectory}/${d} 1775 ${user} ${user} -")
|
||||
([ "" ] ++ servicePaths ++ databasePaths);
|
||||
|
||||
services.borgmatic.configurations."${config.networking.hostName}" = {
|
||||
source_directories = map (d: "${stateDirectory}/${d}") servicePaths;
|
||||
postgresql_databases = [{
|
||||
name = user;
|
||||
psql_command =
|
||||
"/run/wrappers/bin/sudo -iu ${user} ${pkgs.podman}/bin/podman exec ${user}-db psql --username=${user}";
|
||||
pg_dump_command =
|
||||
"/run/wrappers/bin/sudo -iu ${user} ${pkgs.podman}/bin/podman exec ${user}-db pg_dump --username=${user}";
|
||||
pg_restore_command =
|
||||
"/run/wrappers/bin/sudo -iu ${user} ${pkgs.podman}/bin/podman exec ${user}-db pg_restore --username=${user}";
|
||||
}];
|
||||
};
|
||||
|
||||
services.caddy.virtualHosts."money.millironx.com".extraConfig = ''
|
||||
reverse_proxy /outpost.goauthentik.io/* http://127.0.0.1:${authentikPort}
|
||||
@protected not path /oauth/* /api/*
|
||||
forward_auth @protected http://127.0.0.1:${authentikPort} {
|
||||
uri /outpost.goauthentik.io/auth/caddy
|
||||
copy_headers X-Authentik-Username X-Authentik-Groups X-Authentik-Entitlements X-Authentik-Email X-Authentik-Name X-Authentik-Uid X-Authentik-Jwt X-Authentik-Meta-Jwks X-Authentik-Meta-Outpost X-Authentik-Meta-Provider X-Authentik-Meta-App X-Authentik-Meta-Version
|
||||
}
|
||||
reverse_proxy http://127.0.0.1:${port}
|
||||
'';
|
||||
|
||||
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";
|
||||
|
||||
systemd.user = let inherit (config.virtualisation.quadlet) containers;
|
||||
in {
|
||||
services."${user}-cron" = {
|
||||
Unit = {
|
||||
Description = "Firefly III cron";
|
||||
Requires = [ containers."${user}".ref ];
|
||||
After = [ containers."${user}".ref ];
|
||||
};
|
||||
Service.ExecStart =
|
||||
"${pkgs.podman}/bin/podman exec ${user} /usr/local/bin/php /var/www/html/artisan firefly-iii:cron";
|
||||
};
|
||||
timers."${user}-cron" = {
|
||||
Unit.Description = "Firefly III cron";
|
||||
Timer.OnCalendar = "daily";
|
||||
};
|
||||
};
|
||||
|
||||
virtualisation.quadlet = let
|
||||
inherit (config.virtualisation.quadlet) containers;
|
||||
inherit (config.virtualisation.quadlet) networks;
|
||||
secrets = osConfig.millironx.podman-secrets.fireflyiii;
|
||||
in {
|
||||
autoUpdate.enable = true;
|
||||
autoEscape = true;
|
||||
|
||||
networks."${user}" = { };
|
||||
|
||||
containers = {
|
||||
"${user}-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."${user}".ref ];
|
||||
};
|
||||
unitConfig.Requires = [ secrets.ref ];
|
||||
unitConfig.After = [ secrets.ref ];
|
||||
};
|
||||
|
||||
"${user}" = {
|
||||
autoStart = true;
|
||||
containerConfig = {
|
||||
image = "docker.io/fireflyiii/core:version-6";
|
||||
environments = {
|
||||
APP_ENV = "local";
|
||||
DEFAULT_LANGUAGE = "en_US";
|
||||
TZ = "America/New_York";
|
||||
TRUSTED_PROXIES = "*";
|
||||
DB_CONNECTION = "pgsql";
|
||||
DB_HOST = "${user}-db";
|
||||
DB_PORT = "5432";
|
||||
DB_DATABASE = user;
|
||||
DB_USERNAME = user;
|
||||
CACHE_DRIVER = "redis";
|
||||
SESSION_DRIVER = "redis";
|
||||
REDIS_SCHEME = "tcp";
|
||||
REDIS_HOST = "host.docker.internal";
|
||||
REDIS_PORT = "6379";
|
||||
REDIS_DB = "0";
|
||||
REDIS_CACHE_DB = "1";
|
||||
MAIL_MAILER = "smtp";
|
||||
APP_URL = "https://money.millironx.com";
|
||||
AUTHENTICATION_GUARD = "remote_user_guard";
|
||||
AUTHENTICATION_GUARD_HEADER = "HTTP_X_AUTHENTIK_USERNAME";
|
||||
AUTHENTICATION_GUARD_EMAIL = "HTTP_X_AUTHENTIK_EMAIL";
|
||||
};
|
||||
secrets = map (s: "${s},type=env") [
|
||||
"SITE_OWNER"
|
||||
"APP_KEY"
|
||||
"DB_PASSWORD"
|
||||
"MAIL_HOST"
|
||||
"MAIL_PORT"
|
||||
"MAIL_FROM"
|
||||
"MAIL_USERNAME"
|
||||
"MAIL_PASSWORD"
|
||||
"MAIL_ENCRYPTION"
|
||||
"REDIS_PASSWORD"
|
||||
];
|
||||
volumes = [ "${stateDirectory}/upload:/var/html/storage/upload:U" ];
|
||||
networks = [ networks."${user}".ref ];
|
||||
publishPorts = [ "127.0.0.1:${port}:${containerPort}" ];
|
||||
};
|
||||
unitConfig.Requires = [ secrets.ref containers."${user}-db".ref ];
|
||||
unitConfig.After = [ secrets.ref containers."${user}-db".ref ];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
@ -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;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
services.gpg-agent = {
|
||||
enable = true;
|
||||
enableBashIntegration = true;
|
||||
enableSshSupport = false;
|
||||
enableSshSupport = true;
|
||||
enableZshIntegration = true;
|
||||
defaultCacheTtl = 604800;
|
||||
maxCacheTtl = 604800;
|
||||
|
|
|
|||
|
|
@ -1,182 +0,0 @@
|
|||
{ config, pkgs, home-manager-quadlet-nix, ... }:
|
||||
let
|
||||
user = "immich";
|
||||
port = "46642";
|
||||
containerPort = "2283";
|
||||
redisPort = 64664;
|
||||
stateDirectory = "/var/lib/${user}";
|
||||
servicePaths = [ ];
|
||||
databasePaths = [ "database" ];
|
||||
s3BucketName = "millironx-photos";
|
||||
s3MountDirectory = "/mount/s3/${s3BucketName}";
|
||||
immich-version = "v2";
|
||||
in {
|
||||
age.secrets = {
|
||||
millironx-photos-s3-token.file = ./../secrets/millironx-photos-s3.age;
|
||||
redis-immich-password.file = ./../secrets/redis-immich-password.age;
|
||||
"immich.toml" = {
|
||||
file = ./../secrets/immich.toml.age;
|
||||
owner = user;
|
||||
};
|
||||
};
|
||||
|
||||
millironx.podman-secrets.immich = {
|
||||
inherit user;
|
||||
secrets-files = [ config.age.secrets."immich.toml".path ];
|
||||
};
|
||||
|
||||
systemd.tmpfiles.rules =
|
||||
map (d: "d ${stateDirectory}/${d} 1775 ${user} ${user} -")
|
||||
([ "" ] ++ servicePaths ++ databasePaths);
|
||||
|
||||
environment.systemPackages = [ pkgs.s3fs ];
|
||||
|
||||
fileSystems."${s3BucketName}" = {
|
||||
device = s3BucketName;
|
||||
mountPoint = s3MountDirectory;
|
||||
fsType = "fuse./run/current-system/sw/bin/s3fs";
|
||||
noCheck = true;
|
||||
options = [
|
||||
"_netdev"
|
||||
"allow_other"
|
||||
"use_path_request_style"
|
||||
"url=https://us-east-1.linodeobjects.com/"
|
||||
"passwd_file=${config.age.secrets.millironx-photos-s3-token.path}"
|
||||
"uid=${user}"
|
||||
"gid=${user}"
|
||||
"umask=0022"
|
||||
];
|
||||
};
|
||||
|
||||
services = {
|
||||
borgmatic.configurations."${config.networking.hostName}" = {
|
||||
source_directories = map (d: "${stateDirectory}/${d}") servicePaths;
|
||||
postgresql_databases = [{
|
||||
name = user;
|
||||
psql_command =
|
||||
"/run/wrappers/bin/sudo -iu ${user} ${pkgs.podman}/bin/podman exec ${user}-db psql --username=${user}";
|
||||
pg_dump_command =
|
||||
"/run/wrappers/bin/sudo -iu ${user} ${pkgs.podman}/bin/podman exec ${user}-db pg_dump --username=${user}";
|
||||
pg_restore_command =
|
||||
"/run/wrappers/bin/sudo -iu ${user} ${pkgs.podman}/bin/podman exec ${user}-db pg_restore --username=${user}";
|
||||
}];
|
||||
};
|
||||
|
||||
caddy.virtualHosts."photos.millironx.com".extraConfig = ''
|
||||
reverse_proxy http://127.0.0.1:${port}
|
||||
'';
|
||||
|
||||
redis.servers."${user}" = {
|
||||
enable = true;
|
||||
port = redisPort;
|
||||
bind = "0.0.0.0";
|
||||
requirePassFile = config.age.secrets.redis-immich-password.path;
|
||||
};
|
||||
};
|
||||
|
||||
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;
|
||||
inherit (config.virtualisation.quadlet) volumes;
|
||||
secrets = osConfig.millironx.podman-secrets.immich;
|
||||
db-user = "postgres";
|
||||
in {
|
||||
autoUpdate.enable = true;
|
||||
autoEscape = true;
|
||||
|
||||
networks."${user}" = { };
|
||||
|
||||
volumes.model-cache.volumeConfig = { };
|
||||
|
||||
containers = {
|
||||
"${user}-db" = {
|
||||
autoStart = true;
|
||||
containerConfig = {
|
||||
# For some reason, the -rootless variant seems to hang, so go with
|
||||
# the rootful one (even though this user has no root access)
|
||||
image = "docker.io/tensorchord/pgvecto-rs:pg16-v0.3.0";
|
||||
environments = {
|
||||
POSTGRES_DB = user;
|
||||
POSTGRES_USER = db-user;
|
||||
POSTGRES_INITDB_ARGS = "--data-checksums";
|
||||
};
|
||||
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."${user}".ref ];
|
||||
};
|
||||
unitConfig.Requires = [ secrets.ref ];
|
||||
unitConfig.After = [ secrets.ref ];
|
||||
};
|
||||
|
||||
"${user}-ml" = {
|
||||
autoStart = true;
|
||||
containerConfig = {
|
||||
image =
|
||||
"ghcr.io/immich-app/immich-machine-learning:${immich-version}";
|
||||
networks = [ networks."${user}".ref ];
|
||||
volumes = [ "${volumes.model-cache.ref}:/cache" ];
|
||||
};
|
||||
};
|
||||
|
||||
"${user}" = {
|
||||
autoStart = true;
|
||||
containerConfig = {
|
||||
image = "ghcr.io/immich-app/immich-server:${immich-version}";
|
||||
environments = {
|
||||
DB_HOSTNAME = "${user}-db";
|
||||
DB_USERNAME = db-user;
|
||||
DB_DATABASE_NAME = user;
|
||||
REDIS_HOSTNAME = "host.docker.internal";
|
||||
REDIS_PORT = builtins.toString redisPort;
|
||||
};
|
||||
secrets =
|
||||
map (s: "${s},type=env") [ "DB_PASSWORD" "REDIS_PASSWORD" ];
|
||||
volumes = [
|
||||
# Generally, mounts need the :U directive, but in the case of
|
||||
# mounting the root of a bucket, that hangs. Uploads are verified
|
||||
# to work without that, so everything should be fine
|
||||
"${s3MountDirectory}:/usr/src/app/upload"
|
||||
"/etc/localtime:/etc/localtime:ro"
|
||||
];
|
||||
networks = [ networks."${user}".ref ];
|
||||
publishPorts = [ "127.0.0.1:${port}:${containerPort}" ];
|
||||
addHosts = [ "auth.millironx.com:host-gateway" ];
|
||||
};
|
||||
unitConfig.Requires = [
|
||||
secrets.ref
|
||||
containers."${user}-db".ref
|
||||
containers."${user}-ml".ref
|
||||
];
|
||||
unitConfig.After = [
|
||||
secrets.ref
|
||||
containers."${user}-db".ref
|
||||
containers."${user}-ml".ref
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
@ -1,132 +0,0 @@
|
|||
{ config, pkgs, home-manager-quadlet-nix, ... }:
|
||||
let
|
||||
user = "navidrome";
|
||||
port = "4533";
|
||||
authentikPort = "9000";
|
||||
stateDirectory = "/var/lib/${user}";
|
||||
s3BucketName = "millironx-music";
|
||||
s3MountDirectory = "/mount/s3/${s3BucketName}";
|
||||
in {
|
||||
age.secrets = {
|
||||
millironx-music-s3-token.file = ./../secrets/millironx-music-s3.age;
|
||||
"navidrome.toml" = {
|
||||
file = ./../secrets/navidrome.toml.age;
|
||||
owner = user;
|
||||
};
|
||||
};
|
||||
|
||||
millironx.podman-secrets.navidrome = {
|
||||
inherit user;
|
||||
secrets-files = [ config.age.secrets."navidrome.toml".path ];
|
||||
};
|
||||
|
||||
environment.systemPackages = [ pkgs.s3fs ];
|
||||
|
||||
fileSystems."${s3BucketName}" = {
|
||||
device = s3BucketName;
|
||||
mountPoint = s3MountDirectory;
|
||||
fsType = "fuse./run/current-system/sw/bin/s3fs";
|
||||
noCheck = true;
|
||||
options = [
|
||||
"_netdev"
|
||||
"allow_other"
|
||||
"use_path_request_style"
|
||||
"url=https://us-east-1.linodeobjects.com/"
|
||||
"passwd_file=${config.age.secrets.millironx-music-s3-token.path}"
|
||||
"uid=${user}"
|
||||
"gid=${user}"
|
||||
"umask=0022"
|
||||
];
|
||||
};
|
||||
|
||||
systemd.tmpfiles.rules =
|
||||
map (d: "d ${stateDirectory}/${d} 1775 ${user} ${user} -") [ "" "data" ];
|
||||
|
||||
services.borgmatic.configurations."${config.networking.hostName}" = {
|
||||
source_directories = map (d: "${stateDirectory}/${d}") [ "data" ];
|
||||
};
|
||||
|
||||
# Modified from
|
||||
# - <https://www.navidrome.org/docs/getting-started/extauth-quickstart/#example-caddy-with-authentik>
|
||||
# - <https://www.navidrome.org/docs/usage/integration/authentication/#caddy-with-forward_auth>
|
||||
# Modifications are exclusively changes from Docker hostnames to 127.0.0.1 and
|
||||
# port numbers
|
||||
services.caddy.virtualHosts."music.millironx.com".extraConfig = ''
|
||||
# Authentik output endpoint
|
||||
reverse_proxy /outpost.goauthentik.io/* http://127.0.0.1:${authentikPort}
|
||||
|
||||
# Protect everything except share and subsonic endpoints
|
||||
@protected not path /share/* /rest/*
|
||||
forward_auth @protected http://127.0.0.1:${authentikPort} {
|
||||
uri /outpost.goauthentik.io/auth/caddy
|
||||
copy_headers X-Authentik-Username>Remote-User
|
||||
}
|
||||
|
||||
# Authentik uses the Authorization header if present, so should be able to
|
||||
# authenticate subsonic clients that support BasicAuth. Requests from the
|
||||
# Navidrome Web App will be authenticated via the existing session cookie.
|
||||
# If you want to have Navidrome authenticate subsonic requests, remove this
|
||||
# forward_auth block.
|
||||
@subsonic path /rest/*
|
||||
forward_auth @subsonic http://127.0.0.1:${authentikPort} {
|
||||
uri /outpost.goauthentik.io/auth/caddy
|
||||
copy_headers X-Authentik-Username>Remote-User
|
||||
|
||||
# Some clients that claim to support basicauth still expect a subsonic
|
||||
# response in case of authentication failure instead of a proper basicauth
|
||||
# response.
|
||||
@error status 1xx 3xx 4xx 5xx
|
||||
handle_response @error {
|
||||
respond <<SUBSONICERR
|
||||
<subsonic-response xmlns="http://subsonic.org/restapi" status="failed" version="1.16.1" type="proxy-auth" serverVersion="n/a" openSubsonic="true">
|
||||
<error code="40" message="Invalid credentials or unsupported client"></error>
|
||||
</subsonic-response>
|
||||
SUBSONICERR 200
|
||||
}
|
||||
}
|
||||
|
||||
# Forward everything to Navidrome
|
||||
reverse_proxy http://127.0.0.1:${port}
|
||||
'';
|
||||
|
||||
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 = {
|
||||
autoUpdate.enable = true;
|
||||
containers.navidrome = {
|
||||
autoStart = true;
|
||||
containerConfig = {
|
||||
image = "docker.io/deluan/navidrome:latest";
|
||||
environments = {
|
||||
ND_BASEURL = "https://music.millironx.com";
|
||||
# pasta appears to use the static host IP so trust that
|
||||
ND_EXTAUTH_TRUSTEDSOURCES = "23.239.13.247/24";
|
||||
};
|
||||
secrets =
|
||||
map (s: "${s},type=env") [ "ND_LASTFM_APIKEY" "ND_LASTFM_SECRET" ];
|
||||
volumes = [
|
||||
"${s3MountDirectory}:/music:ro"
|
||||
"${stateDirectory}/data:/data:U"
|
||||
];
|
||||
publishPorts = [ "127.0.0.1:${port}:${port}" ];
|
||||
};
|
||||
unitConfig.Requires =
|
||||
[ osConfig.millironx.podman-secrets.navidrome.ref ];
|
||||
unitConfig.After = [ osConfig.millironx.podman-secrets.navidrome.ref ];
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
50
services/nixos-update.nix
Normal file
50
services/nixos-update.nix
Normal 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;
|
||||
}
|
||||
});
|
||||
'';
|
||||
|
||||
}
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
{ ... }: {
|
||||
services.openssh = {
|
||||
enable = true;
|
||||
settings = {
|
||||
PermitRootLogin = "no";
|
||||
PasswordAuthentication = false;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
@ -1,212 +0,0 @@
|
|||
{ config, pkgs, home-manager-quadlet-nix, ... }:
|
||||
let
|
||||
domain = "video.millironx.com";
|
||||
user = "peertube";
|
||||
port = "33788";
|
||||
containerPort = "9000";
|
||||
rtmpHostPort = "41936";
|
||||
rtmpContainerPort = "1935";
|
||||
redisPort = 63378;
|
||||
stateDirectory = "/var/lib/${user}";
|
||||
servicePaths = [ "data" "config" "assets" ];
|
||||
databasePaths = [ "database" ];
|
||||
peertubeVersion = "v8.1.4";
|
||||
peertubeAssetsDir = "${pkgs.peertube}/client/dist";
|
||||
in {
|
||||
age.secrets = {
|
||||
"redis-${user}-password".file = ./../secrets/redis-${user}-password.age;
|
||||
"${user}.toml" = {
|
||||
file = ./../secrets/${user}.toml.age;
|
||||
owner = user;
|
||||
};
|
||||
};
|
||||
|
||||
millironx.podman-secrets.${user} = {
|
||||
inherit user;
|
||||
secrets-files = [ config.age.secrets."${user}.toml".path ];
|
||||
};
|
||||
|
||||
systemd.tmpfiles.rules =
|
||||
map (d: "d ${stateDirectory}/${d} 1775 ${user} ${user} -")
|
||||
([ "" ] ++ servicePaths ++ databasePaths);
|
||||
|
||||
services = {
|
||||
borgmatic.configurations."${config.networking.hostName}" = {
|
||||
source_directories = map (d: "${stateDirectory}/${d}") servicePaths;
|
||||
postgresql_databases = [{
|
||||
name = user;
|
||||
psql_command =
|
||||
"/run/wrappers/bin/sudo -iu ${user} ${pkgs.podman}/bin/podman exec ${user}-db psql --username=${user}";
|
||||
pg_dump_command =
|
||||
"/run/wrappers/bin/sudo -iu ${user} ${pkgs.podman}/bin/podman exec ${user}-db pg_dump --username=${user}";
|
||||
pg_restore_command =
|
||||
"/run/wrappers/bin/sudo -iu ${user} ${pkgs.podman}/bin/podman exec ${user}-db pg_restore --username=${user}";
|
||||
}];
|
||||
};
|
||||
|
||||
caddy.virtualHosts.${domain}.extraConfig =
|
||||
builtins.readFile ./../conf/peertube.caddyfile;
|
||||
|
||||
redis.servers.${user} = {
|
||||
enable = true;
|
||||
port = redisPort;
|
||||
bind = "0.0.0.0";
|
||||
requirePassFile = config.age.secrets."redis-${user}-password".path;
|
||||
};
|
||||
};
|
||||
|
||||
# This is a hack - I'm deliberately hijacking the systemd service that is
|
||||
# set up by `services.caddy` in order to sync the `let` variables with the
|
||||
# external Caddyfile via environment variables
|
||||
# This is safe for NixOS 25.11 - see
|
||||
# <https://github.com/NixOS/nixpkgs/blob/nixos-25.11/nixos/modules/services/web-servers/caddy/default.nix#L412>
|
||||
systemd.services.caddy.environment = {
|
||||
MILLIRONX_PEERTUBE_PORT = port;
|
||||
MILLIRONX_PEERTUBE_ASSETS_DIR = peertubeAssetsDir;
|
||||
MILLIRONX_PEERTUBE_DATA_DIR = "${stateDirectory}/data";
|
||||
};
|
||||
# Another hack - allows the Caddy user to be able to read files that
|
||||
# PeerTube writes into its dist/ folders
|
||||
users.users.${config.services.caddy.user}.extraGroups = [ user ];
|
||||
|
||||
# Forward RTMP (privileged) port to container-accessible (non-privileged) port
|
||||
systemd = {
|
||||
sockets."peertube-rtmp" = {
|
||||
description = "PeerTube RTMP Socket";
|
||||
wantedBy = [ "sockets.target" ];
|
||||
socketConfig = {
|
||||
ListenStream = "0.0.0.0:${rtmpContainerPort}";
|
||||
Accept = false;
|
||||
Service = "peertube-rtmp-forward.service";
|
||||
};
|
||||
};
|
||||
|
||||
services."peertube-rtmp-forward" = {
|
||||
description = "PeerTube RTMP Port Forwarder";
|
||||
requires = [ "peertube-rtmp.socket" ];
|
||||
after = [ "network.target" ];
|
||||
serviceConfig = {
|
||||
Type = "notify";
|
||||
ExecStart =
|
||||
"${pkgs.systemd}/lib/systemd/systemd-socket-proxyd 127.0.0.1:${rtmpHostPort}";
|
||||
PrivateTmp = true;
|
||||
};
|
||||
};
|
||||
};
|
||||
networking.firewall.allowedTCPPorts = [ 1935 ];
|
||||
|
||||
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.${user};
|
||||
in {
|
||||
autoUpdate.enable = true;
|
||||
autoEscape = true;
|
||||
|
||||
networks.${user} = { };
|
||||
|
||||
containers = {
|
||||
"${user}-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."${user}".ref ];
|
||||
};
|
||||
unitConfig.Requires = [ secrets.ref ];
|
||||
unitConfig.After = [ secrets.ref ];
|
||||
};
|
||||
|
||||
"${user}" = {
|
||||
# TODO: Once data is migrated from anderson, turn this server to
|
||||
# autostart true
|
||||
autoStart = false;
|
||||
containerConfig = {
|
||||
image = "docker.io/chocobozzz/peertube:${peertubeVersion}";
|
||||
environments = {
|
||||
PEERTUBE_DB_USERNAME = user;
|
||||
PEERTUBE_DB_SSL = "false";
|
||||
PEERTUBE_DB_HOSTNAME = "${user}-db";
|
||||
PEERTUBE_WEBSERVER_HOSTNAME = domain;
|
||||
PEERTUBE_TRUST_PROXY =
|
||||
''["127.0.0.1","loopback","172.18.0.0/16"]'';
|
||||
PEERTUBE_REDIS_HOSTNAME = "host.docker.internal";
|
||||
PEERTUBE_REDIS_PORT = builtins.toString redisPort;
|
||||
PEERTUBE_USER_VIDEO_QUOTA = "0";
|
||||
PEERTUBE_SIGNUP_ENABLED = "false";
|
||||
PEERTUBE_CONTACT_FORM_ENABLED = "false";
|
||||
PEERTUBE_TRANSCODING_ENABLED = "true";
|
||||
PEERTUBE_TRANSCODING_THREADS = "2";
|
||||
PEERTUBE_TRANSCODING_144P = "true";
|
||||
PEERTUBE_TRANSCODING_360P = "true";
|
||||
PEERTUBE_TRANSCODING_480P = "true";
|
||||
PEERTUBE_TRANSCODING_720P = "true";
|
||||
PEERTUBE_TRANSCODING_1080P = "true";
|
||||
PEERTUBE_TRANSCODING_HLS_ENABLED = "true";
|
||||
PEERTUBE_OBJECT_STORAGE_ENABLED = "true";
|
||||
PEERTUBE_OBJECT_STORAGE_WEB_VIDEOS_PREFIX = "videos";
|
||||
PEERTUBE_OBJECT_STORAGE_UPLOAD_ACL_PUBLIC = "public-read";
|
||||
PEERTUBE_OBJECT_STORAGE_STREAMING_PLAYLISTS_PREFIX = "playlists";
|
||||
};
|
||||
secrets = map (s: "${s},type=env") [
|
||||
"PEERTUBE_DB_PASSWORD"
|
||||
"PEERTUBE_SECRET"
|
||||
"PEERTUBE_SMTP_USERNAME"
|
||||
"PEERTUBE_SMTP_PASSWORD"
|
||||
"PEERTUBE_SMTP_HOSTNAME"
|
||||
"PEERTUBE_SMTP_PORT"
|
||||
"PEERTUBE_SMTP_FROM"
|
||||
"PEERTUBE_ADMIN_EMAIL"
|
||||
"PEERTUBE_REDIS_AUTH"
|
||||
"PEERTUBE_OBJECT_STORAGE_ENDPOINT"
|
||||
"PEERTUBE_OBJECT_STORAGE_REGION"
|
||||
"PEERTUBE_OBJECT_STORAGE_CREDENTIALS_ACCESS_KEY_ID"
|
||||
"PEERTUBE_OBJECT_STORAGE_CREDENTIALS_SECRET_ACCESS_KEY"
|
||||
"PEERTUBE_OBJECT_STORAGE_STREAMING_PLAYLISTS_BUCKET_NAME"
|
||||
"PEERTUBE_OBJECT_STORAGE_WEB_VIDEOS_BUCKET_NAME"
|
||||
];
|
||||
networks = [ networks."${user}".ref ];
|
||||
publishPorts = [ "127.0.0.1:${port}:${containerPort}" ];
|
||||
addHosts = [ "auth.millironx.com:host-gateway" ];
|
||||
volumes = [
|
||||
"${stateDirectory}/data:/data"
|
||||
"${stateDirectory}/config:/config"
|
||||
"${peertubeAssetsDir}:/app/client/dist:ro"
|
||||
];
|
||||
};
|
||||
|
||||
unitConfig.Requires = [ secrets.ref containers."${user}-db".ref ];
|
||||
unitConfig.After = [ secrets.ref containers."${user}-db".ref ];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
27
services/pihole.nix
Normal file
27
services/pihole.nix
Normal 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" ];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@
|
|||
services.samba = {
|
||||
enable = true;
|
||||
package = pkgs.sambaFull;
|
||||
securityType = "user";
|
||||
openFirewall = true;
|
||||
settings = {
|
||||
global = {
|
||||
|
|
|
|||
|
|
@ -1,49 +0,0 @@
|
|||
{ ... }:
|
||||
let port = "7327";
|
||||
in {
|
||||
services.searx = {
|
||||
enable = true;
|
||||
|
||||
configureUwsgi = true;
|
||||
uwsgiConfig = {
|
||||
disable-logging = true;
|
||||
http = ":${port}";
|
||||
};
|
||||
|
||||
redisCreateLocally = true;
|
||||
|
||||
settings = {
|
||||
general = {
|
||||
instance_name = "Milliron X Search";
|
||||
enable_metrics = false;
|
||||
};
|
||||
search = { autocomplete = "duckduckgo"; };
|
||||
server = {
|
||||
base_url = "https://search.millironx.com/";
|
||||
limiter = true;
|
||||
public_instance = true;
|
||||
image_proxy = true;
|
||||
method = "GET";
|
||||
secret_key = "rC35eF8DRpJDqa";
|
||||
};
|
||||
ui = { query_in_title = false; };
|
||||
hostnames = {
|
||||
replace = { "(www.)?reddit.com$" = "old.reddit.com"; };
|
||||
low_priority = [
|
||||
"(.*.)?facebooks.com$"
|
||||
"(.*.)?youtube.com$"
|
||||
"(.*.)?youtu.be$"
|
||||
"(.*.)?reddit.com$"
|
||||
"(.*.)?redd.it$"
|
||||
"(www.)?twitter.com$"
|
||||
"(www.)?x.com$"
|
||||
];
|
||||
high_priority = [ "(.*.)?wikipedia.org$" ];
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
services.caddy.virtualHosts."search.millironx.com".extraConfig = ''
|
||||
reverse_proxy http://127.0.0.1:${port}
|
||||
'';
|
||||
}
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
{ hostname, ... }: {
|
||||
services.syncthing = let
|
||||
devices = {
|
||||
bracket.id =
|
||||
"6I5AHYC-IWSO3SZ-TZY4SSR-Z7MGB2V-QMCMJXE-QW5JBQV-DBDU5YV-4LRLKQW";
|
||||
boozer.id =
|
||||
"JKLIUHR-SBAVQCX-43ETUQR-M4ZZA75-JXK7EOF-F5RJBG7-PT363R6-MJ6WLQ4";
|
||||
} // (if hostname != "odyssey" then {
|
||||
odyssey.id =
|
||||
"YC6NDSU-2JRS4MY-BSM4B5V-FWPXKSJ-S573II2-HDOSWSN-DVIDORQ-UUHTKQB";
|
||||
} else
|
||||
{ }) // (if hostname != "corianne" then {
|
||||
corianne.id =
|
||||
"EN5KDDZ-F6DYDSR-KK35M2M-BVGBU4W-MVC4ENT-5EPWA6M-BBJPIBU-EQTPRQX";
|
||||
} else
|
||||
{ });
|
||||
in {
|
||||
enable = true;
|
||||
settings = {
|
||||
inherit devices;
|
||||
folders = let deviceNames = builtins.attrNames devices;
|
||||
in {
|
||||
Logseq = {
|
||||
label = "Logseq";
|
||||
id = "kkqs5-4upcf";
|
||||
path = "~/Logseq";
|
||||
type = "sendreceive";
|
||||
versioning = {
|
||||
type = "trashcan";
|
||||
params.cleanoutDays = 14;
|
||||
};
|
||||
devices = deviceNames;
|
||||
};
|
||||
SyncBucket = {
|
||||
label = "SyncBucket";
|
||||
id = "9l6gb-rkyou";
|
||||
path = "~/SyncBucket";
|
||||
type = "sendreceive";
|
||||
versioning = {
|
||||
type = "trashcan";
|
||||
params.cleanoutDays = 14;
|
||||
};
|
||||
devices = deviceNames;
|
||||
};
|
||||
};
|
||||
options = { urAccepted = -1; };
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
{ ... }: {
|
||||
services.tailscale = {
|
||||
enable = true;
|
||||
useRoutingFeatures = "server";
|
||||
};
|
||||
}
|
||||
|
|
@ -1,170 +0,0 @@
|
|||
{ config, pkgs, home-manager-quadlet-nix, ... }:
|
||||
let
|
||||
user = "vaultwarden";
|
||||
port = "9285";
|
||||
containerPort = port;
|
||||
authentikPort = "9000";
|
||||
stateDirectory = "/var/lib/${user}";
|
||||
servicePaths = [ "data" ];
|
||||
databasePaths = [ "database" ];
|
||||
in {
|
||||
age.secrets."vaultwarden.toml" = {
|
||||
file = ./../secrets/vaultwarden.toml.age;
|
||||
owner = user;
|
||||
};
|
||||
|
||||
millironx.podman-secrets.vaultwarden = {
|
||||
inherit user;
|
||||
secrets-files = [ config.age.secrets."vaultwarden.toml".path ];
|
||||
};
|
||||
|
||||
systemd.tmpfiles.rules =
|
||||
map (d: "d ${stateDirectory}/${d} 1775 ${user} ${user} -")
|
||||
([ "" ] ++ servicePaths ++ databasePaths);
|
||||
|
||||
services.borgmatic.configurations."${config.networking.hostName}" = {
|
||||
source_directories = map (d: "${stateDirectory}/${d}") servicePaths;
|
||||
postgresql_databases = [{
|
||||
name = user;
|
||||
psql_command =
|
||||
"/run/wrappers/bin/sudo -iu ${user} ${pkgs.podman}/bin/podman exec ${user}-db psql --username=${user}";
|
||||
pg_dump_command =
|
||||
"/run/wrappers/bin/sudo -iu ${user} ${pkgs.podman}/bin/podman exec ${user}-db pg_dump --username=${user}";
|
||||
pg_restore_command =
|
||||
"/run/wrappers/bin/sudo -iu ${user} ${pkgs.podman}/bin/podman exec ${user}-db pg_restore --username=${user}";
|
||||
}];
|
||||
};
|
||||
|
||||
services.caddy.virtualHosts."vault.millironx.com".extraConfig = ''
|
||||
# See <https://github.com/dani-garcia/vaultwarden/wiki/Proxy-examples>
|
||||
encode zstd gzip
|
||||
header / {
|
||||
Strict-Transport-Security "max-age=31536000;"
|
||||
X-XSS-Protection "0"
|
||||
X-Frame-Options "DENY"
|
||||
X-Robots-Tag "noindex, nofollow"
|
||||
X-Content-Type-Options "nosniff"
|
||||
-Server
|
||||
-X-Powered-By
|
||||
-Last-Modified
|
||||
}
|
||||
|
||||
@admin {
|
||||
path /admin*
|
||||
not remote_ip private_ranges 100.64.0.0/10
|
||||
}
|
||||
respond @admin "Access denied to remote clients. Use localhost or VPN." 403
|
||||
|
||||
reverse_proxy http://127.0.0.1:${port} {
|
||||
header_up X-Real-IP {remote_host}
|
||||
}
|
||||
'';
|
||||
|
||||
services.crowdsec = {
|
||||
localConfig.acquisitions = [{
|
||||
source = "journalctl";
|
||||
journalctl_filter = [ "_SYSTEMD_USER_UNIT=${user}.service" ];
|
||||
labels.type = "bitwarden";
|
||||
}];
|
||||
hub.collections = [ "MariuszKociubinski/bitwarden" ];
|
||||
};
|
||||
|
||||
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.vaultwarden;
|
||||
in {
|
||||
autoUpdate.enable = true;
|
||||
autoEscape = true;
|
||||
|
||||
networks."${user}" = { };
|
||||
|
||||
containers = {
|
||||
"${user}-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."${user}".ref ];
|
||||
};
|
||||
unitConfig.Requires = [ secrets.ref ];
|
||||
unitConfig.After = [ secrets.ref ];
|
||||
};
|
||||
|
||||
"${user}" = {
|
||||
autoStart = true;
|
||||
containerConfig = {
|
||||
image = "ghcr.io/dani-garcia/vaultwarden:latest";
|
||||
addHosts = [ "auth.millironx.com:host-gateway" ];
|
||||
environments = {
|
||||
DOMAIN = "https://vault.millironx.com";
|
||||
ROCKET_PORT = port;
|
||||
PUSH_ENABLED = "true";
|
||||
SIGNUPS_ALLOWED = "false";
|
||||
SMTP_FROM_NAME = "Milliron X Vault";
|
||||
SMTP_SECURITY = "force_tls";
|
||||
SSO_ENABLED = "true";
|
||||
SSO_ONLY = "true";
|
||||
SSO_AUTHORITY =
|
||||
"https://auth.millironx.com/application/o/vaultwarden/";
|
||||
SSO_SCOPES = "openid profile email offline_access";
|
||||
# Needed to keep token expiration errors from happening
|
||||
# See <https://github.com/dani-garcia/vaultwarden/issues/6311#issuecomment-3929409097>
|
||||
SSO_AUTH_ONLY_NOT_SESSION = "true";
|
||||
};
|
||||
secrets = map (s: "${s},type=env") [
|
||||
"ADMIN_TOKEN"
|
||||
"DATABASE_URL"
|
||||
"PUSH_INSTALLATION_ID"
|
||||
"PUSH_INSTALLATION_KEY"
|
||||
"SMTP_FROM"
|
||||
"SMTP_HOST"
|
||||
"SMTP_PORT"
|
||||
"SMTP_PASSWORD"
|
||||
"SMTP_USERNAME"
|
||||
"SSO_CLIENT_ID"
|
||||
"SSO_CLIENT_SECRET"
|
||||
"YUBICO_CLIENT_ID"
|
||||
"YUBICO_SECRET_KEY"
|
||||
];
|
||||
volumes = [ "${stateDirectory}/data:/data:U" ];
|
||||
networks = [ networks."${user}".ref ];
|
||||
publishPorts = [ "127.0.0.1:${port}:${containerPort}" ];
|
||||
};
|
||||
unitConfig.Requires = [ secrets.ref containers."${user}-db".ref ];
|
||||
unitConfig.After = [ secrets.ref containers."${user}-db".ref ];
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -15,8 +15,6 @@ in {
|
|||
rig-install
|
||||
];
|
||||
|
||||
age.secrets.firefox-policy.file = ./../../secrets/darwin-policies-json.age;
|
||||
|
||||
# Use a custom configuration.nix location.
|
||||
# $ darwin-rebuild switch -I darwin-config=$HOME/.config/nixpkgs/darwin/configuration.nix
|
||||
environment.darwinConfig = "$HOME/.config/home-manager/configuration.nix";
|
||||
|
|
@ -26,35 +24,9 @@ in {
|
|||
};
|
||||
|
||||
# Auto upgrade nix package and the daemon service.
|
||||
nix = {
|
||||
enable = true;
|
||||
gc = {
|
||||
automatic = true;
|
||||
interval = { Weekday = 1; };
|
||||
options = ''
|
||||
--delete-older-than 90d
|
||||
'';
|
||||
};
|
||||
settings = {
|
||||
substituters =
|
||||
[ "https://nix-community.cachix.org" "https://cache.nixos.org/" ];
|
||||
trusted-public-keys = [
|
||||
"nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs="
|
||||
];
|
||||
};
|
||||
|
||||
# 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;
|
||||
nix.enable = true;
|
||||
#services.nix-daemon.tempDir = "/nix/tmp";
|
||||
nix.package = pkgs.nix;
|
||||
|
||||
# Create /etc/zshrc that loads the nix-darwin environment.
|
||||
programs.zsh.enable = true; # default shell on catalina
|
||||
|
|
@ -99,12 +71,11 @@ in {
|
|||
in [
|
||||
(sysApp "Firefox")
|
||||
(sysApp "Thunderbird")
|
||||
(sysApp "Microsoft Outlook")
|
||||
(sysApp "Zed")
|
||||
(sysApp "Logseq")
|
||||
(sysApp "Zed")
|
||||
(sysApp "Steam")
|
||||
(localApp "Instinct Dashboard")
|
||||
(localApp "Carestream")
|
||||
(chromeApp "Instinct Dashboard")
|
||||
(chromeApp "Carestream")
|
||||
];
|
||||
show-process-indicators = true;
|
||||
show-recents = false;
|
||||
|
|
@ -159,11 +130,6 @@ in {
|
|||
--user=${config.system.primaryUser} \
|
||||
--set-home \
|
||||
_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" ];
|
||||
|
|
@ -192,7 +158,15 @@ in {
|
|||
no_quarantine = true;
|
||||
|
||||
};
|
||||
taps = [ "r-lib/rig" ];
|
||||
taps = [
|
||||
"homebrew/services"
|
||||
{
|
||||
name = "millironx/millironx";
|
||||
clone_target =
|
||||
"https://code.millironx.com/millironx/homebrew-millironx.git";
|
||||
}
|
||||
"r-lib/rig"
|
||||
];
|
||||
brews = [
|
||||
"borgbackup/tap/borgbackup-fuse"
|
||||
"buildkit"
|
||||
|
|
@ -203,17 +177,23 @@ in {
|
|||
"docker"
|
||||
"docker-buildx"
|
||||
"docker-credential-helper"
|
||||
"firefoxpwa"
|
||||
"mpv"
|
||||
];
|
||||
casks = [
|
||||
"alt-tab"
|
||||
"dash"
|
||||
"anki"
|
||||
"db-browser-for-sqlite"
|
||||
"dolphin"
|
||||
"firefox"
|
||||
"freetube"
|
||||
"ghostty"
|
||||
"inkscape"
|
||||
"iterm2"
|
||||
"logi-options+"
|
||||
"logseq"
|
||||
"macfuse"
|
||||
"musescore"
|
||||
"nextcloud"
|
||||
"openrct2"
|
||||
"qownnotes"
|
||||
|
|
@ -221,7 +201,6 @@ in {
|
|||
"rig"
|
||||
"rstudio"
|
||||
"signal"
|
||||
"skim"
|
||||
"slack"
|
||||
"stats"
|
||||
"steam"
|
||||
|
|
@ -231,7 +210,6 @@ in {
|
|||
"ungoogled-chromium"
|
||||
"veracrypt"
|
||||
"vlc"
|
||||
"vienna"
|
||||
"vorta"
|
||||
"zed"
|
||||
"zotero"
|
||||
|
|
|
|||
|
|
@ -8,7 +8,9 @@
|
|||
imports = [
|
||||
./hardware-configuration/bosephus.nix
|
||||
./hardware-configuration/bosephus-external-drives.nix
|
||||
./../../services/nixos-update.nix
|
||||
./../../services/samba.nix
|
||||
./../../services/pihole.nix
|
||||
];
|
||||
|
||||
# Bootloader.
|
||||
|
|
@ -16,8 +18,8 @@
|
|||
boot.loader.efi.canTouchEfiVariables = true;
|
||||
|
||||
# Ignore lid - so I can close without having the system go into sleep mode
|
||||
services.logind.settings.Login.HandleLidSwitch = "ignore";
|
||||
services.logind.settings.Login.HandleLidSwitchDocked = "ignore";
|
||||
services.logind.lidSwitch = "ignore";
|
||||
services.logind.lidSwitchDocked = "ignore";
|
||||
|
||||
# Secrets
|
||||
age.secrets = {
|
||||
|
|
|
|||
|
|
@ -3,21 +3,8 @@
|
|||
{
|
||||
imports = [ # Include the results of the hardware scan.
|
||||
./hardware-configuration/mcentire.nix
|
||||
./../../modules/podman-secrets.nix
|
||||
./../../services/borgmatic.nix
|
||||
./../../services/caddy.nix
|
||||
./../../services/nixos-update.nix
|
||||
./../../services/crowdsec.nix
|
||||
./../../services/authentik.nix
|
||||
./../../services/audiobookshelf.nix
|
||||
./../../services/fireflyiii.nix
|
||||
./../../services/freshrss.nix
|
||||
./../../services/immich.nix
|
||||
./../../services/navidrome.nix
|
||||
./../../services/openssh.nix
|
||||
./../../services/peertube.nix
|
||||
./../../services/searxng.nix
|
||||
./../../services/tailscale.nix
|
||||
./../../services/vaultwarden.nix
|
||||
];
|
||||
|
||||
# Use the GRUB 2 boot loader.
|
||||
|
|
@ -28,7 +15,6 @@
|
|||
useDHCP = false;
|
||||
interfaces.eth0.useDHCP = true;
|
||||
hostName = "mcentire"; # Define your hostname.
|
||||
firewall.allowedTCPPorts = [ 80 443 ];
|
||||
};
|
||||
|
||||
# Set your time zone.
|
||||
|
|
@ -54,7 +40,7 @@
|
|||
millironx = {
|
||||
isNormalUser = true;
|
||||
description = "Thomas A. Christensen II";
|
||||
extraGroups = [ "adm" "wheel" ];
|
||||
extraGroups = [ "wheel" ];
|
||||
};
|
||||
};
|
||||
|
||||
|
|
@ -65,29 +51,10 @@
|
|||
|
||||
environment.systemPackages = with pkgs; [ neovim inetutils mtr sysstat git ];
|
||||
|
||||
age.secrets.redis-password = {
|
||||
file = ./../../secrets/redis-password.age;
|
||||
owner = config.services.redis.servers.redis.user;
|
||||
};
|
||||
|
||||
services = {
|
||||
# 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;
|
||||
|
||||
redis.servers.redis = {
|
||||
enable = true;
|
||||
# Apparently, a port is actually required, see
|
||||
# <https://discourse.nixos.org/t/how-to-setup-redis-server-configuration-nix/21878/7>
|
||||
# Evaluating this in the Nix repl confirms that the port is set
|
||||
# incorrectly in the final config when left out
|
||||
port = 6379;
|
||||
bind = "0.0.0.0";
|
||||
requirePassFile = config.age.secrets.redis-password.path;
|
||||
openssh.enable = true;
|
||||
tailscale.enable = true;
|
||||
};
|
||||
};
|
||||
|
||||
virtualisation.quadlet.enable = true;
|
||||
|
||||
system.stateVersion = "25.05"; # Did you read the comment?
|
||||
nix = { extraOptions = "experimental-features = nix-command flakes"; };
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue