Compare commits

...

72 commits

Author SHA1 Message Date
0c706f4a0b
feat(git): add git module, set user config 2025-07-07 09:51:53 +08:00
afa5f72f8f
feat(hm): add git config to rafiq home, use forAllUsers' 2025-07-07 09:51:53 +08:00
d3d4f646b5
feat(shell): enable shell in home-manager module 2025-07-07 09:51:53 +08:00
3f4ba7523a
feat(users): add shell module, simplify users module 2025-07-07 09:51:53 +08:00
c2686b48ec
feat(users): use forAllUsers' in nixos module 2025-07-07 09:51:53 +08:00
8511991d9c
feat(nix): add forAllUsers' and docstrings to lib
Adds forAllUsers' function and docstrings to nix lib.
2025-07-07 09:51:53 +08:00
e104d89793
feat(nix): add system module, pass config to lib
This commit introduces a system module for NixOS and Home Manager,
and passes the configuration to the lib file.
2025-07-07 09:51:53 +08:00
f44b3ee8ce
feat(hm): add sharedModules, move configurations.nix to hosts.nix 2025-07-07 09:51:53 +08:00
24d6e31e8d
refactor: move readme to docs dir, update nix file 2025-07-07 09:51:53 +08:00
94e045e0f3
feat(nixos): pass hostName to nixosSystem, move hm modules 2025-07-07 09:51:53 +08:00
f71c65f639
refactor(nixos): simplify configurations.nix and lib
This commit refactors configurations.nix and lib/default.nix by removing the
extractConfigurations function and related code from lib/default.nix,
and moving the NixOS system building logic directly into configurations.nix.
It also changes the structure of hosts in manifest.nix to use a
hosts.nixos attribute.
2025-07-07 09:51:53 +08:00
94728cdc83
refactor(users): move owner config to users config in manifest 2025-07-07 09:51:53 +08:00
2439544ccf
build(flake): bump inputs 2025-07-07 09:51:53 +08:00
ea408abfce
feat(git): add .gitignore and pre-commit config to gitignore 2025-07-07 09:51:53 +08:00
ffa769c01e
chore: remove pre-commit config symlink 2025-07-07 09:51:53 +08:00
a79fe97fee
docs(readme): add acknowledgements and git links 2025-07-07 09:51:53 +08:00
966ed51a21
refactor(configurations): add default nixos module 2025-07-07 09:51:53 +08:00
90a42fc7d5
feat(nixos): add owner config to manifest and users module 2025-07-07 09:51:53 +08:00
b6ba95a93c
build(flake): add home-manager and example config 2025-07-07 09:51:53 +08:00
53fe600399
refactor: move flake-parts modules to nix/modules/flake 2025-07-07 09:51:53 +08:00
2fe89b9e00
refactor: rename nixos.nix to configurations.nix 2025-07-07 09:51:53 +08:00
2885fe99bb
refactor: rename hostSpec to manifest and update references 2025-07-07 09:51:53 +08:00
5cbffbe90c
feat(readme): add structure section and update README.md 2025-07-07 09:51:53 +08:00
4270e3a304
refactor: rename modules directory to nix 2025-07-07 09:51:53 +08:00
dd665a2f86
feat(lib/mkSystem): add profile suppoet
This commit adds the mkSystem function to simplify nixosSystem
creation and merge profile configs.
2025-07-07 09:51:53 +08:00
3c4bfeece9
refactor(nixos): move nixosSystem config to lib
Moves the nixosSystem configuration logic to a library function for reuse.
2025-07-07 09:51:53 +08:00
61064b955d
feat(nixos): pass flakeConfig to nixosSystem specialArgs 2025-07-07 09:51:53 +08:00
eed6e45067
feat(lib): add flattenAttrs helper 2025-07-07 09:51:53 +08:00
69b626307a
feat(hostSpec): add apollo and nemesis hosts (WIP) 2025-07-07 09:51:53 +08:00
c3c1ca78f6
feat(nixos): pass hostConfig to nixosSystem specialArgs 2025-07-07 09:51:53 +08:00
8f9f400f92
feat(nixos): add common nixos profile; move hostspec config 2025-07-07 09:51:53 +08:00
7f6c65320a
feat(hostSpec): add extraCfg to hostSpec and imports to nixosSystem 2025-07-07 09:51:53 +08:00
7155c1276d
feat: add nix-systems/default flake and remove systems module 2025-07-07 09:51:53 +08:00
a103193459
refactor: move docs/readme and docs/cheatsheet to flake-parts 2025-07-07 09:51:53 +08:00
7a74f1d6c0
refactor(text): extract text helper to rrvsh/text.nix
This commit adds the rrvsh/text.nix flake to manage text generation.
It also removes the old text generation helpers module and updates the
README and flake.nix files to use the new flake.
2025-07-07 09:51:53 +08:00
f8e81564d3
feat(nixos): pass inputs and hostName as specialArgs to nixosSystem 2025-07-07 09:51:53 +08:00
3e11c923ac
refactor(nixos): change hostSpec to define top level option 2025-07-07 09:51:53 +08:00
30c0b3c804
feat(owner): add module to define owner metadata in flake 2025-07-07 09:51:53 +08:00
72e28fd387
feat(hostSpec): add module to define hosts in flake 2025-07-07 09:51:53 +08:00
8a0a7f57e8
feat(git-hooks): add more git-hooks and linters 2025-07-07 09:51:53 +08:00
5020df56b6
feat(git-hooks): enable nixpkgs-fmt pre-commit hook 2025-07-07 09:51:53 +08:00
aee34b5a0a
chore(helpers/text): add TODO comment to extract into repo 2025-07-07 09:51:43 +08:00
816c25a459
chore(readme): remove order attributes from readme module 2025-07-07 09:51:43 +08:00
5f1536d44b
feat(helpers/text): add support for unordered parts 2025-07-07 09:51:43 +08:00
a160c6d5dd
fix(files): remove TODO comment from files module 2025-07-07 09:51:43 +08:00
9ac010c5a0
feat(readme): add headings and descriptions to text.readme 2025-07-07 09:51:43 +08:00
ce56879983
feat(helpers/text): add headings and descriptions 2025-07-07 09:51:43 +08:00
8c38ccea4b
feat(docs): add cheatsheet 2025-07-07 09:51:43 +08:00
8a69549741
feat(files): generate list of generated files dynamically 2025-07-07 09:51:43 +08:00
14c5a81672
build(flake.lock,flake.nix): add git-hooks and dedupe_gitignore 2025-07-07 09:51:43 +08:00
521049d8d3
feat: add files module, text helper, and update readme generation 2025-07-07 09:51:43 +08:00
ce8729dcba
feat: basic flake-parts structure, make-shell, files and readme 2025-07-07 09:51:41 +08:00
c45add1cb1
feat: initial flake setup with flake-parts
Sets up a basic flake with flake-parts for modularity.
2025-07-07 09:51:03 +08:00
525001c374
chore(tree-wide): rm everything for rebase 2025-07-07 09:50:59 +08:00
a9dff2f9d5
feat: rework hostSpec to use providers and machine definitions 2025-07-07 09:48:45 +08:00
e5c78f5051
feat: add rebuild package definition 2025-07-07 09:48:45 +08:00
1722785ae1
refactor: use flake-parts, keep snowfall-lib for compatibility 2025-07-07 09:48:45 +08:00
fc2ee5468e
docs: improve README structure, add headings 2025-07-07 09:48:45 +08:00
7aa6a45c5e
docs: add structure section to README 2025-07-07 09:48:45 +08:00
6a7e64b268
fix(deps): update rrv.sh submodule in flake.lock 2025-07-05 21:27:51 +08:00
d082b03bf1
feat(web-apps): add comfy-ui web app, update stable-diffusion-webui 2025-07-05 01:50:15 +08:00
4dca26d81d
feat(homes/rafiq): make note script auto push 2025-07-05 01:04:03 +08:00
bd1a29f1a4
feat(commit): exit early if no staged changes exist 2025-07-05 00:46:41 +08:00
ce3443f692
fix(homes/rafiq): fix note script cd issues in zk cli 2025-07-05 00:26:54 +08:00
c1615bbb29
feat(homes/rafiq): enable auto updates for tealdeer 2025-07-05 00:19:00 +08:00
df65236c27
feat(homes/rafiq): add note bash script to zk notes cli 2025-07-05 00:16:35 +08:00
4b802b372e
feat(homes/rafiq): zk notes persist directory 2025-07-04 23:30:46 +08:00
c4151ec5cf
feat(homes/rafiq): add zk notes cli tool 2025-07-04 23:27:21 +08:00
e5a6cc9626
fix(homes/rafiq): use bare ghostty instead of -e 2025-07-04 21:15:36 +08:00
fad82fc781
refactor(homes/rafiq): clean up terminal settings 2025-07-04 21:11:52 +08:00
8eceb81ad9
feat(homes/rafiq): enable starship prompt in fish shell 2025-07-04 18:42:59 +08:00
9fffa1d295
feat(commit): improve commit message prompt guidelines 2025-07-03 21:17:14 +08:00
101 changed files with 617 additions and 4187 deletions

4
.gitignore vendored
View file

@ -1,2 +1,2 @@
result
*.qcow2
# gitignore
.pre-commit-config.*

View file

@ -1,7 +0,0 @@
keys:
- &admin age12l33pas8eptwjc7ewux3d8snyzfzwz0tn9qg5kw8le79fswmjgjqdjgyy6
creation_rules:
- path_regex: \.(yaml|json|env|ini)$
key_groups:
- age:
- *admin

View file

@ -1,83 +0,0 @@
# Planning
## To-do
- [ ] Copy over ~/.ssh/id_ed25519 and zellij status bar plugin confirmation
- [ ] Migrate immich to apollo, point to helios
- [x] Migrate LibreChat to apollo, maintain db
- [ ] Figure out wakapi
- [x] Add forgejo
- [ ] Add simple blog
## Versions
- 1.0.0
- Setup desktop as hypervisor with nixos and win11
- Spare drive as steam library
- GPU passthrough to either system
- Always running, VMs spun down except when in use
- Apollo as hypervisor
- VMs for docker host, home-assistant, bare metal or containerised services
- Automated backups for home and state directories
- Ability to build VMs of all systems and implement integration tests
- Staging VMs for ad-hoc testing
- All servers set up with following services:
- Git server
- Chat app
- Network shares
- Federation with ActivityPub
- Wakapi
- Add a way to define services per host and refer to them by hostname
- helios as file and db server, apollo as services and reverse proxy
- 0.3.0
- Integration tests for all services
- Migrate services from helios
# Modules
The nixosModules and homeModules exposed by this flake are slightly out of the norm.
Option declarations for user specific configuration are kept to:
- homeModules for CLI
- nixosModules for desktop
System configurations, to this end, should include the window manager, lockscreen, terminal etc. for that system.
These desktop programs will be **configured** in home-manager for each user, but those configurations consult the osConfig variable passed in by home-manager.
# System Setup
The following files are **required** for system activation:
- /persist/home/${mainUser}/.ssh/id_ed25519
This private key will be used by sops-nix to decrypt the secrets in [this encrypted file](secrets/secrets.yaml). The secrets inside the yaml file should also be set, or otherwise removed alongside their declarations , found [here](modules/nixos/system/secrets.nix) and references.
```bash
# On the target machine
# Boot into the NixOS installer
sudo passwd
# On the host machine
deploy --user "rafiq" --ip "10.10.0.102" --hostname "apollo"
```
### From a Local NixOS Installer
The installation may run out of space when installing from an install ISO. In that case, use Disko to format the drives first, then create a `/mnt/tmp` directory and set it as TMPDIR for nixos-install.
```bash
sudo su
nix --extra-experimental-features "nix-command flakes" run github:nix-community/disko/master -- --mode destroy,format,mount --flake github:rrvsh/pantheon#<HOSTNAME>
# Copy SSH key to /persist/home/rafiq/.ssh
mkdir /mnt/tmp
TMPDIR=/mnt/tmp nixos-install --flake github:rrvsh/pantheon#<HOSTNAME> --no-root-password
reboot
```
# Impermanence
System and user state is stored under /persist. Anything not declared under
`{environment,home}.persistence` is deleted on system boot.

29
docs/README.md Normal file
View file

@ -0,0 +1,29 @@
# Pantheon
This flake serves as a monorepo for my systems (using IaC), dotfiles, and scripts.
It's hosted at https://git.rrv.sh/rrvsh/pantheon, and mirrored to https://github.com/rrvsh/pantheon.
## Structure
The system configurations are defined in [`flake.manifest`](nix/manifest.nix).
`flake.manifest.owner` provides the attributes for the administrator user, including username and pubkey.
`flake.manifest.hosts` provides the specifications for the system configurations that should be exposed by the flake as nixosConfigurations.
`flake.modules.nixos.*` provide NixOS options and configurations.
The attribute `flake.modules.nixos.default` provides options that will be applied to every system of that class.
You can use it as seen [here](nix/modules/flake/home-manager.nix):
```nix
flake.modules.nixos.default.imports = [ inputs.home-manager.nixosModules.default ];
```
The other attributes under `flake.modules.nixos` should be opt-in, i.e. provide options that will be set in the profiles.
`flake.profiles.nixos` provides profiles which use the options defined in `flake.modules.nixos` to define different roles for each system, such as graphical, laptop, headless, etc.
Options should not be defined here.
`flake.contracts.nixos.*` will provide contracts, such as reverse proxies or databases, which will configure options on the provider and receiver host.
## Acknowledgements
Thanks to the following for inspiring this configuration. I highly recommend you look through their writings and configurations.
- [ornicar](https://github.com/ornicar/dotfiles) which is where I first heard of NixOS
- [No Boilerplate](https://www.youtube.com/watch?v=CwfKlX3rA6E&pp=0gcJCfwAo7VqN5tD) for making me finally try the OS
- [ryan4yin](https://nixos-and-flakes.thiscute.world/) for being an amazing introduction to NixOS, home-manager, and flakes
- [NotAShelf](https://github.com/NotAShelf/) for their blog and for the wonderful [NVF](https://github.com/notashelf/nvf)
- [mightyiam](https://github.com/mightyiam/infra) for their infrastructure repo using flake-parts
- [drupol](https://not-a-number.io/2025/refactoring-my-infrastructure-as-code-configurations/) for this blog post which convinced me to rebase my infra to use flake-parts

2
docs/cheatsheet.md Normal file
View file

@ -0,0 +1,2 @@
# cheatsheet
`__curPos.file` will give the full evaluated path of the nix file it is called in. See [this issue](https://github.com/NixOS/nix/issues/5897#issuecomment-1012165198) for more information.

1208
flake.lock generated

File diff suppressed because it is too large Load diff

View file

@ -1,43 +1,54 @@
{
# TODO: use flake-parts and remove snowfall-lib
outputs =
inputs:
inputs.snowfall-lib.mkFlake {
inherit inputs;
src = ./.;
snowfall.namespace = "pantheon";
};
inputs = {
# We use nixos-unstable as everything is cached.
# nixos-unstable provides a binary cache for all packages.
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
# My fork for random shit
rrvsh-nixpkgs.url = "github:rrvsh/nixpkgs/librechat-module";
# import-tree lets us use less imports = []
import-tree.url = "github:vic/import-tree";
# The following are used for less boilerplate.
flake-parts.url = "github:hercules-ci/flake-parts";
#TODO: remove snowfall
snowfall-lib = {
url = "github:snowfallorg/lib";
# systems provides a list of supported nix systems.
systems.url = "github:nix-systems/default";
# flake-parts lets us define flake modules.
flake-parts = {
url = "github:hercules-ci/flake-parts";
inputs.nixpkgs-lib.follows = "nixpkgs";
};
# home-manager manages our user packages and dotfiles
home-manager = {
url = "github:nix-community/home-manager";
inputs.nixpkgs.follows = "nixpkgs";
};
# Various nix things
nur.url = "github:nix-community/NUR";
sops-nix.url = "github:Mic92/sops-nix";
disko.url = "github:nix-community/disko";
home-manager.url = "github:nix-community/home-manager";
impermanence.url = "github:nix-community/impermanence";
nix-index-database.url = "github:nix-community/nix-index-database";
stylix.url = "github:nix-community/stylix";
systems.url = "github:nix-systems/default";
# Packages and services that we don't use nixpkgs for.
rrv-sh.url = "github:rrvsh/rrv.sh";
nvf.url = "github:rrvsh/nvf/uv-nvim";
stable-diffusion-webui-nix.url = "github:janrupf/stable-diffusion-webui-nix";
zjstatus.url = "github:dj95/zjstatus";
# import-tree imports all nix files in a given directory.
import-tree.url = "github:vic/import-tree";
# files lets us write text files and automatically add checks for them
files.url = "github:mightyiam/files";
# text.nix lets us easily define markdown text to pass to files
text.url = "github:rrvsh/text.nix";
# make-shells.<name> creates devShells and checks
make-shell = {
url = "github:nicknovitski/make-shell";
inputs.flake-compat.follows = "dedupe_flake-compat";
};
# git-hooks ensures nix flake check is ran before commits
git-hooks = {
url = "github:cachix/git-hooks.nix";
inputs = {
flake-compat.follows = "dedupe_flake-compat";
nixpkgs.follows = "nixpkgs";
gitignore.follows = "dedupe_gitignore";
};
};
# The following are not used but are here for deduplication.
dedupe_flake-compat.url = "github:edolstra/flake-compat";
dedupe_gitignore = {
url = "github:hercules-ci/gitignore.nix";
inputs.nixpkgs.follows = "nixpkgs";
};
};
outputs =
inputs:
inputs.flake-parts.lib.mkFlake { inherit inputs; } (
(inputs.import-tree ./nix)
// {
systems = import inputs.systems;
}
);
}

View file

@ -1,37 +0,0 @@
{ pkgs, inputs, ... }:
{
imports = [ inputs.nix-index-database.hmModules.nix-index ];
programs = {
tealdeer.enable = true;
direnv = {
enable = true;
nix-direnv.enable = true;
};
zoxide.enable = true;
nix-index.enable = true;
nix-index-database.comma.enable = true;
};
persistDirs = [ ".local/share/zoxide" ];
home = {
shellAliases = {
windows = "sudo systemctl reboot --boot-loader-entry=auto-windows";
v = "$EDITOR";
e = "edit";
cd = "z"; # zoxide
ai = "aichat -r %shell% -e";
};
packages = with pkgs; [
ripgrep
aichat
pantheon.rebuild
pantheon.deploy
pantheon.edit
pantheon.commit
];
};
xdg.configFile."aichat/config.yaml".text = ''
model: gemini:gemini-2.0-flash
clients:
- type: gemini
'';
}

View file

@ -1,25 +0,0 @@
{ lib }:
{
blink-cmp = {
enable = true;
friendly-snippets.enable = true;
sourcePlugins.ripgrep.enable = true;
setupOpts = {
# Disable completion in markdown files
# TODO: Disable completion when in comments
enabled =
lib.generators.mkLuaInline
# lua
''
function()
return not vim.tbl_contains({"markdown"}, vim.bo.filetype)
and vim.bo.buftype ~= "prompt"
and vim.b.completion ~= false
end
'';
completion.documentation.auto_show_delay_ms = 0;
# Show e.g. function parameters
signature.enabled = true;
};
};
}

View file

@ -1,3 +0,0 @@
{
whichKey.enable = true;
}

View file

@ -1,36 +0,0 @@
{
enableExtraDiagnostics = true;
enableFormat = true;
enableTreesitter = true;
bash.enable = true;
clang.enable = true;
csharp.enable = true;
css.enable = true;
go.enable = true;
html.enable = true;
lua.enable = true;
markdown = {
enable = true;
extensions.markview-nvim.enable = true;
format.type = "prettierd";
};
nix = {
enable = true;
format.type = "nixfmt";
lsp.server = "nil";
};
python = {
enable = true;
format.type = "ruff";
lsp.server = "pyright";
uv.enable = true;
uv.setupOpts.picker_integration = true;
};
rust.enable = true;
rust.crates.enable = true;
ts.enable = true;
ts.extensions.ts-error-translator.enable = true;
typst.enable = true;
typst.extensions.typst-preview-nvim.enable = true;
yaml.enable = true;
}

View file

@ -1,17 +0,0 @@
{
enable = true;
# Show virtual text hints
inlayHints.enable = true;
lightbulb.enable = true;
# Show icons for lsp actions
lspkind.enable = true;
null-ls.enable = true;
otter-nvim = {
enable = true;
setupOpts = {
buffers.set_filetype = true;
buffers.write_to_disk = true;
handle_leading_whitespace = true;
};
};
}

View file

@ -1,9 +0,0 @@
{
harpoon = {
enable = true;
mappings.listMarks = "<leader>ml";
mappings.markFile = "<leader>mm";
setupOpts.defaults.save_on_toggle = true;
setupOpts.defaults.sync_on_ui_close = true;
};
}

View file

@ -1,28 +0,0 @@
{ pkgs }:
{
luasnip = {
enable = true;
setupOpts.enable_autosnippets = true;
providers = with pkgs.vimPlugins; [ vim-snippets ];
loaders = "require('luasnip.loaders.from_vscode').lazy_load()";
customSnippets.snipmate = {
nix = [
{
trigger = "mod";
description = "empty module";
body = # nix
''
{config, lib, ...}:
let
cfg = config.$1;
in
{
options.$1 = { $2 };
config = $3;
}
'';
}
];
};
};
}

View file

@ -1,10 +0,0 @@
{
lualine = {
enable = true;
refresh = {
statusline = 10;
winbar = 10;
};
#TODO: rice lualine
};
}

View file

@ -1,16 +0,0 @@
{
borders = {
enable = true;
globalStyle = "rounded";
};
breadcrumbs.enable = true;
# Show color values e.g. #ffffff
colorizer.enable = true;
# Highlight matching symbols
illuminate.enable = true;
noice.enable = true;
noice.setupOpts.notify.enabled = false;
# Make folds look nicer
nvim-ufo.enable = true;
smartcolumn.enable = true;
}

View file

@ -1,11 +0,0 @@
{
motion.hop.enable = true;
yazi-nvim = {
enable = true;
mappings = {
openYazi = "<leader>tt";
openYaziDir = "<leader>TT";
};
setupOpts.open_for_directories = true;
};
}

View file

@ -1,7 +0,0 @@
{
indent-blankline.enable = true;
fidget-nvim.enable = true;
fidget-nvim.setupOpts.notification.override_vim_notify = true;
nvim-web-devicons.enable = true;
rainbow-delimiters.enable = true;
}

View file

@ -1,47 +0,0 @@
{
pkgs,
lib,
inputs,
...
}:
{
imports = [ inputs.nvf.homeManagerModules.default ];
home.sessionVariables.EDITOR = "nvim";
programs.nvf.enable = true;
programs.nvf.settings.vim = {
syntaxHighlighting = true;
hideSearchHighlight = true;
searchCase = "ignore";
undoFile.enable = true;
telescope.enable = true;
fzf-lua.enable = true;
git.enable = true;
autopairs.nvim-autopairs.enable = true;
autocomplete = import ./_nvf/autocomplete.nix { inherit lib; };
binds = import ./_nvf/binds.nix;
languages = import ./_nvf/languages.nix;
lsp = import ./_nvf/lsp.nix;
navigation = import ./_nvf/navigation.nix;
notes.todo-comments.enable = true;
options = {
autoindent = true;
backspace = "indent,eol,start";
cursorline = true;
expandtab = true;
shiftwidth = 2;
smartindent = true;
tabstop = 2;
};
snippets = import ./_nvf/snippets.nix { inherit pkgs; };
statusline = import ./_nvf/statusline.nix;
treesitter = {
autotagHtml = true;
fold = true;
indent.disable = [ "markdown" ];
textobjects.enable = true;
};
ui = import ./_nvf/ui.nix;
utility = import ./_nvf/utility.nix;
visuals = import ./_nvf/visuals.nix;
};
}

View file

@ -1,23 +0,0 @@
{ pkgs, ... }:
{
home = {
packages = [ pkgs.fastfetch ];
sessionVariables.FETCH = "hyfetch";
shellAliases.fetch = "hyfetch";
};
programs.hyfetch = {
enable = true;
settings = {
preset = "bisexual";
mode = "rgb";
light_dark = "dark";
lightness = 0.5;
color_align = {
# Flag color alignment
mode = "horizontal";
fore_back = null;
};
backend = "fastfetch";
};
};
}

View file

@ -1,8 +0,0 @@
{
home.sessionVariables.FILE_BROWSER = "yazi";
programs.yazi = {
enable = true;
shellWrapperName = "t";
settings.mgr.sort_by = "natural";
};
}

View file

@ -1,5 +0,0 @@
{
programs.fzf.enable = true;
#TODO: fish
programs.fzf.enableZshIntegration = true;
}

View file

@ -1,61 +0,0 @@
{
osConfig,
inputs,
pkgs,
...
}:
let
zjstatus = inputs.zjstatus.packages.${pkgs.stdenv.hostPlatform.system}.default;
in
{
home.sessionVariables.MULTIPLEXER = "zellij";
# Persists sessions
persistDirs = [ "/.cache/zellij" ];
programs.zellij = {
enable = true;
#TODO: fish
enableZshIntegration = true;
settings = {
pane_frames = false;
show_startup_tips = false;
show_release_notes = false;
};
};
xdg.configFile."zellij/layouts/default.kdl".text = # kdl
''
layout {
default_tab_template {
pane size=1 borderless=true {
plugin location="file:${zjstatus}/bin/zjstatus.wasm" {
format_left "{mode} ${osConfig.hostname}"
format_center "{tabs}"
format_right "{datetime}"
format_space ""
format_hide_on_overlength "true"
format_precedence "lrc"
border_enabled "false"
hide_frame_for_single_pane "false"
mode_default_to_mode "normal"
mode_normal "#[bg=#89B4FA] {name} "
mode_locked "#[bg=#f55e18] {name} "
mode_session "#[bg=#00ff00] {name} "
tab_normal "#[fg=#6C7086] {index} "
tab_active "#[fg=#9399B2,bold,italic] {index} "
tab_display_count "3" // limit to showing 3 tabs
tab_truncate_start_format "..."
tab_truncate_end_format "..."
//TODO: disable if we are not on ssh
datetime "#[fg=#6C7086,bold] {format}"
datetime_format "%H:%M:%S"
datetime_timezone "Asia/Singapore"
}
}
children
}
}
'';
}

View file

@ -1,28 +0,0 @@
{ lib, pkgs, ... }:
let
inherit (builtins) toString;
inherit (lib) getExe mkOrder;
screensaverTimeout = toString 100;
screensaverCommand = "${getExe pkgs.cbonsai} -S -w 0.1 -L 40 -M 2 -b 2";
in
{
home.shell.enableShellIntegration = true;
home.sessionVariables.SHELL = "fish";
programs.fish.enable = true;
programs.zsh.initContent =
mkOrder 1200
# zsh
''
precmd() {
TMOUT=${screensaverTimeout}
}
TRAPALRM() {
TMOUT=1
${screensaverCommand}
# If we exit, assume the previous command was exited out of
TMOUT=${screensaverTimeout}
zle reset-prompt
}
'';
}

View file

@ -1,25 +0,0 @@
{
home.sessionVariables.GIT_CONFIG_GLOBAL = "$HOME/.config/git/config";
home.shellAliases = {
gs = "git status";
gc = "git commit";
gcam = "git commit -am";
gu = "git push";
gy = "git pull";
gdh = "git diff HEAD";
};
programs.git = {
enable = true;
userName = "Mohammad Rafiq";
userEmail = "rafiq@rrv.sh";
signing.key = "~/.ssh/id_ed25519.pub";
signing.signByDefault = true;
extraConfig = {
init.defaultBranch = "prime";
push.autoSetupRemote = true;
pull.rebase = false;
core.editor = "$EDITOR";
gpg.format = "ssh";
};
};
}

View file

@ -1,28 +0,0 @@
{ lib, ... }:
let
inherit (lib.strings) concatStrings;
in
{
programs.starship = {
#TODO: enable after switching to fish
enable = false;
# settings = {
# add_newline = false;
# format = concatStrings [
# # First Line
# ## Left Prompt
# "$hostname$directory"
# "$fill"
# ## Right Prompt
# "$all"
# # Second Line
# ## Left Prompt
# "$character"
# ];
# git_branch.format = "[$symbol$branch(:$remote_branch)]($style) ";
# shlvl.disabled = false;
# username.disabled = true;
# fill.symbol = " ";
# };
};
}

View file

@ -1,15 +0,0 @@
{
inputs,
osConfig,
lib,
...
}:
let
inherit (lib) singleton optional;
inherit (inputs) import-tree;
in
{
imports =
(optional osConfig.desktop.enable (import-tree ./desktop))
++ singleton (import-tree ./cli);
}

View file

@ -1,46 +0,0 @@
{
lib,
inputs,
pkgs,
...
}:
let
inherit (builtins) map listToAttrs;
inherit (lib.lists) findFirstIndex;
inherit (inputs.nur.legacyPackages.${pkgs.stdenv.hostPlatform.system}.repos.rycee) firefox-addons;
profiles = listToAttrs (
map (name: {
inherit name;
# If there are duplicate profile names, findFirstIndex will cause issues.
value = profileCfg (findFirstIndex (x: x == name) null syncedProfiles);
}) syncedProfiles
);
syncedProfiles = [
"rafiq"
"test"
];
profileCfg = id: {
inherit id;
settings."extensions.autoDisableScopes" = 0; # Auto enable extensions
#TODO: add default seach unduck and add rest of extensions
extensions = {
force = true;
packages = with firefox-addons; [
darkreader
gesturefy
sponsorblock
ublock-origin
];
};
};
in
{
home.sessionVariables.BROWSER = "firefox";
persistDirs = [ ".mozilla/firefox" ];
programs.firefox = {
enable = true;
inherit profiles;
};
stylix.targets.firefox.colorTheme.enable = true;
stylix.targets.firefox.profileNames = syncedProfiles;
}

View file

@ -1,19 +0,0 @@
{ pkgs, ... }:
{
persistDirs = [
"docs"
"repos"
"vids"
"tmp"
".cache/Smart Code ltd/Stremio"
".local/share/Smart Code ltd/Stremio"
];
programs = {
obs-studio.enable = true;
vesktop.enable = true;
thunderbird.enable = true;
thunderbird.profiles.rafiq.isDefault = true;
};
home.packages = with pkgs; [ stremio ];
stylix.image = ./wallpaper.png;
}

View file

@ -1,3 +0,0 @@
{
home.sessionVariables.LAUNCHER = "fuzzel";
}

View file

@ -1,29 +0,0 @@
let
styling = {
halign = "center";
valign = "center";
zindex = 1;
shadow_passes = 5;
shadow_size = 5;
};
in
{
home.sessionVariables.LOCKSCREEN = "hyprlock";
programs.hyprlock.settings = {
general.hide_cursor = true;
general.ignore_empty_input = true;
background.blur_passes = 5;
background.blur_size = 5;
label = {
text = ''hi, $USER.'';
font_size = 32;
position = "0, 0";
}// styling;
input-field = {
placeholder_text = "";
fade_on_empty = true;
size = "200, 45";
position = "0, -5%";
} // styling;
};
}

View file

@ -1,200 +0,0 @@
{
xdg.configFile."vlc/vlcrc".text = ''
[visual] # Visualizer filter
[glspectrum] # 3D OpenGL spectrum visualization
[wall] # Wall video filter
[panoramix] # Panoramix: wall with overlap video filter
[clone] # Clone video filter
[yuv] # YUV video output
[xdg_shell] # XDG shell surface
[xcb_xv] # XVideo output (XCB)
[xcb_x11] # X11 video output (XCB)
[xcb_window] # X11 video window (XCB)
[wl_shell] # Wayland shell surface
[vmem] # Video memory output
[vdummy] # Dummy video output
[gl] # OpenGL video output
[flaschen] # Flaschen-Taschen video output
[fb] # GNU/Linux framebuffer video output
[transform] # Video transformation filter
[sharpen] # Sharpen video filter
[sepia] # Sepia video filter
[scene] # Scene video filter
[rotate] # Rotate video filter
[puzzle] # Puzzle interactive game video filter
[postproc] # Video post processing filter
[posterize] # Posterize video filter
[motionblur] # Motion blur filter
[mirror] # Mirror video filter
[hqdn3d] # High Quality 3D Denoiser filter
[grain] # Grain video filter
[gradient] # Gradient video filter
[gradfun] # Gradfun video filter
[gaussianblur] # Gaussian blur video filter
[fps] # FPS conversion video filter
[extract] # Extract RGB component video filter
[erase] # Erase video filter
[deinterlace] # Deinterlacing video filter
[croppadd] # Video cropping filter
[colorthres] # Color threshold filter
[canvas] # Canvas video filter
[bluescreen] # Bluescreen video filter
[blendbench] # Blending benchmark filter
[ball] # Ball video filter
[antiflicker] # antiflicker video filter
[anaglyph] # Convert 3D picture to anaglyph image video filter
[alphamask] # Alpha mask video filter
[adjust] # Image properties filter
[swscale] # Video scaling filter
[vaapi_filters] # Video Accelerated API filters
[svg] # svg
[freetype] # Freetype2 font renderer
[stream_out_transcode] # Transcode stream output
[stats] # Writes statistic info about stream
[stream_out_standard] # Standard stream output
[smem] # Stream output to memory buffer
[setid] # Change the id of an elementary stream
[stream_out_rtp] # RTP stream output
[record] # Record stream output
[mosaic_bridge] # Mosaic bridge stream output
[es] # Elementary stream output
[display] # Display stream output
[delay] # Delay a stream
[stream_out_chromecast] # Chromecast stream output
[bridge] # Bridge stream output
[prefetch] # Stream prefetch filter
[subsdelay] # Subtitle delay
[rss] # RSS and Atom feed display
[remoteosd] # Remote-OSD over VNC
[mosaic] # Mosaic video sub source
[marq] # Marquee display
[logo] # Logo sub source
[dynamicoverlay] # Dynamic video overlay
[audiobargraph_v] # Audio Bar Graph Video sub source
[upnp] # Universal Plug'n'Play
[sap] # Network streams (SAP)
[podcast] # Podcasts
[mpegvideo] # MPEG-I/II video packetizer
[mux_ts] # TS muxer (libdvbpsi)
[ps] # PS muxer
[mux_ogg] # Ogg/OGM muxer
[mp4] # MP4/MOV muxer
[avi] # AVI muxer
[asf] # ASF muxer
[rtsp] # Legacy RTSP VoD server
[logger] # File logging
[gnutls] # GNU TLS transport layer security
[audioscrobbler] # Submission of played songs to last.fm
[folder] # Folder meta data
[lua] # Lua interpreter
[syslog] # System logger (syslog)
[file] # File logger
[console] # Console logger
[file] # Secrets are stored on a file without any encryption
[skins2] # Skinnable Interface
[qt] # Qt interface
qt-privacy-ask=0
[ncurses] # Ncurses interface
[vc1] # VC1 video demuxer
[ts] # MPEG Transport Stream demuxer
[subtitle] # Text subtitle parser
[rawvid] # Raw video demuxer
[rawdv] # DV (Digital Video) demuxer
[rawaud] # Raw audio demuxer
[ps] # MPEG-PS demuxer
[playlist] # Playlist
[mp4] # MP4 stream demuxer
[mod] # MOD demuxer (libmodplug)
[mkv] # Matroska stream demuxer
[mjpeg] # M-JPEG camera demuxer
[image] # Image demuxer
[h26x] # H264 video demuxer
[es] # MPEG-I/II/4 / A52 / DTS / MLP audio
[diracsys] # Dirac video demuxer
[demuxdump] # File dumper
[avi] # AVI demuxer
[avformat] # Avformat demuxer
[adaptive] # Unified adaptive streaming for DASH/HLS
[oldrc] # Remote control interface
[netsync] # Network synchronization
[motion] # motion control interface
[gestures] # Mouse gestures control interface
[vorbis] # Vorbis audio decoder
[ttml] # TTML subtitles decoder
[theora] # Theora video decoder
[telx] # Teletext subtitles decoder
[svgdec] # SVG video decoder
[svcdsub] # Philips OGT (SVCD subtitle) decoder
[subsusf] # USF subtitles decoder
[subsdec] # Text subtitle decoder
[spudec] # DVD subtitles decoder
[speex] # Speex audio decoder
[schroedinger] # Dirac video decoder using libschroedinger
[libass] # Subtitle renderers using libass
[kate] # Kate overlay decoder
[jpeg] # JPEG image decoder
[fluidsynth] # FluidSynth MIDI synthesizer
[dvbsub] # DVB subtitles decoder
[ddummy] # Dummy decoder
[cc] # Closed Captions decoder
[avcodec] # FFmpeg audio/video decoder
[a52] # ATSC A/52 (AC-3) audio decoder
[amem] # Audio memory output
[alsa] # ALSA audio output
[afile] # File audio output
[stereo_widen] # Simple stereo widening effect
[speex_resampler] # Speex resampler
[spatializer] # Audio Spatializer
[spatialaudio] # Ambisonics renderer and binauralizer
[scaletempo] # Audio tempo scaler synched with rate
[scaletempo_pitch] # Pitch Shifter
[samplerate] # Secret Rabbit Code (libsamplerate) resampler
[remap] # Audio channel remapper
[param_eq] # Parametric Equalizer
[normvol] # Volume normalizer
[mono] # Stereo to mono downmixer
[headphone] # Headphone virtual spatialization effect
[gain] # Gain control filter
[equalizer] # Equalizer with 10 bands
[compressor] # Dynamic range compressor
[chorus_flanger] # Sound Delay
[audiobargraph_a] # Audio part of the BarGraph function
[udp] # UDP stream output
[access_output_srt] # SRT stream output
[access_output_rist] # RIST stream output
[access_output_livehttp] # HTTP Live streaming output
[http] # HTTP stream output
[file] # File stream output
[xcb_screen] # Screen capture (with X11/XCB)
[vdr] # VDR recordings
[v4l2] # Video4Linux input
[udp] # UDP input
[timecode] # Time code subpicture elementary stream generator
[smb] # SMB input
[shm] # Shared memory framebuffer
[sftp] # SFTP input
[satip] # SAT>IP Receiver Plugin
[rtp] # Real-Time Protocol (RTP) input
[rist] # RIST input
[live555] # RTP/RTSP/SDP demuxer (using Live555)
[linsys_hdsdi] # HD-SDI Input
[libbluray] # Blu-ray Disc support (libbluray)
[access] # HTTPS input
[http] # HTTP input
[ftp] # FTP input
[filesystem] # File input
[dvdread] # DVDRead Input (no menu support)
[dvdnav] # DVDnav Input
[dvb] # DVB input with v4l2 support
[dtv] # Digital Television and Radio
[cdda] # Audio CD input
[avio] # libavformat AVIO access
[access_srt] # SRT input
[access_mms] # Microsoft Media Server (MMS) input
[imem] # Memory input
[concat] # Concatenated inputs
[access_alsa] # ALSA audio capture
[core] # core program
metadata-network-access=1
'';
}

View file

@ -1,5 +0,0 @@
{
home.sessionVariables.NOTIFICATION_DAEMON = "mako";
services.mako.enable = true;
services.mako.settings.default-timeout = 10000;
}

View file

@ -1,53 +0,0 @@
{ pkgs, ... }:
{
home.sessionVariables.STATUS_BAR = "waybar";
stylix.targets.waybar.addCss = false;
programs.waybar = {
enable = true;
settings = [
{
#TODO: review the rest of the modules to see what else can be added
layer = "top";
modules-left = [
"pulseaudio"
];
modules-right = [
"battery"
"clock"
];
"pulseaudio" = {
format = "{icon} {volume}%";
format-muted = "";
format-icons.default = [
""
""
];
on-click = "${pkgs.pulseaudio}/bin/pactl set-sink-mute @DEFAULT_SINK@ toggle";
};
"clock" = {
interval = 1;
format = "{:%F %T}";
};
"battery" = {
interval = 1;
bat-compatibility = true;
};
}
];
style = # css
''
window#waybar {
background-color: rgba(0, 0, 0, 0);
}
#pulseaudio,
#battery,
#clock {
padding-top: 5px;
padding-bottom: 5px;
padding-right: 5px;
color: #ffffff;
}
'';
};
}

View file

@ -1,5 +0,0 @@
{
home.sessionVariables.TERMINAL = "ghostty -e";
programs.ghostty.enable = true;
programs.ghostty.settings.confirm-close-surface = false;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 MiB

View file

@ -1,14 +0,0 @@
{
animation = [ "workspaces, 1, 1, default" ];
general = {
border_size = 2;
gaps_in = 0;
gaps_out = 0;
resize_on_border = true;
};
decoration = {
rounding = 10;
rounding_power = 2;
inactive_opacity = 0.9;
};
}

View file

@ -1,56 +0,0 @@
{ pkgs, ... }:
{
"$hypr" = "CTRL_SUPER_ALT_SHIFT";
"$meh" = "CONTROL_SHIFT_ALT";
bind = [
"$hypr, Q, exec, uwsm stop"
"SUPER, W, killactive"
"SUPER, return, exec, uwsm app -- $TERMINAL"
"SUPER, O, exec, uwsm app -- $BROWSER"
"SUPER, Escape, exec, uwsm app -- $LOCKSCREEN"
#TODO:add file browser
#TODO: make it directional
"SUPER, H, cyclenext, visible"
"SUPER, L, cyclenext, visible prev"
"SUPER_ALT, H, movewindow, l"
"SUPER_ALT, J, movewindow, d"
"SUPER_ALT, K, movewindow, u"
"SUPER_ALT, L, movewindow, r"
"ALT_SHIFT, H, resizeactive, -10% 0"
"ALT_SHIFT, J, resizeactive, 0 -10%"
"ALT_SHIFT, K, resizeactive, 0 10%"
"ALT_SHIFT, L, resizeactive, 10% 0"
"SUPER_CTRL, H, workspace, r-1"
"SUPER_CTRL, L, workspace, r+1"
"$hypr, H, movetoworkspace, r-1"
"$hypr, L, movetoworkspace, r+1"
"$hypr, V, togglefloating"
];
bindr = [
# Activates on SUPER without any other modifier
"SUPER, Super_L, exec, uwsm app -- $($LAUNCHER --launch-prefix=\"uwsm app -- \")"
];
bindle = [
"SUPER, 6, exec, ${pkgs.wireplumber}/bin/wpctl set-volume -l 1.5 @DEFAULT_AUDIO_SINK@ 5%-"
"SUPER, 7, exec, ${pkgs.playerctl}/bin/playerctl previous"
"SUPER, 8, exec, ${pkgs.playerctl}/bin/playerctl -a play-pause"
"SUPER, 9, exec, ${pkgs.playerctl}/bin/playerctl next"
"SUPER, 0, exec, ${pkgs.wireplumber}/bin/wpctl set-volume -l 1.5 @DEFAULT_AUDIO_SINK@ 5%+"
"ALT, mouse_up, resizeactive, 10% 10%"
"ALT, mouse_down, resizeactive, -10% -10%"
];
bindm = [
"ALT, mouse:272, movewindow"
"ALT, mouse:273, resizeactive"
];
bindc = [
"ALT, mouse:272, togglefloating"
];
}

View file

@ -1,21 +0,0 @@
{ lib, pkgs, ... }:
let
inherit (lib) mkMerge;
in
{
wayland.windowManager.hyprland.settings = mkMerge [
(import ./_hyprland/decoration.nix)
(import ./_hyprland/keybinds.nix { inherit pkgs; })
{
ecosystem.no_update_news = true;
xwayland.force_zero_scaling = true;
monitor = [ ", preferred, auto, 1" ];
exec-once = [
"uwsm app -- $LOCKSCREEN"
"uwsm app -- $NOTIFICATION_DAEMON"
"uwsm app -- $STATUS_BAR"
];
}
];
# TODO: add gamescope here or in nixos desktop module
}

View file

@ -1,78 +0,0 @@
{
users.rafiq = {
primary = true;
email = "rafiq@rrv.sh";
alternate-emails = [
"mohammadrafiq@rrv.sh"
"mohammadrafiq567@gmail.com"
];
pubkey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILdsZyY3gu8IGB8MzMnLdh+ClDxQQ2RYG9rkeetIKq8n";
};
entrypoints = {
# For services that should only have one instance across the whole
# flake, define them here and they will get provisioned on those
# hosts, with whatever depends on them configured via that hostname.
nginx.host = "apollo";
ssh.host = "apollo";
};
# This will define all the hosts exposed by the flake and designate the
# modules and services, along with defining the hardware configuration
# for each host.
# <name> of each attr set will resolve to the host's hostname.
hosts.nemesis = {
platform = "amd";
gpu = "nvidia";
ephemeralRoot = true;
boot-drive = "/dev/disk/by-id/nvme-CT2000P3SSD8_2325E6E77434";
bootloader = "systemd-boot";
# Enables dotfiles and desktop environment/services.
desktop.enable = true;
extraCfg = { };
};
hosts.apollo = {
platform = "intel";
ephemeralRoot = true;
bootloader = "systemd-boot";
boot-drive = "/dev/disk/by-id/nvme-eui.002538d221b47b01";
# Public services will be exposed to the web server.
public-services = [
{
name = "librechat";
domain = "chat.bwfiq.com";
}
{
name = "forgejo";
domain = "git.rrv.sh";
}
{
name = "rrv-sh";
domain = "rrv.sh";
}
{
name = "immich";
domain = "photos.bwfiq.com";
}
{
name = "aenyrathia-wiki";
domain = "aenyrathia.wiki";
}
];
# Internal services will be exposed with tailscale only.
internal-services = [
"mongodb"
"mariadb"
"postgresql"
"redis"
];
extraCfg = { };
};
host.helios = {
platform = "intel";
boot-drive = "nvme-eui.6479a784aad00284";
ephemeralRoot = true;
bootloader = "systemd-boot";
extraCfg = { };
};
}

View file

@ -1,57 +0,0 @@
{ lib, ... }:
let
inherit (lib) mkOption singleton;
inherit (lib.types)
int
str
port
path
attrs
;
inherit (lib.strings) splitString;
inherit (builtins) length concatStringsSep tail;
in
rec {
# Helpers
splitDomain = domain: splitString "." domain;
shortenList =
count: list:
let
len = length list;
in
if len <= count then list else (shortenList count (tail list));
# Modules
mkAttrOption = mkOption {
type = attrs;
default = { };
};
mkIntOption =
default:
mkOption {
type = int;
inherit default;
};
mkStrOption = mkOption {
type = str;
default = "";
};
mkPortOption =
default:
mkOption {
type = port;
inherit default;
};
mkPathOption =
default:
mkOption {
type = path;
inherit default;
};
# Domains
isRootDomain = domain: length (splitDomain domain) <= 2;
mkRootDomain = domain: concatStringsSep "." (shortenList 2 (splitDomain domain));
mkWildcardDomain = rootDomain: concatStringsSep "." ((singleton "*") ++ (splitDomain rootDomain));
mkHost = domain: if isRootDomain domain then domain else mkWildcardDomain (mkRootDomain domain);
}

View file

@ -1,66 +0,0 @@
{ lib, ... }:
let
inherit (builtins) toString;
inherit (lib)
mkMerge
mkEnableOption
singleton
mkIf
;
inherit (lib.pantheon)
mkAttrOption
mkRootDomain
mkPortOption
mkStrOption
;
networkingConfig =
{
config,
cfg,
name,
}:
mkIf (cfg.domain != "") {
assertions = singleton {
assertion = config.server.web-servers.nginx.enable;
message = "You must enable a web server if you want to set server.web-apps.${name}.domain.";
};
server.networking.ddns.domains = singleton (mkRootDomain cfg.domain);
server.web-servers.nginx.proxies = singleton {
source = cfg.domain;
target = "http://${config.hostname}:${toString cfg.port}";
};
};
in
{
modules.mkWebApp =
{
config,
name,
defaultPort,
persistDirs ? [ ],
#TODO: specify required secrets
extraOptions ? { },
extraConfig ? { },
}:
let
cfg = config.server.web-apps.${name};
in
{
options.server.web-apps.${name} = {
enable = mkEnableOption "";
port = mkPortOption defaultPort;
domain = mkStrOption;
openFirewall = mkEnableOption "";
extraCfg = mkAttrOption;
} // extraOptions;
config = mkIf cfg.enable (mkMerge [
{
inherit persistDirs;
networking.firewall = mkIf cfg.openFirewall { allowedTCPPorts = singleton cfg.port; };
}
(networkingConfig { inherit config cfg name; })
extraConfig
]);
};
}

View file

@ -1,38 +0,0 @@
{
config,
lib,
inputs,
...
}:
let
inherit (lib) mkOption;
inherit (lib.types) listOf str;
in
{
imports = [ inputs.impermanence.homeManagerModules.impermanence ];
options.persistDirs = mkOption {
type = listOf str;
default = [ ];
};
config = {
# Helper options
home.persistence."/persist/home/${config.home.username}" = {
directories = config.persistDirs;
allowOther = true;
};
# Global options
persistDirs = [
# For system activation
".ssh"
".config/sops/age"
];
programs.ssh.enable = true;
# To set colors properly when on ssh
programs.ssh.extraConfig = ''
Host *
SetEnv TERM=xterm-256color
'';
home.stateVersion = "24.11";
};
}

View file

@ -1,112 +0,0 @@
{
inputs,
lib,
config,
...
}:
let
inherit (lib) mkOption singleton;
inherit (lib.types)
listOf
str
coercedTo
submodule
shellPackage
;
inherit (lib.pantheon) mkStrOption;
inherit (lib.snowfall.fs) get-file;
rootDir = submodule {
options = {
directory = mkOption { type = str; };
user = mkOption {
type = str;
default = "root";
};
group = mkOption {
type = str;
default = "root";
};
mode = mkOption {
type = str;
default = "0755";
};
};
};
in
{
imports = [
inputs.sops-nix.nixosModules.sops
inputs.stylix.nixosModules.stylix
];
options = {
hostname = mkStrOption;
mainUser = {
name = mkStrOption;
publicKey = mkStrOption;
email = mkStrOption;
shell = mkOption {
type = shellPackage;
};
};
persistDirs = mkOption {
type = listOf (coercedTo str (d: { directory = d; }) rootDir);
default = [ ];
};
};
config = {
# Helper options
environment.persistence."/persist".directories = config.persistDirs;
# Global options
persistDirs = [
"/var/lib/systemd"
"/var/lib/nixos"
];
stylix.enable = true;
nixpkgs.config.allowUnfree = true;
nix.settings.experimental-features = [
"nix-command"
"flakes"
"pipe-operators"
];
nix.settings.trusted-users = [ "@wheel" ];
system.stateVersion = "25.05";
time.timeZone = "Asia/Singapore";
i18n.defaultLocale = "en_US.UTF-8";
users = {
# Don't allow imperative configuration
mutableUsers = false;
users.root.openssh.authorizedKeys.keys = [ config.mainUser.publicKey ];
groups.users = {
gid = 100;
members = [ "${config.mainUser.name}" ];
};
users."${config.mainUser.name}" = {
inherit (config.mainUser) shell;
uid = 1000;
isNormalUser = true;
hashedPasswordFile = config.sops.secrets."${config.mainUser.name}/hashedPassword".path;
extraGroups = [ "wheel" ];
openssh.authorizedKeys.keys = [ config.mainUser.publicKey ];
};
};
security.sudo.wheelNeedsPassword = false;
sops = {
defaultSopsFile = get-file "secrets/secrets.yaml";
age.sshKeyPaths = [ "/persist/home/${config.mainUser.name}/.ssh/id_ed25519" ];
secrets = {
"keys/openrouter" = { };
"keys/gemini" = { };
"keys/cloudflare" = { };
"keys/telegram_bot" = { };
"rafiq/hashedPassword".neededForUsers = true;
"rafiq/personalEmailPassword" = { };
"rafiq/workEmailPassword" = { };
};
};
environment.shellInit = # sh
''
export GEMINI_API_KEY=$(sudo cat ${config.sops.secrets."keys/gemini".path})
'';
};
}

View file

@ -1,6 +0,0 @@
{
services.pipewire = {
enable = true;
pulse.enable = true;
};
}

View file

@ -1,20 +0,0 @@
{
config,
lib,
pkgs,
...
}:
let
inherit (lib) mkEnableOption mkIf singleton;
cfg = config.desktop.browser.tor-browser;
in
{
options.desktop.browser.tor-browser.enable = mkEnableOption "";
config = mkIf cfg.enable {
home-manager.sharedModules = singleton {
persistDirs = singleton ".tor project";
home.packages = singleton pkgs.tor-browser;
};
};
}

View file

@ -1,37 +0,0 @@
{
lib,
config,
pkgs,
...
}:
let
inherit (lib)
mkEnableOption
mkIf
singleton
optional
;
inherit (lib.pantheon) mkStrOption;
inherit (pkgs) font-awesome wl-clipboard-rs;
cfg = config.desktop;
in
{
options.desktop = {
enable = mkEnableOption "";
enableWaylandUtilities = mkEnableOption "";
mainMonitor = {
id = mkStrOption;
scale = mkStrOption;
resolution = mkStrOption;
refresh-rate = mkStrOption;
};
};
config = mkIf cfg.enable {
fonts.packages = singleton font-awesome;
services.getty.autologinUser = config.mainUser.name;
home-manager.sharedModules = optional cfg.enableWaylandUtilities {
home.packages = [ wl-clipboard-rs ];
};
};
}

View file

@ -1,37 +0,0 @@
{
config,
lib,
pkgs,
...
}:
let
inherit (lib)
mkEnableOption
mkIf
mkMerge
singleton
;
cfg = config.desktop.gaming;
in
{
options.desktop.gaming = {
steam.enable = mkEnableOption "";
prism-launcher.enable = mkEnableOption "";
};
config = mkMerge [
(mkIf cfg.steam.enable {
programs.steam = {
enable = true;
gamescopeSession.enable = true;
};
home-manager.sharedModules = singleton { persistDirs = singleton ".local/share/Steam"; };
})
(mkIf cfg.prism-launcher.enable {
home-manager.sharedModules = singleton {
home.packages = singleton pkgs.prismlauncher;
persistDirs = singleton ".local/share/PrismLauncher";
};
})
];
}

View file

@ -1,16 +0,0 @@
{ lib, config, ... }:
let
inherit (lib) singleton mkEnableOption;
cfg = config.desktop.launcher;
in
{
options.desktop.launcher = {
fuzzel.enable = mkEnableOption "";
wofi.enable = mkEnableOption "";
};
config.home-manager.sharedModules = singleton {
programs.fuzzel.enable = cfg.fuzzel.enable;
programs.wofi.enable = cfg.wofi.enable;
};
}

View file

@ -1,22 +0,0 @@
{ config, lib, ... }:
let
inherit (lib)
mkEnableOption
mkIf
mkMerge
singleton
;
cfg = config.desktop.lockscreen;
in
{
options.desktop.lockscreen = {
hyprlock.enable = mkEnableOption "";
};
config = mkMerge [
(mkIf cfg.hyprlock.enable {
security.pam.services.hyprlock = { };
home-manager.sharedModules = singleton { programs.hyprlock.enable = true; };
})
];
}

View file

@ -1,18 +0,0 @@
{
config,
lib,
pkgs,
...
}:
let
inherit (lib) mkEnableOption optional singleton;
inherit (pkgs) vlc;
cfg = config.desktop.media-player;
in
{
options.desktop.media-player = {
vlc.enable = mkEnableOption "";
};
config.home-manager.sharedModules = optional cfg.vlc.enable { home.packages = singleton vlc; };
}

View file

@ -1,30 +0,0 @@
{ config, lib, ... }:
let
inherit (lib)
mkMerge
singleton
mkEnableOption
mkIf
;
cfg = config.desktop.services;
in
{
options.desktop.services = {
spotifyd.enable = mkEnableOption "";
};
config = mkMerge [
(mkIf cfg.spotifyd.enable {
networking.firewall.allowedTCPPorts = [ 5353 ];
networking.firewall.allowedUDPPorts = [ 5353 ];
home-manager.sharedModules = singleton {
services.spotifyd.enable = true;
services.spotifyd.settings.global = {
device_name = "${config.hostname}";
device_type = "computer";
zeroconf_port = 5353;
};
};
})
];
}

View file

@ -1,24 +0,0 @@
{ config, lib, ... }:
let
inherit (lib) singleton mkIf mkEnableOption;
cfg = config.desktop.services.sunshine;
in
{
options.desktop.services.sunshine = {
enable = mkEnableOption "";
};
config = mkIf cfg.enable {
services.sunshine = {
enable = true;
capSysAdmin = true;
openFirewall = true;
settings = {
sunshine_name = config.hostname;
origin_pin_allowed = "wan";
origin_web_ui_allowed = "wan";
};
applications = { };
};
home-manager.sharedModules = singleton { persistDirs = singleton ".config/sunshine"; };
};
}

View file

@ -1,55 +0,0 @@
{ config, lib, ... }:
let
inherit (lib) mkEnableOption mkIf singleton;
inherit (config.desktop) mainMonitor;
cfg = config.desktop.window-manager.hyprland;
in
{
options.desktop.window-manager.hyprland.enable = mkEnableOption "";
config = mkIf cfg.enable {
# Enable custom module for wayland utilities (clipboard etc.)
desktop.enableWaylandUtilities = true;
# Start Hyprland at boot only if not connecting through SSH
environment.loginShellInit = # sh
''
if [[ -z "$SSH_CLIENT" && -z "$SSH_CONNECTION" ]]; then
if uwsm check may-start; then
exec uwsm start hyprland-uwsm.desktop
fi
fi
'';
environment.variables = {
# Get Electron apps to use Wayland
ELECTRON_OZONE_PLATFORM_HINT = "auto";
NIXOS_OZONE_WL = "1";
};
programs.hyprland = {
enable = true;
# Use UWSM to have each process controlled by systemd init
withUWSM = true;
};
home-manager.sharedModules = singleton {
wayland.windowManager.hyprland = {
enable = true;
# This is needed for UWSM
systemd.enable = false;
# Null the packages since we use them system wide
package = null;
portalPackage = null;
settings.monitor = [ "${mainMonitor.id}, ${mainMonitor.resolution}@${mainMonitor.refresh-rate}, auto, ${mainMonitor.scale}" ];
};
xdg.configFile."uwsm/env".text = # sh
''
# Force apps to scale right with Wayland
export GDK_SCALE=${mainMonitor.scale}
export STEAM_FORCE_DESKTOPUI_SCALING=${mainMonitor.scale}
'';
xdg.configFile."uwsm/env-hyprland".text = # sh
''
export GDK_SCALE=${mainMonitor.scale}
export STEAM_FORCE_DESKTOPUI_SCALING=${mainMonitor.scale}
'';
};
};
}

View file

@ -1,30 +0,0 @@
{
config,
lib,
...
}:
let
inherit (lib.pantheon) mkIntOption mkStrOption;
cfg = config.machine.bootloader;
in
{
options.machine.bootloader = {
type = mkStrOption;
configurationLimit = mkIntOption 5;
};
config.boot = {
initrd.availableKernelModules = [
"nvme"
"xhci_pci"
"ahci"
"usbhid"
"usb_storage"
"sd_mod"
];
loader.efi.canTouchEfiVariables = true;
loader.systemd-boot = {
enable = cfg.type == "systemd-boot";
inherit (cfg) configurationLimit;
};
};
}

View file

@ -1,19 +0,0 @@
{ lib, modulesPath, ... }:
let
inherit (lib) singleton;
in
{
imports = [
(modulesPath + "/installer/scan/not-detected.nix")
];
config = {
services.fwupd.enable = true;
persistDirs = singleton "/var/lib/bluetooth";
hardware.bluetooth = {
enable = true;
settings.General.Experimental = true;
};
hardware.xone.enable = true;
};
}

View file

@ -1,116 +0,0 @@
{
config,
lib,
inputs,
...
}:
let
inherit (lib) mkIf mkEnableOption;
inherit (lib.pantheon) mkStrOption;
cfg = config.machine.drives.btrfs;
ephemeralRootCfg = {
boot.initrd.postDeviceCommands = lib.mkAfter ''
mkdir /btrfs_tmp
mount /dev/root_vg/root /btrfs_tmp
if [[ -e /btrfs_tmp/root ]]; then
mkdir -p /btrfs_tmp/old_roots
timestamp=$(date --date="@$(stat -c %Y /btrfs_tmp/root)" "+%Y-%m-%-d_%H:%M:%S")
mv /btrfs_tmp/root "/btrfs_tmp/old_roots/$timestamp"
fi
delete_subvolume_recursively() {
IFS=$'\n'
for i in $(btrfs subvolume list -o "$1" | cut -f 9- -d ' '); do
delete_subvolume_recursively "/btrfs_tmp/$i"
done
btrfs subvolume delete "$1"
}
for i in $(find /btrfs_tmp/old_roots/ -maxdepth 1 -mtime +30); do
delete_subvolume_recursively "$i"
done
btrfs subvolume create /btrfs_tmp/root
umount /btrfs_tmp
'';
programs.fuse.userAllowOther = true;
fileSystems."/persist".neededForBoot = true;
#FIXME: below should be in module or something
environment.persistence."/persist" = {
hideMounts = true;
files = [
"/etc/ssh/ssh_host_ed25519_key"
"/etc/ssh/ssh_host_ed25519_key.pub"
"/etc/ssh/ssh_host_rsa_key"
"/etc/ssh/ssh_host_rsa_key.pub"
"/etc/machine-id"
];
};
};
in
{
imports = [
inputs.disko.nixosModules.disko
inputs.impermanence.nixosModules.impermanence
];
options.machine.drives.btrfs = {
enable = mkEnableOption "";
drive = mkStrOption;
ephemeralRoot = mkEnableOption "";
};
config = mkIf cfg.enable (
{
boot.initrd.kernelModules = [ "dm-snapshot" ];
disko.devices.disk.main = {
device = cfg.drive;
type = "disk";
content.type = "gpt";
content.partitions = {
boot.name = "boot";
boot.size = "1M";
boot.type = "EF02";
esp.name = "ESP";
esp.size = "500M";
esp.type = "EF00";
esp.content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
};
swap.size = "4G";
swap.content = {
type = "swap";
resumeDevice = true;
};
root.name = "root";
root.size = "100%";
root.content = {
type = "lvm_pv";
vg = "root_vg";
};
};
};
disko.devices.lvm_vg.root_vg = {
type = "lvm_vg";
lvs.root.size = "100%FREE";
lvs.root.content.type = "btrfs";
lvs.root.content.extraArgs = [ "-f" ];
lvs.root.content.subvolumes = {
"/root".mountpoint = "/";
"/persist".mountpoint = "/persist";
"/persist".mountOptions = [
"subvol=persist"
"noatime"
];
"/nix".mountpoint = "/nix";
"/nix".mountOptions = [
"subvol=nix"
"noatime"
];
};
};
}
// ephemeralRootCfg
);
}

View file

@ -1,41 +0,0 @@
{
config,
lib,
pkgs,
...
}:
let
inherit (lib)
mkMerge
mkIf
mkEnableOption
singleton
;
cfg = config.machine.gpu;
in
{
options.machine.gpu = {
nvidia.enable = mkEnableOption "";
};
config = mkMerge [
(mkIf cfg.nvidia.enable {
hardware = {
graphics.enable = true;
graphics.extraPackages = singleton pkgs.nvidia-vaapi-driver;
nvidia.open = true;
nvidia.package = config.boot.kernelPackages.nvidiaPackages.latest;
};
services.xserver.videoDrivers = [ "nvidia" ];
nixpkgs.config.allowUnfree = true;
environment.variables = {
LIBVA_DRIVER_NAME = "nvidia";
__GLX_VENDOR_LIBRARY_NAME = "nvidia";
NVD_BACKEND = "direct";
};
nix.settings.substituters = [ "https://cuda-maintainers.cachix.org" ];
nix.settings.trusted-public-keys = [
"cuda-maintainers.cachix.org-1:0dq3bujKpuEPMCX6U4WylrUDZ9JyUG0VpVZa7CNfq5E="
];
})
];
}

View file

@ -1,21 +0,0 @@
{ config, lib, ... }:
let
inherit (lib) singleton mkOption;
inherit (lib.types) enum;
cfg = config.machine.platform;
in
{
options.machine.platform = {
type = mkOption {
type = enum [
"amd"
"intel"
];
};
};
config = {
hardware.cpu.${cfg.type}.updateMicrocode = true;
boot.kernelModules = singleton "kvm-${cfg.type}";
};
}

View file

@ -1,45 +0,0 @@
{
config,
pkgs,
lib,
...
}:
let
inherit (lib)
mkEnableOption
mkIf
mkMerge
singleton
;
cfg = config.machine.usb;
in
{
options.machine.usb = {
automount = mkEnableOption "";
enableQmk = mkEnableOption "";
};
config = mkMerge [
(mkIf cfg.automount {
services.udisks2.enable = true;
home-manager.sharedModules = singleton {
services.udiskie = {
enable = true;
automount = true;
notify = true;
};
};
})
(mkIf cfg.enableQmk {
hardware.keyboard.qmk.enable = true;
services.udev = {
packages = with pkgs; [
vial
qmk
qmk-udev-rules
qmk_hid
];
};
})
];
}

View file

@ -1,22 +0,0 @@
{
config,
lib,
pkgs,
...
}:
let
inherit (lib) mkIf mkEnableOption singleton;
cfg = config.machine.virtualisation.distrobox;
in
{
options.machine.virtualisation.distrobox = {
enable = mkEnableOption "";
};
config = mkIf cfg.enable {
machine.virtualisation.podman.enable = true;
home-manager.sharedModules = singleton {
home.packages = singleton pkgs.distrobox;
# persistDirs = [ ".local/share/containers" ];
};
};
}

View file

@ -1,32 +0,0 @@
{ config, lib, ... }:
let
inherit (lib) mkEnableOption mkIf;
cfg = config.machine.virtualisation.podman;
in
{
options.machine.virtualisation.podman = {
enable = mkEnableOption "";
};
config = mkIf cfg.enable {
virtualisation = {
containers.enable = true;
podman = {
enable = true;
dockerCompat = true;
defaultNetwork.settings.dns_enabled = true;
};
};
users.users."${config.mainUser.name}" = {
extraGroups = [ "podman" ];
# https://wiki.nixos.org/wiki/Distrobox
# subGidRanges = singleton {
# count = 65536;
# startGid = 1000;
# };
# subUidRanges = singleton {
# count = 65536;
# startUid = 1000;
# };
};
};
}

View file

@ -1,28 +0,0 @@
{ config, lib, ... }:
let
inherit (lib) mkDefault singleton;
in
{
sops.secrets = {
"tailscale/client-id".sopsFile = ./tailscale.yaml;
"tailscale/client-secret".sopsFile = ./tailscale.yaml;
};
networking = {
enableIPv6 = false;
useDHCP = mkDefault true;
hostName = config.hostname;
networkmanager.enable = true;
};
services.openssh = {
enable = true;
settings.PrintMotd = true;
};
services.tailscale = {
enable = true;
authKeyFile = config.sops.secrets."tailscale/client-secret".path;
authKeyParameters.preauthorized = true;
};
persistDirs = singleton "/var/lib/tailscale";
}

View file

@ -1,18 +0,0 @@
tailscale:
client-id: ENC[AES256_GCM,data:kQ4H9b2h8DN+5eTvwIYHZ6s=,iv:/nC3LM0qDNj3wIm9XZd7UUn5SxmAOA1dofsDGElKjVU=,tag:AIj5F7KkORujLDe+ZOxJgw==,type:str]
client-secret: ENC[AES256_GCM,data:O0cKyuK+FfK2E1mzQpkgybPrqEs0fH1y3jCOG6usT++6x3sWuJNvT56OIHpVNu8GH/6BIBsnenC1J/sVNTYIzA==,iv:FugIzSjNpoe9Bwy+x/GHl0BpCtbogQXpY7s3ICevQc0=,tag:1kQIO4ekjKuvexQ923YE3g==,type:str]
sops:
age:
- recipient: age12l33pas8eptwjc7ewux3d8snyzfzwz0tn9qg5kw8le79fswmjgjqdjgyy6
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBGbTNsZE5lN2JOT1Jsd2hz
OWpDWTFzTW05Nzl5K1AyMmgxcVV2eHlBRlF3Cnc3VW5IN014ck8zM3BIWnBMNFFt
UnE4aGhGNERUOTlwZEJyNWF1Q1o0RXcKLS0tIFlZSFFoaDlOMnBMSFVyT3FMbFZj
ckl5RVZiMnkzV0RFQXN1aHZKM2doMnMKD6BjRdqsHiKDth4aBiZ1lvlcO1OgY36O
cGkZjuH45L4a0Y0kvptq3iZ/iPnmX8hw8n/gdplzUkpBzdsNPebvSg==
-----END AGE ENCRYPTED FILE-----
lastmodified: "2025-07-01T21:11:39Z"
mac: ENC[AES256_GCM,data:YWgrMqqJgrGe+40a9CSDpAAgwPOeGXRFb58c6X6PxDHve3u5vQfHh+wkC0TFxadMsYcJTczRYf8YWuAwf7kFoO7ofYs+PfEi4ydKhl8WY9nXTsq+BFT4rDl/BaCfQw6qWD5/TKTtxm2pdtBNrG7bNeZJ8cVSOO/wsjoqrrbh3fk=,iv:8BXOX5O5apYLhZOWihagQBVldmsVoV+uEcejcO3cC0I=,tag:vansSul5Ebwooay48uYNZQ==,type:str]
unencrypted_suffix: _unencrypted
version: 3.10.2

View file

@ -1,84 +0,0 @@
{
lib,
config,
pkgs,
...
}:
let
inherit (lib) singleton;
cfg = config.server.databases;
in
{
options.server.databases = {
mongodb = {
enable = lib.mkEnableOption "the MongoDB server";
port = lib.pantheon.mkPortOption 27017;
};
mysql = {
enable = lib.mkEnableOption "the MySQL server";
port = lib.pantheon.mkPortOption 3306;
};
postgresql = {
enable = lib.mkEnableOption "the postgresql server";
port = lib.pantheon.mkPortOption 5432;
};
};
config = lib.mkMerge [
(lib.mkIf cfg.postgresql.enable {
networking.firewall.allowedTCPPorts = lib.singleton cfg.postgresql.port;
persistDirs = singleton {
directory = builtins.toString config.services.postgresql.dataDir;
user = "postgres";
group = "postgres";
};
services.postgresql = {
enable = true;
enableTCPIP = true;
settings = { inherit (cfg.postgresql) port; };
authentication = lib.mkOverride 10 ''
#type database DBuser auth-method
local all all trust
# ipv4
host all all 0.0.0.0/0 trust
'';
ensureDatabases = singleton "alphastory";
ensureUsers = singleton {
name = "alphastory";
ensureDBOwnership = true;
};
};
})
(lib.mkIf cfg.mongodb.enable {
networking.firewall.allowedTCPPorts = [ cfg.mongodb.port ];
persistDirs = singleton {
directory = builtins.toString config.services.mongodb.dbpath;
user = "mongodb";
group = "mongodb";
};
services.mongodb = {
enable = true;
bind_ip = "0.0.0.0";
extraConfig = ''
net.port: ${builtins.toString cfg.mongodb.port}
'';
};
})
(lib.mkIf cfg.mysql.enable {
networking.firewall.allowedTCPPorts = [ cfg.mysql.port ];
persistDirs = singleton {
directory = builtins.toString config.services.mysql.dataDir;
user = "mysql";
group = "mysql";
};
services.mysql = {
enable = true;
package = pkgs.mariadb;
settings.mysqld = {
inherit (cfg.mysql) port;
};
};
})
];
}

View file

@ -1,43 +0,0 @@
{ lib, config, ... }:
{
options.server.mountHelios = lib.mkEnableOption "";
config = lib.mkIf config.server.mountHelios {
sops.secrets."rafiq/oldSMBCredentials" = { };
sops.templates."smb-credentials".content = ''
username=rafiq
password=${config.sops.placeholder."rafiq/oldSMBCredentials"}
'';
fileSystems = {
"/media/helios/data" = {
device = "//helios/data";
fsType = "cifs";
options = [
"x-systemd.automount"
"x-systemd.requires=tailscaled.service"
"x-systemd.mount-timeout=0"
];
};
"/media/helios/rafiqcloud" = {
device = "//helios/rafiqcloud";
fsType = "cifs";
options = [
"x-systemd.automount"
"x-systemd.requires=tailscaled.service"
"x-systemd.mount-timeout=0"
"credentials=${config.sops.templates."smb-credentials".path}"
];
};
"/media/helios/rafiqmedia" = {
device = "//helios/rafiqmedia";
fsType = "cifs";
options = [
"x-systemd.automount"
"x-systemd.requires=tailscaled.service"
"x-systemd.mount-timeout=0"
"credentials=${config.sops.templates."smb-credentials".path}"
];
};
};
};
}

View file

@ -1,62 +0,0 @@
{ config, lib, ... }:
let
inherit (lib) mkIf mkOption mkEnableOption;
inherit (lib.types) enum str listOf;
inherit (lib.lists) unique;
inherit (builtins) map;
cfg = config.server.networking.ddns;
mkDomain = domain_name: {
inherit domain_name;
sub_domains = [
"@"
"*"
];
};
# Sanitize the list of domains with unique so we can add to it with every service.
mkDomains = map mkDomain (unique cfg.domains);
in
{
options.server.networking.ddns = {
enable = mkEnableOption "";
type = mkOption {
type = enum [ "godns" ];
default = "godns";
};
domains = mkOption {
type = listOf str;
default = [ ];
};
};
config = mkIf cfg.enable {
services.godns = {
enable = if (cfg.type == "godns") then true else false;
loadCredential = [
"cf_token:${config.sops.secrets."keys/cloudflare".path}"
"telegram_bot_token:${config.sops.secrets."keys/telegram_bot".path}"
];
settings = {
provider = "Cloudflare";
login_token_file = "$CREDENTIALS_DIRECTORY/cf_token";
domains = mkDomains;
resolver = "1.1.1.1";
ip_urls = [
"https://wtfismyip.com/text"
"https://api.ipify.org"
"https://myip.biturl.top"
"https://api-ipv4.ip.sb/ip"
];
ip_type = "IPv4";
interval = 300;
notify = {
telegram = {
enabled = true;
bot_api_key_file = "$CREDENTIALS_DIRECTORY/telegram_bot_token";
chat_id = "384288005";
message_template = "Domain *{{ .Domain }} has been updated to %0A{{ .CurrentIP }}";
};
};
};
};
};
}

View file

@ -1,41 +0,0 @@
{ config, lib, ... }:
let
inherit (lib) singleton optional;
inherit (lib.pantheon) mkPortOption;
inherit (lib.pantheon.modules) mkWebApp;
cfg = config.server.web-apps.forgejo;
upstreamCfg = config.services.forgejo;
in
mkWebApp {
inherit config;
name = "forgejo";
defaultPort = 3000;
persistDirs = singleton {
directory = upstreamCfg.stateDir;
inherit (upstreamCfg) user group;
};
extraOptions = {
sshPort = mkPortOption 2222;
};
extraConfig = {
networking.firewall.allowedTCPPorts = optional cfg.openFirewall cfg.sshPort;
services.forgejo = {
enable = true;
settings = {
server = {
DOMAIN = cfg.domain;
ROOT_URL = "https://${cfg.domain}/";
HTTP_PORT = cfg.port;
START_SSH_SERVER = true;
SSH_PORT = cfg.sshPort;
};
repository = {
USE_COMPAT_SSH_URI = false;
ENABLE_PUSH_CREATE_USER = true;
ENABLE_PUSH_CREATE_ORG = true;
};
"repository.signing".FORMAT = "ssh";
};
};
};
}

View file

@ -1,17 +0,0 @@
{ lib, config, ... }:
let
inherit (lib.pantheon.modules) mkWebApp;
cfg = config.server.web-apps.glance;
in
mkWebApp {
inherit config;
name = "glance";
defaultPort = 8080;
extraConfig = {
services.glance = {
enable = true;
settings.server.host = "0.0.0.0";
settings.server.port = cfg.port;
};
};
}

View file

@ -1,80 +0,0 @@
{
config,
lib,
inputs,
...
}:
let
inherit (lib) singleton;
inherit (lib.pantheon) mkStrOption;
inherit (lib.pantheon.modules) mkWebApp;
cfg = config.server.web-apps.librechat;
upstreamCfg = config.services.librechat;
in
mkWebApp {
inherit config;
name = "librechat";
defaultPort = 3080;
persistDirs = singleton {
directory = upstreamCfg.dataDir;
inherit (upstreamCfg) user group;
};
extraOptions.mongodbURI = mkStrOption // {
default = "mongodb://${config.hostname}:27017/LibreChat";
};
extraConfig = {
sops.secrets = {
"librechat/creds_key" = { };
"librechat/creds_iv" = { };
"librechat/jwt_secret" = { };
"librechat/jwt_refresh_secret" = { };
};
services.librechat = {
enable = true;
openFirewall = true;
inherit (cfg) port;
env = {
HOST = "0.0.0.0";
ALLOW_REGISTRATION = "true";
NO_INDEX = "true";
MONGO_URI = cfg.mongodbURI;
DOMAIN_CLIENT = cfg.domain;
DOMAIN_SERVER = cfg.domain;
ENDPOINTS = "anthropic,agents,google";
};
credentials = {
CREDS_KEY = config.sops.secrets."librechat/creds_key".path;
CREDS_IV = config.sops.secrets."librechat/creds_iv".path;
JWT_SECRET = config.sops.secrets."librechat/jwt_secret".path;
JWT_REFRESH_SECRET = config.sops.secrets."librechat/jwt_refresh_secret".path;
OPENROUTER_KEY = config.sops.secrets."keys/openrouter".path;
GOOGLE_KEY = config.sops.secrets."keys/gemini".path;
};
settings = {
version = "1.1.4";
cache = true;
endpoints.custom = [
{
name = "OpenRouter";
apiKey = "\${OPENROUTER_KEY}";
baseURL = "https://openrouter.ai/api/v1";
models.default = [ "meta-llama/llama-3-70b-instruct" ];
models.fetch = true;
titleConvo = true;
titleModel = "current_model";
modelDisplayLabel = "OpenRouter";
}
];
interface = {
privacyPolicy = {
externalUrl = "https://librechat.ai/privacy-policy";
openNewTab = true;
};
};
};
};
};
}
// {
imports = singleton "${inputs.rrvsh-nixpkgs}/nixos/modules/services/web-apps/librechat.nix";
}

View file

@ -1,24 +0,0 @@
{
config,
lib,
inputs,
...
}:
let
inherit (lib.pantheon.modules) mkWebApp;
cfg = config.server.web-apps.rrv-sh;
in
mkWebApp {
inherit config;
name = "rrv-sh";
defaultPort = 2309;
extraConfig = {
services.rrv-sh = {
enable = true;
inherit (cfg) port;
};
};
}
// {
imports = [ inputs.rrv-sh.nixosModules.default ];
}

View file

@ -1,34 +0,0 @@
{
config,
lib,
inputs,
...
}:
let
inherit (lib) singleton;
inherit (lib.pantheon.modules) mkWebApp;
upstreamCfg = config.services.sd-webui-forge;
in
mkWebApp {
inherit config;
name = "sd-webui-forge";
defaultPort = 7860;
persistDirs = singleton {
directory = upstreamCfg.dataDir;
inherit (upstreamCfg) user group;
};
extraConfig = {
assertions = singleton {
assertion = config.machine.gpu.nvidia.enable;
message = "You must run the sd-webui-forge service only with an nvidia gpu.";
};
services.sd-webui-forge = {
enable = true;
listen = true;
extraArgs = "--cuda-malloc";
};
};
}
// {
imports = [ inputs.stable-diffusion-webui-nix.nixosModules.default ];
}

View file

@ -1,34 +0,0 @@
{ config, lib, ... }:
let
inherit (lib)
mkMerge
mkIf
mkEnableOption
singleton
;
cfg = config.server.web-servers;
in
{
options.server.web-servers = {
enableSSL = mkEnableOption "";
};
config = mkMerge [
(mkIf cfg.enableSSL {
security.acme = {
acceptTerms = true;
defaults = {
inherit (config.mainUser) email;
dnsProvider = "cloudflare";
credentialFiles."CLOUDFLARE_DNS_API_TOKEN_FILE" = config.sops.secrets."keys/cloudflare".path;
};
certs = {
"rrv.sh".extraDomainNames = singleton "*.rrv.sh";
"bwfiq.com".extraDomainNames = singleton "*.bwfiq.com";
"slayment.com".extraDomainNames = singleton "*.slayment.com";
"aenyrathia.wiki".extraDomainNames = singleton "*.aenyrathia.wiki";
};
};
})
];
}

View file

@ -1,119 +0,0 @@
{ config, lib, ... }:
let
inherit (lib)
mkMerge
mkOption
mkEnableOption
mkIf
singleton
;
inherit (lib.types) listOf submodule attrs;
inherit (lib.pantheon) mkStrOption mkPathOption mkRootDomain;
inherit (builtins) listToAttrs map;
cfg = config.server.web-servers.nginx;
sslCheck = good: bad: if config.server.web-servers.enableSSL then good else bad;
defaultSink = mkIf cfg.enableDefaultSink {
"_" = {
default = true;
rejectSSL = sslCheck true false;
locations."/" = {
return = "444";
};
};
};
pages = listToAttrs (
map (page: {
name = page.domain;
value = {
addSSL = sslCheck true false;
useACMEHost = sslCheck (mkRootDomain page.domain) null;
acmeRoot = null; # needed for DNS validation
locations = {
"/" = {
inherit (page) root;
} // page.extraConfig;
} // page.locations;
};
}) cfg.pages
);
proxyPasses = listToAttrs (
map (proxy: {
name = proxy.source;
value = {
addSSL = sslCheck true false;
useACMEHost = sslCheck (mkRootDomain proxy.source) null;
acmeRoot = null; # needed for DNS validation
locations = {
"/" = {
proxyPass = proxy.target;
} // proxy.extraConfig;
} // proxy.locations;
};
}) cfg.proxies
);
in
{
options.server.web-servers.nginx = {
enable = mkEnableOption "the Nginx server";
openFirewall = mkEnableOption "" // {
default = true;
};
enableDefaultSink = mkEnableOption "" // {
default = true;
};
pages = mkOption {
default = [ ];
type = listOf (submodule {
options = {
domain = mkStrOption;
root = mkPathOption "";
extraConfig = lib.mkOption {
type = attrs;
default = { };
};
locations = lib.mkOption {
type = attrs;
default = { };
};
};
});
};
proxies = mkOption {
default = [ ];
type = listOf (submodule {
options = {
source = mkStrOption;
target = mkStrOption;
extraConfig = lib.mkOption {
type = attrs;
default = { };
};
locations = lib.mkOption {
type = attrs;
default = { };
};
};
});
};
};
config = mkIf cfg.enable {
networking.firewall.allowedTCPPorts = mkIf cfg.openFirewall [
443
80
];
users.users.nginx.extraGroups = singleton "acme";
services.nginx = {
enable = true;
recommendedProxySettings = true;
recommendedTlsSettings = true;
recommendedOptimisation = true;
recommendedGzipSettings = true;
virtualHosts = mkMerge [
defaultSink
proxyPasses
pages
];
};
};
}

17
nix/files/cheatsheet.nix Normal file
View file

@ -0,0 +1,17 @@
{ lib, config, ... }:
let
inherit (lib) concatStringsSep singleton;
in
{
text.cheatsheet = concatStringsSep "\n" [
"`__curPos.file` will give the full evaluated path of the nix file it is called in. See [this issue](https://github.com/NixOS/nix/issues/5897#issuecomment-1012165198) for more information."
];
perSystem =
{ pkgs, ... }:
{
files.files = singleton {
path_ = "docs/cheatsheet.md";
drv = pkgs.writeText "cheatsheet.md" config.text.cheatsheet;
};
};
}

13
nix/files/gitignore.nix Normal file
View file

@ -0,0 +1,13 @@
{ config, ... }:
{
perSystem =
{ pkgs, ... }:
{
files.files = [
{
path_ = ".gitignore";
drv = pkgs.writeText ".gitignore" config.text.gitignore;
}
];
};
}

54
nix/files/readme.nix Normal file
View file

@ -0,0 +1,54 @@
{ config, ... }:
{
text.readme = {
heading = "Pantheon";
description = # markdown
''
This flake serves as a monorepo for my systems (using IaC), dotfiles, and scripts.
It's hosted at https://git.rrv.sh/rrvsh/pantheon, and mirrored to https://github.com/rrvsh/pantheon.
'';
order = [
"Structure"
"Acknowledgements"
];
parts."Acknowledgements" = # markdown
''
Thanks to the following for inspiring this configuration. I highly recommend you look through their writings and configurations.
- [ornicar](https://github.com/ornicar/dotfiles) which is where I first heard of NixOS
- [No Boilerplate](https://www.youtube.com/watch?v=CwfKlX3rA6E&pp=0gcJCfwAo7VqN5tD) for making me finally try the OS
- [ryan4yin](https://nixos-and-flakes.thiscute.world/) for being an amazing introduction to NixOS, home-manager, and flakes
- [NotAShelf](https://github.com/NotAShelf/) for their blog and for the wonderful [NVF](https://github.com/notashelf/nvf)
- [mightyiam](https://github.com/mightyiam/infra) for their infrastructure repo using flake-parts
- [drupol](https://not-a-number.io/2025/refactoring-my-infrastructure-as-code-configurations/) for this blog post which convinced me to rebase my infra to use flake-parts
'';
parts."Structure" = # markdown
''
The system configurations are defined in [`flake.manifest`](nix/manifest.nix).
`flake.manifest.owner` provides the attributes for the administrator user, including username and pubkey.
`flake.manifest.hosts` provides the specifications for the system configurations that should be exposed by the flake as nixosConfigurations.
`flake.modules.nixos.*` provide NixOS options and configurations.
The attribute `flake.modules.nixos.default` provides options that will be applied to every system of that class.
You can use it as seen [here](nix/modules/flake/home-manager.nix):
```nix
flake.modules.nixos.default.imports = [ inputs.home-manager.nixosModules.default ];
```
The other attributes under `flake.modules.nixos` should be opt-in, i.e. provide options that will be set in the profiles.
`flake.profiles.nixos` provides profiles which use the options defined in `flake.modules.nixos` to define different roles for each system, such as graphical, laptop, headless, etc.
Options should not be defined here.
`flake.contracts.nixos.*` will provide contracts, such as reverse proxies or databases, which will configure options on the provider and receiver host.
'';
};
perSystem =
{ pkgs, ... }:
{
files.files = [
{
path_ = "docs/README.md";
drv = pkgs.writeText "README.md" config.text.readme;
}
];
};
}

22
nix/homes/rafiq/git.nix Normal file
View file

@ -0,0 +1,22 @@
{
flake.homes.rafiq = {
home.shellAliases = {
gs = "git status";
gc = "git commit";
gcam = "git commit -am";
gu = "git push";
gy = "git pull";
gdh = "git diff HEAD";
};
programs.git = {
signing.signByDefault = true;
extraConfig = {
init.defaultBranch = "prime";
push.autoSetupRemote = true;
pull.rebase = false;
core.editor = "$EDITOR";
gpg.format = "ssh";
};
};
};
}

96
nix/lib/default.nix Normal file
View file

@ -0,0 +1,96 @@
{ lib, config, ... }:
let
cfg = config.flake;
inherit (lib.attrsets) mapAttrs concatMapAttrs;
in
{
flake.lib = {
/**
Remove the top level attributes from an attribute set and return the merged attributes.
# Inputs
`attrset`
: An attribute set to flatten.
# Type
```
flattenAttrs :: AttrSet -> AttrSet
```
# Examples
:::{.example}
## `flattenAttrs` usage example
```nix
flattenAttrs { x = { a = 1; b = 2; }; y = { c = 3; d = 4; }; }
=> { a = 1; b = 2; c = 3; d = 4; }
```
*/
flattenAttrs = attrset: concatMapAttrs (_: v: v) attrset;
/**
Return an attribute set for use with a option that needs to be used for all users.
# Inputs
`attrset`
: An attribute set to apply to all the users.
# Type
```
forAllUsers :: AttrSet -> AttrSet
```
# Examples
:::{.example}
## `forAllUsers` usage example
```nix
flake.manifest.users.rafiq = { ... };
flake.modules.nixos.default.users = forAllUsers {
isNormalUser = true;
};
=> flake.modules.nixos.default.users.rafiq.isNormalUser = true;
```
:::
*/
forAllUsers = attrset: mapAttrs (_: _: attrset) cfg.manifest.users;
/**
Like forAllUsers, but passes in the name and value from the manifest.
# Inputs
`f`
: A function that takes an attribute name and its value, and returns the new value for the attribute.
# Type
```
forAllUsers' :: (String -> Any -> Any) -> AttrSet
```
# Examples
:::{.example}
## `forAllUsers'` usage example
```nix
flake.manifest.users.rafiq = { ... };
flake.modules.homeManager.users = forAllUsers' (name: value: {
home.username = name;
});
=> flake.modules.homeManager.default.users.rafiq.home.username = "rafiq";
```
:::
*/
forAllUsers' = f: mapAttrs f cfg.manifest.users;
};
}

49
nix/manifest.nix Normal file
View file

@ -0,0 +1,49 @@
let
testCfg =
{ hostName, ... }:
{
fileSystems."/" = {
device = "/dev/sda1";
fsType = "ext4";
};
nixpkgs.hostPlatform = "x86_64-linux";
boot.loader.systemd-boot.enable = true;
networking = { inherit hostName; };
};
in
{
flake.manifest = {
users.rafiq = {
primary = true;
name = "Mohammad Rafiq";
email = "rafiq@rrv.sh";
shell = "fish";
pubkey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILdsZyY3gu8IGB8MzMnLdh+ClDxQQ2RYG9rkeetIKq8n rafiq";
};
hosts.nixos = {
nemesis = {
machine = {
platform = "amd";
gpu = "nvidia";
root.drive = "/dev/disk/by-id/nvme-CT2000P3SSD8_2325E6E77434";
monitors = [
{
id = "desc:OOO AN-270W04K";
scale = "2";
resolution = "3840x2160";
refresh-rate = "60";
}
];
};
extraCfg = testCfg;
};
apollo = {
machine = {
platform = "intel";
root.drive = "/dev/disk/by-id/nvme-eui.002538d221b47b01";
};
extraCfg = testCfg;
};
};
};
}

View file

@ -0,0 +1,28 @@
{
inputs,
withSystem,
lib,
config,
...
}:
let
inherit (builtins) map head;
inherit (lib) concatStringsSep;
mkListEntry = x: "- [" + x.path_ + "](" + x.path_ + ")";
listOfGeneratedFiles = withSystem (head config.systems) (psArgs: psArgs.config.files.files);
in
{
imports = [ inputs.files.flakeModules.default ];
perSystem = psArgs: {
make-shells.default.packages = [ psArgs.config.files.writer.drv ];
};
text.readme.parts."Generated Files" = concatStringsSep "\n" (
[
"This flake uses the [files flake-parts module](https://flake.parts/options/files.html) to generate documentation."
"The list of generated files are:"
]
++ (map mkListEntry listOfGeneratedFiles)
);
}

View file

@ -0,0 +1,24 @@
{ inputs, ... }:
{
imports = [ inputs.git-hooks.flakeModule ];
text.gitignore = ".pre-commit-config.*";
perSystem = psArgs: {
pre-commit.settings.hooks = {
# Nix Linters
deadnix.enable = true;
statix.enable = true;
nil.enable = true;
nixfmt-rfc-style.enable = true;
# Flake Health Checks
flake-checker.enable = true;
# Misc
mixed-line-endings.enable = true;
trim-trailing-whitespace.enable = true;
#TODO: figure out vale
#TODO: make nix develop work
#TODO: add nix flake check
#TODO: add write-files
};
make-shells.default.shellHook = psArgs.config.pre-commit.installationScript;
};
}

View file

@ -0,0 +1,5 @@
{ inputs, ... }:
{
#TODO: add to readme
imports = [ inputs.make-shell.flakeModules.default ];
}

View file

@ -0,0 +1,4 @@
{ inputs, ... }:
{
imports = [ inputs.flake-parts.flakeModules.modules ];
}

View file

@ -0,0 +1,4 @@
{ inputs, ... }:
{
imports = [ inputs.text.flakeModules.default ];
}

12
nix/modules/git.nix Normal file
View file

@ -0,0 +1,12 @@
{
flake.modules.homeManager.default =
{ manifest, config, ... }:
{
home.sessionVariables.GIT_CONFIG_GLOBAL = "$HOME/.config/git/config";
programs.git = {
userName = manifest.users.${config.home.username}.name;
userEmail = manifest.users.${config.home.username}.email;
signing.key = "~/.ssh/id_ed25519.pub";
};
};
}

View file

@ -0,0 +1,20 @@
{ inputs, config, ... }:
let
inherit (cfg.lib) forAllUsers' flattenAttrs;
cfg = config.flake;
hm = inputs.home-manager;
globalCfg = {
useGlobalPkgs = true;
useUserPackages = true;
extraSpecialArgs = { inherit (cfg) manifest; };
sharedModules = [
(flattenAttrs (cfg.modules.homeManager or { }))
];
users = forAllUsers' (name: _: cfg.homes.${name});
};
in
{
imports = [ hm.flakeModules.home-manager ];
flake.modules.nixos.default.imports = [ hm.nixosModules.home-manager ];
flake.modules.nixos.default.config.home-manager = globalCfg;
}

35
nix/modules/hosts.nix Normal file
View file

@ -0,0 +1,35 @@
{
config,
lib,
inputs,
...
}:
let
inherit (lib) nixosSystem;
inherit (cfg.lib) flattenAttrs;
inherit (lib.attrsets) mapAttrs;
cfg = config.flake;
hosts = cfg.manifest.hosts or { };
mkConfigurations =
class: hosts:
mapAttrs (
name: value:
if class == "nixos" then
nixosSystem {
specialArgs = {
inherit inputs;
inherit (cfg) manifest;
hostName = name;
};
modules = [
(flattenAttrs cfg.modules.nixos)
(value.extraCfg or { })
];
}
else
{ }
) hosts;
in
{
flake.nixosConfigurations = mkConfigurations "nixos" hosts.nixos;
}

22
nix/modules/shell.nix Normal file
View file

@ -0,0 +1,22 @@
{ config, lib, ... }:
let
cfg = config.flake;
inherit (cfg.lib) forAllUsers';
inherit (lib.attrsets) mapAttrs';
in
{
flake.modules.nixos.default =
{ pkgs, ... }:
{
programs = mapAttrs' (name: value: {
name = value.shell;
value.enable = true;
}) cfg.manifest.users;
users.users = forAllUsers' (_: value: { shell = pkgs.${value.shell}; });
};
flake.modules.homeManager.default =
{ config, ... }:
{
programs.${cfg.manifest.users.${config.home.username}.shell}.enable = true;
};
}

10
nix/modules/system.nix Normal file
View file

@ -0,0 +1,10 @@
{
flake.modules.nixos.default = {
system.stateVersion = "25.11";
};
flake.modules.homeManager.default =
{ osConfig, ... }:
{
home.stateVersion = osConfig.system.stateVersion;
};
}

34
nix/modules/users.nix Normal file
View file

@ -0,0 +1,34 @@
{ config, lib, ... }:
let
cfg = config.flake;
inherit (cfg.lib) forAllUsers';
inherit (lib.lists) optional;
in
{
flake.modules.nixos.default =
{ config, ... }:
{
#TODO: move sudo/security options elsewhere
# security.sudo.wheelNeedsPassword = false;
# nix.settings.trusted-users = [ "@wheel" ];
#TODO: move ssh key settings elsewhere
# users.users.root.openssh.authorizedKeys.keys = [ owner.pubkey ];
users = {
mutableUsers = false;
groups.users.gid = 100;
users = forAllUsers' (
_: value: {
isNormalUser = true;
extraGroups = optional (value.primary or false) "wheel";
openssh.authorizedKeys.keys = [ value.pubkey ];
}
);
};
home-manager.users = forAllUsers' (
name: _: {
home.username = name;
home.homeDirectory = config.users.users.${name}.home;
}
);
};
}

View file

@ -1,60 +0,0 @@
{ pkgs, ... }:
pkgs.writeShellScriptBin "commit" # bash
''
PROMPT="Please generate a commit message for this diff."
GUIDELINES="1. Use conventional commit syntax, following the context. 2. Cap the commit message at 80 characters, preferably less. You must not go beyond this limit. 3. Do not include backticks. Only generate the raw text."
NUM_ANCESTORS=0
# Parse arguments
while [[ $# -gt 0 ]]; do
case "$1" in
--num-ancestors | -n)
NUM_ANCESTORS="$2"
shift 2
;;
*)
echo "Unrecognised argument: $1. Exiting..."
exit 1
;;
esac
done
# Get context and diff
CONTEXT=$(git --no-pager log -n 10)
DIFF=$(git --no-pager diff HEAD~$NUM_ANCESTORS)
# Generate initial response
RESPONSE=$(aichat "$PROMPT\nGuidelines: $GUIDELINES\nContext from git log:\n$CONTEXT\nDiff from git diff HEAD:\n$DIFF")
while true; do
echo "$RESPONSE"
echo
echo "Choose an action:"
read -p "Options: [y]es, [r]eroll, [e]dit, [q]uit? " -n 1 -r choice
echo
case "$choice" in
y | yes)
git commit -am "$RESPONSE"
echo "Committed successfully."
exit 0
;;
r | reroll)
RESPONSE=$(aichat "$PROMPT\nGuidelines: $GUIDELINES\nContext from git log:\n$CONTEXT\nDiff from git diff HEAD:\n$DIFF")
;;
e | edit)
echo "$RESPONSE" > /tmp/commit_msg.txt
"$EDITOR" /tmp/commit_msg.txt
RESPONSE=$(cat /tmp/commit_msg.txt)
rm /tmp/commit_msg.txt
;;
q | quit | "")
echo "Aborted."
exit 1
;;
*)
echo "Invalid choice. Please choose again."
;;
esac
done
''

View file

@ -1,123 +0,0 @@
{ pkgs, ... }:
pkgs.writeShellScriptBin "deploy" # sh
''
while [[ $# -gt 0 ]]; do
case "$1" in
--user)
USER="$2"
shift 2
;;
--ip)
IP="$2"
shift 2
;;
--hostname)
HOSTNAME="$2"
shift 2
;;
*)
echo "Error: Unknown parameter: $1"
exit 1
;;
esac
done
# Check if required arguments are provided
if [[ -z "$USER" || -z "$IP" || -z "$HOSTNAME" ]]; then
echo "Usage: $0 --user <user> --ip <ip_address> --hostname <hostname> [--wait-timeout <seconds>]"
exit 1
fi
# --- Helper Functions ---
wait_for_ping() {
local ip="$1"
echo "Waiting for ping to $ip..."
while true; do
if ping -c 1 -W 1 "$ip"; then
echo "Ping successful."
return 0
fi
sleep 2
done
}
wait_for_ssh() {
local ip="$1"
echo "Waiting for SSH to $ip..."
while true; do
ssh-keygen -R "$ip" || true # Suppress error if key doesn't exist
if ssh -o StrictHostKeyChecking=no root@"$ip" exit; then
echo "SSH connection successful."
return 0
fi
sleep 2
done
}
retry_rebuild() {
local ip="$1"
echo "Attempting rebuild..."
while true; do
if nixos-rebuild switch --flake . --target-host root@"$ip"; then
echo "Rebuild successful."
return 0
fi
sleep 2
done
}
test_connection() {
local ip="$1"
# Wait for the server to come back up after the reboot. Ping first.
if ! wait_for_ping $ip; then
echo "Error: Server did not respond to ping after reboot."
exit 1
fi
# Wait for SSH access after reboot
if ! wait_for_ssh $ip; then
echo "Error: SSH access not available after reboot."
exit 1
fi
}
# --- Deployment Steps ---
test_connection "$IP"
# Copy SSH key to remote server
ssh-copy-id -o StrictHostKeyChecking=no root@"$IP" || { echo "Error: Failed to copy SSH key."; exit 1; }
# Deploy NixOS configuration using nixos-anywhere
nix run github:nix-community/nixos-anywhere -- \
-i ~/.ssh/id_ed25519 --ssh-option StrictHostKeyChecking=no \
--flake .#"$HOSTNAME" --target-host root@"$IP" || { echo "Error: nixos-anywhere failed."; exit 1; }
test_connection "$IP"
# Create SSH directory on the remote server (if not already present)
ssh root@"$IP" -o StrictHostKeyChecking=no mkdir -p "/persist/home/$USER/.ssh" || { echo "Error: Failed to create SSH directory."; exit 1; }
# Set owner of the user's home directory
ssh root@"$IP" -o StrictHostKeyChecking=no chown -R "$USER:users" "/persist/home/$USER" || { echo "Error: Failed to set ownership."; exit 1; }
# Copy SSH keys to the remote server
scp -r ~/.ssh root@"$IP":/persist/home/"$USER" || { echo "Error: Failed to copy SSH keys."; exit 1; }
#TODO: remove device from tailscale
# Build and switch the configuration
retry_rebuild "$IP"
# Reboot the system
ssh root@"$IP" -o StrictHostKeyChecking=no systemctl reboot || { echo "Error: Failed to reboot."; exit 1; }
test_connection "$IP"
test_connection "$HOSTNAME"
echo "Deployment complete. System should be ready."
''

View file

@ -1,12 +0,0 @@
{ pkgs, ... }:
let
finder = "${pkgs.fzf}/bin/fzf --preview 'cat {}'";
in
pkgs.writeShellScriptBin "edit" # sh
''
if [ $# -gt 0 ]; then
$EDITOR $(${finder} -q $*)
else
$EDITOR $(${finder})
fi
''

View file

@ -1,148 +0,0 @@
{ pkgs, lib, ... }:
let
inherit (lib) getExe;
in
pkgs.writeShellScriptBin "rebuild" # sh
''
QUICK=false
NO_GENERATION_CHECK=false
TEST_SHELL=false
REMOTE_HOSTS=()
REBUILDING_ALL=false
# ANSI color codes
GREEN='\033[0;32m'
ORANGE='\033[0;33m'
RED='\033[0;31m'
NC='\033[0m'
info() {
timestamp=$(date "+%Y-%m-%d %H:%M:%S")
echo -e "''${GREEN}''${timestamp} INFO: $1''${NC}"
}
warn() {
timestamp=$(date "+%Y-%m-%d %H:%M:%S")
echo -e "''${ORANGE}''${timestamp} WARN: $1''${NC}"
}
err() {
timestamp=$(date "+%Y-%m-%d %H:%M:%S")
echo -e "''${RED}''${timestamp} ERROR: $1''${NC}"
}
prompt() {
local PROMPT="$1"
shift
read -p "$PROMPT? (y/n) [n]: " -n 1 -r REPLY
echo
if [[ "$REPLY" =~ ^[Yy]$ ]]; then
"$*"
else
info "$PROMPT aborted."
fi
}
spawn_test_shell() {
info "Spawning test shell on $1..."
(export PS1="Test shell> "
exec ${pkgs.bash}/bin/bash ssh "$1") || {
${pkgs.cowsay}/bin/cowsay "You aborted."
exit 1
}
}
rebuild_remote() {
local args=(".#nixosConfigurations.$1" "--target-host" "$1")
local CURRENT_GENERATION=$(ssh "$1" readlink /nix/var/nix/profiles/system | cut -d- -f2)
if "$TEST_SHELL"; then
info "Testing $1..."
${getExe pkgs.nh} os test "''${args[@]}" || exit 1
git diff HEAD --color=always --stat --patch
spawn_test_shell "$1"
info "Rebuilding $1..."
${getExe pkgs.nh} os boot "''${args[@]}" || exit 1
else
info "Rebuilding $1 on $HOSTNAME..."
${getExe pkgs.nh} os switch "''${args[@]}" || exit 1
fi
if ! "$NO_GENERATION_CHECK"; then
local NEW_GENERATION=$(ssh "$1" readlink /nix/var/nix/profiles/system | cut -d- -f2)
info "$1 - New generation is $NEW_GENERATION. Current is $CURRENT_GENERATION."
if [ ! $NEW_GENERATION -gt $CURRENT_GENERATION ]; then
warn "New config was not added to bootloader."
fi
fi
}
info "Starting rebuild script."
if [ ! -f "flake.nix" ]; then
err "flake.nix not found in the current directory. Exiting."
exit 1 # Indicate an error
fi
while [[ $# -gt 0 ]]; do
case "$1" in
--quick | -q)
QUICK=true
shift
;;
--no-generation-check | -n)
NO_GENERATION_CHECK=true
shift
;;
--test-shell | -t)
TEST_SHELL=true
shift
;;
--all | -a)
reachable_hosts=()
hostnames=$(nix flake show --all-systems --json | , jq -r '.nixosConfigurations | keys | .[]')
for host in ''${hostnames[@]}; do
info "Checking if $host is reachable..."
if ping -c 1 -W 1 "$host" > /dev/null 2>&1 ; then
info "$host is reachable."
reachable_hosts+=("$host")
else
warn "$host is unreachable."
fi
done
REMOTE_HOSTS=(''${reachable_hosts[@]})
REBUILDING_ALL=true
shift
;;
*)
if [ !REBUILDING_ALL ]; then
if ping -c 1 -W 1 "$1" > /dev/null 2>&1 ; then
REMOTE_HOSTS+=("$1")
else
err "$1 is unreachable. Exiting."
exit 1
fi
fi
shift
;;
esac
done
if [ ''${#REMOTE_HOSTS[@]} == 0 ]; then
info "No hostnames provided."
REMOTE_HOSTS=("$HOSTNAME")
fi
git add .
for host in "''${REMOTE_HOSTS[@]}"; do
rebuild_remote $host
done
if ! "$QUICK"; then
prompt "Commit changes" commit
prompt "Reboot system" sudo systemctl reboot
fi
info "Rebuild script completed successfully."
exit 0
''

View file

@ -1,30 +0,0 @@
rafiq:
hashedPassword: ENC[AES256_GCM,data:SzzSPg5Ze4H+fVl6ZvAULO9FDfRehusmP6uldT4Ok2/9ZeOp9r4LgjKajoiw2A1DWD1zQ1GQwMCHKpeZjCC4rBUNWW5DMcBUJA==,iv:KktKuqr0JNhjeJIlIgkoAv6mP2dQlfQrXiIOASLPkbw=,tag:g9LarkT6EjDrH+dXSjMwPg==,type:str]
personalEmailPassword: ENC[AES256_GCM,data:TGJtDO++QcWqU1AbLe4=,iv:RjLRmq7fdbVRbv0M8ZQHyCK5l95JW3TRjN5w9Ci92zs=,tag:JibrH863smajCXESwhAR4g==,type:str]
workEmailPassword: ENC[AES256_GCM,data:++Gm9dIhmqEQz3+Ej9c=,iv:dAvyyLZvsHcjudU4gdU0iyWYDjjhe49UC2swHh++ldc=,tag:6o1DyJk5WOFO/Hfr0uMKSw==,type:str]
oldSMBCredentials: ENC[AES256_GCM,data:aY41trUJcvGa584H0A==,iv:3h9AZ33HXWT4D/vGMyy/o+TXyGg75Ixcj3+h2EskvIQ=,tag:dDo55h1ljOYLZBHn9bK7ew==,type:str]
keys:
openrouter: ENC[AES256_GCM,data:Uddc0leKVD2xxpvDpsTJV3qZ4oe89Uz6dJMuzF/TeI5iIrG+DNIAYPcnIQiA6LDScO9mag8XNiYpYH7lyMnUg1cvThChiVhO+A==,iv:RHSrL/L74dSvLKAvGwyMME53RzKr2+RDnI8xBpDJVng=,tag:d81mr26SeStmAa8UgEF/LA==,type:str]
gemini: ENC[AES256_GCM,data:t4XTzJLMbHBG7LNaWMwO0YyYHREYOp4Zn95Kwshunnpwq9ezVv+0,iv:ZHq1ytak7Qy5a/zHghwEIWRinDWAkk2Vxw4iu/Q/UPk=,tag:Wyk0FqLTOWelznWHg/anxg==,type:str]
cloudflare: ENC[AES256_GCM,data:nrtHnQR0Oon9BrSN0AeAjl8H8B7quuwSu/Qjabe9HFpWgcZq9n1JCA==,iv:ovyHqy5iKXDYXe4H7eRA51+kODhP+vAWoc98cS/6zG0=,tag:JyktO6EMRZ00CRhTb03+fg==,type:str]
telegram_bot: ENC[AES256_GCM,data:qGJx1Bph94oU2USjZL4h2NqV5ueCiYIvEbx84Xg687F5//MItLAS58MZdUPSuQ==,iv:WmldN5Je4miamLXCK6Cv17TTGmaBq/lde2czsEgNBi4=,tag:aU27eDE5PbYAniKEXk+MRA==,type:str]
librechat:
creds_key: ENC[AES256_GCM,data:/fzPgZiDnyWZalJUBFpFQ2/anxvbX3XLp18n+x1xfzOMisq52ISB5VJOzi9xaNRNruQEoh/lva9gDbIgNyzduA==,iv:xGgufMc/tPOLCKEb2MnEkxmf0FPpENGW1FcCm15CW6k=,tag:9aR+DndXkCg1sboxTFuygQ==,type:str]
creds_iv: ENC[AES256_GCM,data:fbBD9RsuEHwDETwiYtAS9kBxgTy6zubrxHWpcuoEsR0=,iv:uZcwIfDPPn4XUf8IZkI29VH9CiKvEOlWuUaWgSjl1Kc=,tag:qbgiQU7bWSFjoGEwoptCpg==,type:str]
jwt_secret: ENC[AES256_GCM,data:ZhDNIXrCaRWWfrlPxpBfnmeUluW0z72KGpQv9mGyf1kCCnfx3V2lPMm6QS6biajC+4oPVfgwqcXc4Lvs8OqU9g==,iv:1Ecj8fh+M5kw8cmVD96U6QgE7fNy9cbQV9v2Q305puc=,tag:U1ZglGWdTH1TGfcIIORMHQ==,type:str]
jwt_refresh_secret: ENC[AES256_GCM,data:/4X6h51oRRaOg7UZ/zUcS1L8QyFnhsTYrz8D6R3ZP/tFAEMO/IfYJHHQQ8UtgKjAEwIVYcpIco8lUDhm06folw==,iv:02/LgoiMZ6MzBSd+JAi+iuF3dzqsVyqX6gQfWPY8sIc=,tag:5VrCh7ZKNJD3ynjcyQpVyg==,type:str]
sops:
age:
- recipient: age12l33pas8eptwjc7ewux3d8snyzfzwz0tn9qg5kw8le79fswmjgjqdjgyy6
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBrUDN6TFlTVHdlWCsrWkFn
R1g5UjVLVk1NQzJRRE9NbDZlRVVJUjVvbmlnCk93NFhSRS9vbDUzNVd6Q3RuTEtZ
cFZvY0JML2tDSUZIbkcyVWVWWVFMY0UKLS0tIDlCbmxhUThUaHRGNkgySEp2QTB1
WXFKbjNMWDF0LzNyekJJMGFva2diemcKQTc8ODuK6IWqRhulHiCF92aU+3p23riY
M94Nzh+VT6QTFOgb3J7bBJMLhRH/fkQb6L6ia2n9QrVXFyYYMJ0oBw==
-----END AGE ENCRYPTED FILE-----
lastmodified: "2025-07-01T21:34:46Z"
mac: ENC[AES256_GCM,data:NvJ6lCb80dsVMH4T4f4ZPO0b4JI44LfMvdanVaWtXDpi6FHJsF4OY8dftIyTBjacaLzdrVoT+JFfP3BrAnuEaZrCrfE1E+IRF4x/9NG4c4Cw++Jxgs7z7d01iYEjWJoVVPCLVnV32LGIq6nQltx2GFEVAsvV5zukJ/aJjvcIpQA=,iv:FWGaIdok23jgxMUs3d5ddK2iyJoOBliwv/yJDxmKLE0=,tag:FfLYymjZEJtW4cfFNhlNFg==,type:str]
unencrypted_suffix: _unencrypted
version: 3.10.2

View file

@ -1,62 +0,0 @@
{
lib,
pkgs,
inputs,
...
}:
{
imports = lib.singleton ../common.nix;
hostname = "apollo";
machine = {
platform.type = "intel";
bootloader.type = "systemd-boot";
drives.btrfs = {
enable = true;
drive = "/dev/disk/by-id/nvme-eui.002538d221b47b01";
ephemeralRoot = true;
};
};
server = {
networking.ddns = {
enable = true;
domains = [
"aenyrathia.wiki"
"slayment.com"
];
};
databases = {
mongodb.enable = true;
mysql.enable = true;
postgresql.enable = true;
};
web-apps = {
librechat.enable = true;
librechat.domain = "chat.bwfiq.com";
forgejo.enable = true;
forgejo.domain = "git.rrv.sh";
forgejo.openFirewall = true;
glance.enable = true;
glance.domain = "glance.bwfiq.com";
rrv-sh.enable = true;
rrv-sh.domain = "rrv.sh";
};
web-servers = {
enableSSL = true;
nginx = {
enable = true;
proxies = [
{
source = "aenyrathia.wiki";
target = "http://helios:5896";
}
{
source = "il.bwfiq.com";
target = "http://helios:2283";
}
];
};
};
};
}

View file

@ -1,13 +0,0 @@
{ pkgs, ... }:
{
mainUser = {
name = "rafiq";
publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILdsZyY3gu8IGB8MzMnLdh+ClDxQQ2RYG9rkeetIKq8n";
email = "rafiq@rrv.sh";
shell = pkgs.fish;
};
server.mountHelios = true;
stylix.base16Scheme = "${pkgs.base16-schemes}/share/themes/atelier-cave.yaml";
programs.fish.enable = true;
programs.nix-ld.enable = true;
}

View file

@ -1,14 +0,0 @@
{
desktop = {
enable = true;
lockscreen.hyprlock.enable = true;
launcher.fuzzel.enable = true;
media-player.vlc.enable = true;
window-manager.hyprland.enable = true;
};
machine.usb = {
automount = true;
enableQmk = true;
};
}

View file

@ -1,24 +0,0 @@
{
imports = [
../common.nix
../desktop.nix
];
hostname = "mellinoe";
machine = {
platform.type = "intel";
bootloader.type = "systemd-boot";
drives.btrfs = {
enable = true;
drive = "/dev/disk/by-id/nvme-KBG40ZPZ128G_TOSHIBA_MEMORY_Z0U103PCNCDL";
ephemeralRoot = true;
};
};
desktop.mainMonitor = {
id = "BOE 0x088B";
scale = "2";
resolution = "1920x1280";
refresh-rate = "60";
};
}

Some files were not shown because too many files have changed in this diff Show more