Compare commits

..

1 commit

Author SHA1 Message Date
a2f660db91 add not working configuration 2024-10-12 01:11:44 +03:00
121 changed files with 242 additions and 7280 deletions

View file

@ -1,11 +0,0 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
version: 2
updates:
- package-ecosystem: "github-actions" # See documentation for possible values
directory: "/" # Location of package manifests
schedule:
interval: "daily"

View file

@ -1,35 +0,0 @@
name: Build SD image for Raspberry Pi 5
on:
workflow_dispatch:
jobs:
build-sd-image:
runs-on: ubuntu-24.04
steps:
- name: Checkout
uses: actions/checkout@v5
- name: Install QEMU
run: sudo apt-get install qemu-user-static
- name: Install Nix
uses: cachix/install-nix-action@v31
with:
extra_nix_config: |
system = aarch64-linux
- name: Build SD image
run: nix build -L '.#nixosConfigurations.pochita-sd.config.system.build.sdImage'
- name: Zip the Resulting SD image
run: |
mkdir -p sd-image
cp -r result/* sd-image
zip -r sd-image.zip sd-image
- name: Upload SD image
uses: actions/upload-artifact@v5
with:
name: sd-image

View file

@ -1,31 +0,0 @@
name: Nix
on:
push:
paths-ignore:
- README.md
pull_request:
paths-ignore:
- README.md
jobs:
build:
strategy:
matrix:
target-system:
- ymir
- tartarus
- pochita
- pochita-sd
- harmonica
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v5
- uses: cachix/install-nix-action@v31
- run: nix flake check --accept-flake-config
- run: nix build --accept-flake-config --dry-run .#nixosConfigurations.${{ matrix.target-system }}.config.system.build.toplevel
check-formatting:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v5
- uses: cachix/install-nix-action@v31
- run: nix flake check --accept-flake-config

View file

@ -1,41 +0,0 @@
name: Build and test the packages i maintain on Nixpkgs
on:
# cron every hour at 3 minutes 00:03 AM, 01:03 AM etc.
schedule:
- cron: '3 0 * * *'
workflow_dispatch:
jobs:
build:
runs-on: ubuntu-24.04
strategy:
matrix:
nixpkgs-branch: [nixos-unstable, nixpkgs-unstable, master] # TODO: add stable branch once they are available
# platform: [x86_64-linux, aarch64-linux] #TODO: add aarch64-linux
myPackages: # TODO automate this list
- python313Packages.runstats
- python313Packages.fastmri
- python312Packages.runstats
- python312Packages.fastmri
- python311Packages.runstats
# - python311Packages.fastmri # broken for now
steps:
- name: Checkout
uses: actions/checkout@v5
- name: Install Nix
uses: cachix/install-nix-action@v31
# with:
# extra_nix_config: |
# # system = ${{ matrix.platform }}
# i want to create .drv file and check if they exist in the nixos.cache
# if they dont exist, i want to skip the build
# - name: Check if the dependencies are in the cache
# run: nix path-info --closure-size --eval-store auto --store https://cache.nixos.org 'github:nixos/nixpkgs/${{ matrix.nixpkgs-branch }}#${{ matrix.myPackages }}^*'
- name: Build my packages
run: nix build -L "github:nixos/nixpkgs/${{ matrix.nixpkgs-branch }}#${{ matrix.myPackages }}"

6
.gitignore vendored
View file

@ -1,6 +0,0 @@
/result*
.DS_Store
.direnv
*.old
.vscode/
.claude/

View file

@ -1,3 +0,0 @@
osbm <osbm@osbm.dev> <osmanfbayram@gmail.com>
osbm <osbm@osbm.dev> <osbm@users.noreply.github.com>
osbm <osbm@osbm.dev> <74963545+osbm@users.noreply.github.com>

View file

@ -1,6 +1,6 @@
MIT License
Copyright (c) 2025 Osman F Bayram
Copyright (c) 2024 Osman F Bayram
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View file

@ -1,42 +1,2 @@
<div align="center">
<h1>osbm's nix flake</h1>
</div>
<details>
<summary>Hosts</summary>
I may have some tendency on naming my hosts after mythological figures...
| Name | Description | Architecture | Status |
|------|-------------|--------------|--------|
| ymir | Desktop workstation | x86_64-linux | ✅ |
| tartarus | Personal laptop | x86_64-linux | ✅ |
| pochita | Raspberry Pi 5 | aarch64-linux | ✅ |
| wallfacer | Dell Enterprise server | x86_64-linux | ✅ |
| atreus | Work Phone (nix-on-droid) | aarch64-linux | ✅ |
| luoji | Personal Phone (nix-on-droid) | aarch64-linux | ✅ |
| prometheus | Work Laptop | x86_64-linux | ✅ |
| artemis | Oneplus 6 (mobile nixos) | aarch64-linux | ✅ |
| harmonica | Raspberry Pi Zero 2 | aarch64-linux | ✅ |
| ares | Valve Steam Deck | aarch64-linux | ❌ |
| apollo | netcup VPS | x86_64-linux | ✅ |
</details>
<details>
<summary>Options</summary>
TODO i want to make all my options documented here
</details>
<details>
<summary>Development Environments</summary>
Currently all my development environments are defined under the devshells repository: https://github.com/osbm/devshells
</details>
# nix-configuration
The nix configuration

206
configuration.nix Normal file
View file

@ -0,0 +1,206 @@
# Edit this configuration file to define what should be installed on
# your system. Help is available in the configuration.nix(5) man page
# and in the NixOS manual (accessible by running nixos-help).
{ config, pkgs, ... }:
{
imports =
[ # Include the results of the hardware scan.
./hardware-configuration.nix
];
# Bootloader.
boot.loader.systemd-boot.enable = true;
boot.loader.efi.canTouchEfiVariables = true;
networking.hostName = "tartarus"; # Define your hostname.
# networking.wireless.enable = true; # Enables wireless support via wpa_supplicant.
# Configure network proxy if necessary
# networking.proxy.default = "http://user:password@proxy:port/";
# networking.proxy.noProxy = "127.0.0.1,localhost,internal.domain";
# Enable networking
networking.networkmanager.enable = true;
# Set your time zone.
time.timeZone = "Europe/Istanbul";
# Select internationalisation properties.
i18n.defaultLocale = "en_US.UTF-8";
i18n.extraLocaleSettings = {
LC_ADDRESS = "tr_TR.UTF-8";
LC_IDENTIFICATION = "tr_TR.UTF-8";
LC_MEASUREMENT = "tr_TR.UTF-8";
LC_MONETARY = "tr_TR.UTF-8";
LC_NAME = "tr_TR.UTF-8";
LC_NUMERIC = "tr_TR.UTF-8";
LC_PAPER = "tr_TR.UTF-8";
LC_TELEPHONE = "tr_TR.UTF-8";
LC_TIME = "tr_TR.UTF-8";
};
# Enable the X11 windowing system.
# You can disable this if you're only using the Wayland session.
services.xserver.enable = true;
# Enable the KDE Plasma Desktop Environment.
services.displayManager.sddm.enable = true;
services.desktopManager.plasma6.enable = true;
# Configure keymap in X11
services.xserver.xkb = {
layout = "us";
variant = "";
};
# Enable CUPS to print documents.
services.printing.enable = true;
# Enable sound with pipewire.
hardware.pulseaudio.enable = false;
security.rtkit.enable = true;
services.pipewire = {
enable = true;
alsa.enable = true;
alsa.support32Bit = true;
pulse.enable = true;
# If you want to use JACK applications, uncomment this
#jack.enable = true;
# use the example session manager (no others are packaged yet so this is enabled by default,
# no need to redefine it in your config for now)
#media-session.enable = true;
};
# Enable touchpad support (enabled default in most desktopManager).
# services.xserver.libinput.enable = true;
# Define a user account. Don't forget to set a password with passwd.
users.users.osbm = {
isNormalUser = true;
description = "osbm";
extraGroups = [ "networkmanager" "wheel" ];
packages = with pkgs; [
kdePackages.kate
vscode
discord
alacritty
# thunderbird
];
};
# Install firefox.
programs.firefox.enable = true;
# Allow unfree packages
nixpkgs.config.allowUnfree = true;
nix.settings.experimental-features = [ "nix-command" "flakes"];
# List packages installed in system profile. To search, run:
# $ nix search wget
environment.systemPackages = with pkgs; [
# vim # Do not forget to add an editor to edit configuration.nix! The Nano editor is also installed by default.
wget
git
gnumake
zip
fish
trash-cli
tmux
zoxide
htop
unzip
tlrc
];
# services.xserver.videoDrivers = ["nvidia"]; # this setting makes my computer crash
# Enable OpenGL
hardware.opengl = {
enable = true;
};
# Load nvidia driver for Xorg and Wayland
services.xserver.videoDrivers = ["nvidia"];
hardware.nvidia = {
# Modesetting is required.
modesetting.enable = true;
# Nvidia power management. Experimental, and can cause sleep/suspend to fail.
# Enable this if you have graphical corruption issues or application crashes after waking
# up from sleep. This fixes it by saving the entire VRAM memory to /tmp/ instead
# of just the bare essentials.
powerManagement.enable = false;
# Fine-grained power management. Turns off GPU when not in use.
# Experimental and only works on modern Nvidia GPUs (Turing or newer).
powerManagement.finegrained = false;
# Use the NVidia open source kernel module (not to be confused with the
# independent third-party "nouveau" open source driver).
# Support is limited to the Turing and later architectures. Full list of
# supported GPUs is at:
# https://github.com/NVIDIA/open-gpu-kernel-modules#compatible-gpus
# Only available from driver 515.43.04+
# Currently alpha-quality/buggy, so false is currently the recommended setting.
open = false;
# Enable the Nvidia settings menu,
# accessible via `nvidia-settings`.
nvidiaSettings = true;
# Optionally, you may need to select the appropriate driver version for your specific GPU.
# package = config.boot.kernelPackages.nvidiaPackages.stable;
package = config.boot.kernelPackages.nvidiaPackages.legacy_470;
};
hardware.nvidia.prime = {
offload = {
enable = true;
enableOffloadCmd = true;
};
# Make sure to use the correct Bus ID values for your system!
intelBusId = "PCI:0:2:0";
nvidiaBusId = "PCI:6:0:0";
# amdgpuBusId = "PCI:54:0:0"; For AMD GPU
};
# Some programs need SUID wrappers, can be configured further or are
# started in user sessions.
# programs.mtr.enable = true;
# programs.gnupg.agent = {
# enable = true;
# enableSSHSupport = true;
# };
# List services that you want to enable:
# Enable the OpenSSH daemon.
# services.openssh.enable = true;
# Open ports in the firewall.
# networking.firewall.allowedTCPPorts = [ ... ];
# networking.firewall.allowedUDPPorts = [ ... ];
# Or disable the firewall altogether.
# networking.firewall.enable = false;
# This value determines the NixOS release from which the default
# settings for stateful data, like file locations and database versions
# on your system were taken. Its perfectly fine and recommended to leave
# this value at the release version of the first install of this system.
# Before changing this value read the documentation for this option
# (e.g. man configuration.nix or on https://nixos.org/nixos/options.html).
system.stateVersion = "24.05"; # Did you read the comment?
}

898
flake.lock generated
View file

@ -1,912 +1,24 @@
{
"nodes": {
"agenix": {
"inputs": {
"darwin": "darwin",
"home-manager": [
"home-manager"
],
"nixpkgs": [
"nixpkgs"
],
"systems": "systems"
},
"locked": {
"lastModified": 1762618334,
"narHash": "sha256-wyT7Pl6tMFbFrs8Lk/TlEs81N6L+VSybPfiIgzU8lbQ=",
"owner": "ryantm",
"repo": "agenix",
"rev": "fcdea223397448d35d9b31f798479227e80183f6",
"type": "github"
},
"original": {
"owner": "ryantm",
"repo": "agenix",
"type": "github"
}
},
"blobs": {
"flake": false,
"locked": {
"lastModified": 1604995301,
"narHash": "sha256-wcLzgLec6SGJA8fx1OEN1yV/Py5b+U5iyYpksUY/yLw=",
"owner": "simple-nixos-mailserver",
"repo": "blobs",
"rev": "2cccdf1ca48316f2cfd1c9a0017e8de5a7156265",
"type": "gitlab"
},
"original": {
"owner": "simple-nixos-mailserver",
"repo": "blobs",
"type": "gitlab"
}
},
"darwin": {
"inputs": {
"nixpkgs": [
"agenix",
"nixpkgs"
]
},
"locked": {
"lastModified": 1744478979,
"narHash": "sha256-dyN+teG9G82G+m+PX/aSAagkC+vUv0SgUw3XkPhQodQ=",
"owner": "lnl7",
"repo": "nix-darwin",
"rev": "43975d782b418ebf4969e9ccba82466728c2851b",
"type": "github"
},
"original": {
"owner": "lnl7",
"ref": "master",
"repo": "nix-darwin",
"type": "github"
}
},
"disko": {
"inputs": {
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1766150702,
"narHash": "sha256-P0kM+5o+DKnB6raXgFEk3azw8Wqg5FL6wyl9jD+G5a4=",
"owner": "nix-community",
"repo": "disko",
"rev": "916506443ecd0d0b4a0f4cf9d40a3c22ce39b378",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "disko",
"type": "github"
}
},
"flake-compat": {
"flake": false,
"locked": {
"lastModified": 1761588595,
"narHash": "sha256-XKUZz9zewJNUj46b4AJdiRZJAvSZ0Dqj2BNfXvFlJC4=",
"owner": "edolstra",
"repo": "flake-compat",
"rev": "f387cd2afec9419c8ee37694406ca490c3f34ee5",
"type": "github"
},
"original": {
"owner": "edolstra",
"repo": "flake-compat",
"type": "github"
}
},
"flake-parts": {
"inputs": {
"nixpkgs-lib": [
"osbm-nvim",
"nixvim",
"nixpkgs"
]
},
"locked": {
"lastModified": 1765835352,
"narHash": "sha256-XswHlK/Qtjasvhd1nOa1e8MgZ8GS//jBoTqWtrS1Giw=",
"owner": "hercules-ci",
"repo": "flake-parts",
"rev": "a34fae9c08a15ad73f295041fec82323541400a9",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "flake-parts",
"type": "github"
}
},
"git-hooks": {
"inputs": {
"flake-compat": [
"simple-nixos-mailserver",
"flake-compat"
],
"gitignore": "gitignore",
"nixpkgs": [
"simple-nixos-mailserver",
"nixpkgs"
]
},
"locked": {
"lastModified": 1763988335,
"narHash": "sha256-QlcnByMc8KBjpU37rbq5iP7Cp97HvjRP0ucfdh+M4Qc=",
"owner": "cachix",
"repo": "git-hooks.nix",
"rev": "50b9238891e388c9fdc6a5c49e49c42533a1b5ce",
"type": "github"
},
"original": {
"owner": "cachix",
"repo": "git-hooks.nix",
"type": "github"
}
},
"gitignore": {
"inputs": {
"nixpkgs": [
"simple-nixos-mailserver",
"git-hooks",
"nixpkgs"
]
},
"locked": {
"lastModified": 1709087332,
"narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=",
"owner": "hercules-ci",
"repo": "gitignore.nix",
"rev": "637db329424fd7e46cf4185293b9cc8c88c95394",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "gitignore.nix",
"type": "github"
}
},
"home-manager": {
"inputs": {
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1766553851,
"narHash": "sha256-hHKQhHkXxuPJwLkI8wdu826GLV5AcuW9/HVdc9eBnTU=",
"owner": "nix-community",
"repo": "home-manager",
"rev": "7eca7f7081036a7b740090994c9ec543927f89a7",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "home-manager",
"type": "github"
}
},
"home-manager_2": {
"inputs": {
"nixpkgs": [
"nix-on-droid",
"nixpkgs"
]
},
"locked": {
"lastModified": 1709445365,
"narHash": "sha256-DVv6nd9FQBbMWbOmhq0KVqmlc3y3FMSYl49UXmMcO+0=",
"owner": "nix-community",
"repo": "home-manager",
"rev": "4de84265d7ec7634a69ba75028696d74de9a44a7",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "home-manager",
"type": "github"
}
},
"impermanence": {
"locked": {
"lastModified": 1737831083,
"narHash": "sha256-LJggUHbpyeDvNagTUrdhe/pRVp4pnS6wVKALS782gRI=",
"owner": "nix-community",
"repo": "impermanence",
"rev": "4b3e914cdf97a5b536a889e939fb2fd2b043a170",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "impermanence",
"type": "github"
}
},
"jovian-nixos": {
"inputs": {
"nix-github-actions": "nix-github-actions",
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1766561058,
"narHash": "sha256-VFqsBWqFFBTBqKFw0fGw2a2mJjPP9HPW8nXEW2A5zJM=",
"owner": "Jovian-Experiments",
"repo": "Jovian-NixOS",
"rev": "9d0abe57d633a6e08d72865a761891a8c81e740f",
"type": "github"
},
"original": {
"owner": "Jovian-Experiments",
"repo": "Jovian-NixOS",
"type": "github"
}
},
"libcamera-src": {
"flake": false,
"locked": {
"lastModified": 1725630279,
"narHash": "sha256-KH30jmHfxXq4j2CL7kv18DYECJRp9ECuWNPnqPZajPA=",
"owner": "raspberrypi",
"repo": "libcamera",
"rev": "69a894c4adad524d3063dd027f5c4774485cf9db",
"type": "github"
},
"original": {
"owner": "raspberrypi",
"repo": "libcamera",
"rev": "69a894c4adad524d3063dd027f5c4774485cf9db",
"type": "github"
}
},
"libpisp-src": {
"flake": false,
"locked": {
"lastModified": 1724944683,
"narHash": "sha256-Fo2UJmQHS855YSSKKmGrsQnJzXog1cdpkIOO72yYAM4=",
"owner": "raspberrypi",
"repo": "libpisp",
"rev": "28196ed6edcfeda88d23cc5f213d51aa6fa17bb3",
"type": "github"
},
"original": {
"owner": "raspberrypi",
"ref": "v1.0.7",
"repo": "libpisp",
"type": "github"
}
},
"mobile-nixos": {
"flake": false,
"locked": {
"lastModified": 1763065138,
"narHash": "sha256-46lJeUYH8YrfTccAoKQbO9lprq4dlo9VVLk+StPBSWM=",
"owner": "mobile-nixos",
"repo": "mobile-nixos",
"rev": "1943b7b06fcd1a2c87f4b89df58231915cc2ca44",
"type": "github"
},
"original": {
"owner": "mobile-nixos",
"repo": "mobile-nixos",
"type": "github"
}
},
"nix-darwin": {
"inputs": {
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1766524813,
"narHash": "sha256-N/sxS27+t9nGvGWqwwAceSMW/Y5ddcypS/aiTnZ7ScA=",
"owner": "nix-darwin",
"repo": "nix-darwin",
"rev": "c2b36207f2c396c79dbed9d40536db221bd4e363",
"type": "github"
},
"original": {
"owner": "nix-darwin",
"ref": "master",
"repo": "nix-darwin",
"type": "github"
}
},
"nix-formatter-pack": {
"inputs": {
"nixpkgs": [
"nix-on-droid",
"nixpkgs"
],
"nmd": [
"nix-on-droid",
"nmd"
],
"nmt": "nmt"
},
"locked": {
"lastModified": 1705252799,
"narHash": "sha256-HgSTREh7VoXjGgNDwKQUYcYo13rPkltW7IitHrTPA5c=",
"owner": "Gerschtli",
"repo": "nix-formatter-pack",
"rev": "2de39dedd79aab14c01b9e2934842051a160ffa5",
"type": "github"
},
"original": {
"owner": "Gerschtli",
"repo": "nix-formatter-pack",
"type": "github"
}
},
"nix-formatter-pack_2": {
"inputs": {
"nixpkgs": [
"osbm-nvim",
"nixpkgs"
],
"nmd": "nmd_2",
"nmt": "nmt_2"
},
"locked": {
"lastModified": 1763721296,
"narHash": "sha256-xWX9+lScfVcSejkwieNpeTYdNbyWY/dxf0mLjsy+9ek=",
"owner": "Gerschtli",
"repo": "nix-formatter-pack",
"rev": "8ccdeef5cd29bc30ee881d425d53ef2ae884d64d",
"type": "github"
},
"original": {
"owner": "Gerschtli",
"repo": "nix-formatter-pack",
"type": "github"
}
},
"nix-github-actions": {
"inputs": {
"nixpkgs": [
"jovian-nixos",
"nixpkgs"
]
},
"locked": {
"lastModified": 1729697500,
"narHash": "sha256-VFTWrbzDlZyFHHb1AlKRiD/qqCJIripXKiCSFS8fAOY=",
"owner": "zhaofengli",
"repo": "nix-github-actions",
"rev": "e418aeb728b6aa5ca8c5c71974e7159c2df1d8cf",
"type": "github"
},
"original": {
"owner": "zhaofengli",
"ref": "matrix-name",
"repo": "nix-github-actions",
"type": "github"
}
},
"nix-index-database": {
"inputs": {
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1765267181,
"narHash": "sha256-d3NBA9zEtBu2JFMnTBqWj7Tmi7R5OikoU2ycrdhQEws=",
"owner": "nix-community",
"repo": "nix-index-database",
"rev": "82befcf7dc77c909b0f2a09f5da910ec95c5b78f",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "nix-index-database",
"type": "github"
}
},
"nix-on-droid": {
"inputs": {
"home-manager": "home-manager_2",
"nix-formatter-pack": "nix-formatter-pack",
"nixpkgs": [
"nixpkgs"
],
"nixpkgs-docs": "nixpkgs-docs",
"nixpkgs-for-bootstrap": "nixpkgs-for-bootstrap",
"nmd": "nmd"
},
"locked": {
"lastModified": 1765031149,
"narHash": "sha256-4ZtlnCp4blhsjGnQIxAXDAj7nCJKy7tozoBRtklmwcU=",
"owner": "nix-community",
"repo": "nix-on-droid",
"rev": "55b6449b4582a4ba3ce712543c973360a026db7d",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "nix-on-droid",
"type": "github"
}
},
"nixos-hardware": {
"locked": {
"lastModified": 1766568855,
"narHash": "sha256-UXVtN77D7pzKmzOotFTStgZBqpOcf8cO95FcupWp4Zo=",
"owner": "NixOS",
"repo": "nixos-hardware",
"rev": "c5db9569ac9cc70929c268ac461f4003e3e5ca80",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "master",
"repo": "nixos-hardware",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1766309749,
"narHash": "sha256-3xY8CZ4rSnQ0NqGhMKAy5vgC+2IVK0NoVEzDoOh4DA4=",
"lastModified": 1728500571,
"narHash": "sha256-dOymOQ3AfNI4Z337yEwHGohrVQb4yPODCW9MDUyAc4w=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "a6531044f6d0bef691ea18d4d4ce44d0daa6e816",
"rev": "d51c28603def282a24fa034bcb007e2bcb5b5dd0",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"ref": "nixos-24.05",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs-docs": {
"locked": {
"lastModified": 1705957679,
"narHash": "sha256-Q8LJaVZGJ9wo33wBafvZSzapYsjOaNjP/pOnSiKVGHY=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "9a333eaa80901efe01df07eade2c16d183761fa3",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "release-23.05",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs-for-bootstrap": {
"locked": {
"lastModified": 1720244366,
"narHash": "sha256-WrDV0FPMVd2Sq9hkR5LNHudS3OSMmUrs90JUTN+MXpA=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "49ee0e94463abada1de470c9c07bfc12b36dcf40",
"type": "github"
},
"original": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "49ee0e94463abada1de470c9c07bfc12b36dcf40",
"type": "github"
}
},
"nixpkgs_2": {
"locked": {
"lastModified": 1766309749,
"narHash": "sha256-3xY8CZ4rSnQ0NqGhMKAy5vgC+2IVK0NoVEzDoOh4DA4=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "a6531044f6d0bef691ea18d4d4ce44d0daa6e816",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_3": {
"locked": {
"lastModified": 1766125104,
"narHash": "sha256-l/YGrEpLromL4viUo5GmFH3K5M1j0Mb9O+LiaeCPWEM=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "7d853e518814cca2a657b72eeba67ae20ebf7059",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_4": {
"locked": {
"lastModified": 1736061677,
"narHash": "sha256-DjkQPnkAfd7eB522PwnkGhOMuT9QVCZspDpJJYyOj60=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "cbd8ec4de4469333c82ff40d057350c30e9f7d36",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-24.11",
"repo": "nixpkgs",
"type": "github"
}
},
"nixvim": {
"inputs": {
"flake-parts": "flake-parts",
"nixpkgs": "nixpkgs_3",
"systems": "systems_2"
},
"locked": {
"lastModified": 1766443759,
"narHash": "sha256-iGDhUPOPyY9NOTNHkhNzZKoz3+OlBNGg451qtvPq/Ic=",
"owner": "nix-community",
"repo": "nixvim",
"rev": "1787eeda5a2ce35bcd57dbb482718b0d897786ae",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "nixvim",
"type": "github"
}
},
"nmd": {
"inputs": {
"nixpkgs": [
"nix-on-droid",
"nixpkgs-docs"
],
"scss-reset": "scss-reset"
},
"locked": {
"lastModified": 1705050560,
"narHash": "sha256-x3zzcdvhJpodsmdjqB4t5mkVW22V3wqHLOun0KRBzUI=",
"owner": "~rycee",
"repo": "nmd",
"rev": "66d9334933119c36f91a78d565c152a4fdc8d3d3",
"type": "sourcehut"
},
"original": {
"owner": "~rycee",
"repo": "nmd",
"type": "sourcehut"
}
},
"nmd_2": {
"flake": false,
"locked": {
"lastModified": 1666190571,
"narHash": "sha256-Z1hc7M9X6L+H83o9vOprijpzhTfOBjd0KmUTnpHAVjA=",
"owner": "rycee",
"repo": "nmd",
"rev": "b75d312b4f33bd3294cd8ae5c2ca8c6da2afc169",
"type": "gitlab"
},
"original": {
"owner": "rycee",
"repo": "nmd",
"type": "gitlab"
}
},
"nmt": {
"flake": false,
"locked": {
"lastModified": 1648075362,
"narHash": "sha256-u36WgzoA84dMVsGXzml4wZ5ckGgfnvS0ryzo/3zn/Pc=",
"owner": "rycee",
"repo": "nmt",
"rev": "d83601002c99b78c89ea80e5e6ba21addcfe12ae",
"type": "gitlab"
},
"original": {
"owner": "rycee",
"repo": "nmt",
"type": "gitlab"
}
},
"nmt_2": {
"flake": false,
"locked": {
"lastModified": 1648075362,
"narHash": "sha256-u36WgzoA84dMVsGXzml4wZ5ckGgfnvS0ryzo/3zn/Pc=",
"owner": "rycee",
"repo": "nmt",
"rev": "d83601002c99b78c89ea80e5e6ba21addcfe12ae",
"type": "gitlab"
},
"original": {
"owner": "rycee",
"repo": "nmt",
"type": "gitlab"
}
},
"osbm-nvim": {
"inputs": {
"nix-formatter-pack": "nix-formatter-pack_2",
"nixpkgs": "nixpkgs_2",
"nixvim": "nixvim"
},
"locked": {
"lastModified": 1766602086,
"narHash": "sha256-2uW+3Kxk92423NZFnwTjLDymQ/v12e1YWTfZJM423Bw=",
"owner": "osbm",
"repo": "osbm-nvim",
"rev": "99c87e956bfd0ee36ebc31073aab50e7dfa39432",
"type": "github"
},
"original": {
"owner": "osbm",
"repo": "osbm-nvim",
"type": "github"
}
},
"raspberry-pi-nix": {
"inputs": {
"libcamera-src": "libcamera-src",
"libpisp-src": "libpisp-src",
"nixpkgs": "nixpkgs_4",
"rpi-bluez-firmware-src": "rpi-bluez-firmware-src",
"rpi-firmware-nonfree-src": "rpi-firmware-nonfree-src",
"rpi-firmware-src": "rpi-firmware-src",
"rpi-linux-6_12_17-src": "rpi-linux-6_12_17-src",
"rpi-linux-6_6_78-src": "rpi-linux-6_6_78-src",
"rpi-linux-stable-src": "rpi-linux-stable-src",
"rpicam-apps-src": "rpicam-apps-src"
},
"locked": {
"lastModified": 1742223591,
"narHash": "sha256-ZNTz8r5jlJ1jvpqf5+aUYgpnYJSVX0iP14doOc1Hm0E=",
"owner": "nix-community",
"repo": "raspberry-pi-nix",
"rev": "3e8100d5e976a6a2be363015cb33463af9ef441a",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "raspberry-pi-nix",
"type": "github"
}
},
"root": {
"inputs": {
"agenix": "agenix",
"disko": "disko",
"home-manager": "home-manager",
"impermanence": "impermanence",
"jovian-nixos": "jovian-nixos",
"mobile-nixos": "mobile-nixos",
"nix-darwin": "nix-darwin",
"nix-index-database": "nix-index-database",
"nix-on-droid": "nix-on-droid",
"nixos-hardware": "nixos-hardware",
"nixpkgs": "nixpkgs",
"osbm-nvim": "osbm-nvim",
"raspberry-pi-nix": "raspberry-pi-nix",
"simple-nixos-mailserver": "simple-nixos-mailserver",
"treefmt-nix": "treefmt-nix"
}
},
"rpi-bluez-firmware-src": {
"flake": false,
"locked": {
"lastModified": 1708969706,
"narHash": "sha256-KakKnOBeWxh0exu44beZ7cbr5ni4RA9vkWYb9sGMb8Q=",
"owner": "RPi-Distro",
"repo": "bluez-firmware",
"rev": "78d6a07730e2d20c035899521ab67726dc028e1c",
"type": "github"
},
"original": {
"owner": "RPi-Distro",
"ref": "bookworm",
"repo": "bluez-firmware",
"type": "github"
}
},
"rpi-firmware-nonfree-src": {
"flake": false,
"locked": {
"lastModified": 1723266537,
"narHash": "sha256-T7eTKXqY9cxEMdab8Snda4CEOrEihy5uOhA6Fy+Mhnw=",
"owner": "RPi-Distro",
"repo": "firmware-nonfree",
"rev": "4b356e134e8333d073bd3802d767a825adec3807",
"type": "github"
},
"original": {
"owner": "RPi-Distro",
"ref": "bookworm",
"repo": "firmware-nonfree",
"type": "github"
}
},
"rpi-firmware-src": {
"flake": false,
"locked": {
"lastModified": 1728405098,
"narHash": "sha256-4gnK0KbqFnjBmWia9Jt2gveVWftmHrprpwBqYVqE/k0=",
"owner": "raspberrypi",
"repo": "firmware",
"rev": "7bbb5f80d20a2335066a8781459c9f33e5eebc64",
"type": "github"
},
"original": {
"owner": "raspberrypi",
"ref": "1.20241008",
"repo": "firmware",
"type": "github"
}
},
"rpi-linux-6_12_17-src": {
"flake": false,
"locked": {
"lastModified": 1740765145,
"narHash": "sha256-hoCsGc4+RC/2LmxDtswLBL5ZhWlw4vSiL4Vkl39r2MU=",
"owner": "raspberrypi",
"repo": "linux",
"rev": "5985ce32e511f4e8279a841a1b06a8c7d972b386",
"type": "github"
},
"original": {
"owner": "raspberrypi",
"ref": "rpi-6.12.y",
"repo": "linux",
"type": "github"
}
},
"rpi-linux-6_6_78-src": {
"flake": false,
"locked": {
"lastModified": 1740503700,
"narHash": "sha256-Y8+ot4Yi3UKwlZK3ap15rZZ16VZDvmeFkD46+6Ku7bE=",
"owner": "raspberrypi",
"repo": "linux",
"rev": "2e071057fded90e789c0101498e45a1778be93fe",
"type": "github"
},
"original": {
"owner": "raspberrypi",
"ref": "rpi-6.6.y",
"repo": "linux",
"type": "github"
}
},
"rpi-linux-stable-src": {
"flake": false,
"locked": {
"lastModified": 1728403745,
"narHash": "sha256-phCxkuO+jUGZkfzSrBq6yErQeO2Td+inIGHxctXbD5U=",
"owner": "raspberrypi",
"repo": "linux",
"rev": "5aeecea9f4a45248bcf564dec924965e066a7bfd",
"type": "github"
},
"original": {
"owner": "raspberrypi",
"ref": "stable_20241008",
"repo": "linux",
"type": "github"
}
},
"rpicam-apps-src": {
"flake": false,
"locked": {
"lastModified": 1727515047,
"narHash": "sha256-qCYGrcibOeGztxf+sd44lD6VAOGoUNwRqZDdAmcTa/U=",
"owner": "raspberrypi",
"repo": "rpicam-apps",
"rev": "a8ccf9f3cd9df49875dfb834a2b490d41d226031",
"type": "github"
},
"original": {
"owner": "raspberrypi",
"ref": "v1.5.2",
"repo": "rpicam-apps",
"type": "github"
}
},
"scss-reset": {
"flake": false,
"locked": {
"lastModified": 1631450058,
"narHash": "sha256-muDlZJPtXDIGevSEWkicPP0HQ6VtucbkMNygpGlBEUM=",
"owner": "andreymatin",
"repo": "scss-reset",
"rev": "0cf50e27a4e95e9bb5b1715eedf9c54dee1a5a91",
"type": "github"
},
"original": {
"owner": "andreymatin",
"repo": "scss-reset",
"type": "github"
}
},
"simple-nixos-mailserver": {
"inputs": {
"blobs": "blobs",
"flake-compat": "flake-compat",
"git-hooks": "git-hooks",
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1766321686,
"narHash": "sha256-icOWbnD977HXhveirqA10zoqvErczVs3NKx8Bj+ikHY=",
"owner": "simple-nixos-mailserver",
"repo": "nixos-mailserver",
"rev": "7d433bf89882f61621f95082e90a4ab91eb0bdd3",
"type": "gitlab"
},
"original": {
"owner": "simple-nixos-mailserver",
"repo": "nixos-mailserver",
"type": "gitlab"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
},
"systems_2": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
},
"treefmt-nix": {
"inputs": {
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1766000401,
"narHash": "sha256-+cqN4PJz9y0JQXfAK5J1drd0U05D5fcAGhzhfVrDlsI=",
"owner": "numtide",
"repo": "treefmt-nix",
"rev": "42d96e75aa56a3f70cab7e7dc4a32868db28e8fd",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "treefmt-nix",
"type": "github"
"nixpkgs": "nixpkgs"
}
}
},

145
flake.nix
View file

@ -1,148 +1,19 @@
{
description = "My system configuration";
nixConfig = {
extra-substituters = [
"https://nix-community.cachix.org"
# "http://wallfacer.curl-boga.ts.net:7080/main"
];
extra-trusted-public-keys = [
"nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs="
# "main:2AjPdIsbKyoTGuw+4x2ZXMUT/353CXosW9pdbTQtjqw="
];
};
inputs = {
agenix = {
url = "github:ryantm/agenix";
inputs.nixpkgs.follows = "nixpkgs";
inputs.home-manager.follows = "home-manager";
};
home-manager = {
url = "github:nix-community/home-manager";
inputs.nixpkgs.follows = "nixpkgs";
};
nixos-hardware.url = "github:NixOS/nixos-hardware/master";
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
nix-darwin = {
url = "github:nix-darwin/nix-darwin/master";
inputs.nixpkgs.follows = "nixpkgs";
};
nix-on-droid = {
url = "github:nix-community/nix-on-droid";
inputs.nixpkgs.follows = "nixpkgs";
};
osbm-nvim.url = "github:osbm/osbm-nvim";
treefmt-nix = {
url = "github:numtide/treefmt-nix";
inputs.nixpkgs.follows = "nixpkgs";
};
raspberry-pi-nix = {
url = "github:nix-community/raspberry-pi-nix";
};
# colmena = {
# url = "github:zhaofengli/colmena";
# inputs.nixpkgs.follows = "nixpkgs";
# };
nix-index-database = {
url = "github:nix-community/nix-index-database";
inputs.nixpkgs.follows = "nixpkgs";
};
disko = {
url = "github:nix-community/disko";
inputs.nixpkgs.follows = "nixpkgs";
};
impermanence.url = "github:nix-community/impermanence";
mobile-nixos = {
url = "github:mobile-nixos/mobile-nixos";
flake = false;
};
simple-nixos-mailserver = {
url = "gitlab:simple-nixos-mailserver/nixos-mailserver";
inputs.nixpkgs.follows = "nixpkgs";
};
jovian-nixos = {
url = "github:Jovian-Experiments/Jovian-NixOS";
inputs.nixpkgs.follows = "nixpkgs";
};
nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.05";
};
outputs =
{
self,
nixpkgs,
nix-on-droid,
nix-darwin,
treefmt-nix,
...
}@inputs:
outputs = { self, nixpkgs, ... }:
let
inherit (self) outputs;
supportedSystems = [
"x86_64-linux"
"aarch64-linux"
"x86_64-darwin"
"aarch64-darwin"
];
forAllSystems = f: nixpkgs.lib.genAttrs supportedSystems f;
makePkgs = system: import nixpkgs { inherit system; };
makeNixosConfig =
configName:
nixpkgs.lib.nixosSystem {
specialArgs = { inherit inputs outputs; };
modules = [ ./hosts/nixos/${configName}/configuration.nix ];
lib = nixpkgs.lib;
in {
nixosConfigurations = {
tartarus = lib.nixosSystem {
system = "x86_64-linux";
modules = [ ./configuration.nix ];
};
nixosConfigNames = builtins.attrNames (builtins.readDir ./hosts/nixos);
makeNixOnDroidConfig =
configName:
nix-on-droid.lib.nixOnDroidConfiguration {
extraSpecialArgs = { inherit inputs outputs; };
pkgs = import nixpkgs { system = "aarch64-linux"; };
modules = [ ./hosts/nixOnDroidHosts/${configName}/configuration.nix ];
};
nixOnDroidConfigNames = builtins.attrNames (builtins.readDir ./hosts/nixOnDroidHosts);
treefmtEval = forAllSystems (system: treefmt-nix.lib.evalModule (makePkgs system) ./treefmt.nix);
in
{
nixosConfigurations = nixpkgs.lib.genAttrs nixosConfigNames makeNixosConfig;
nixOnDroidConfigurations = nixpkgs.lib.genAttrs nixOnDroidConfigNames makeNixOnDroidConfig;
darwinConfigurations.prometheus = nix-darwin.lib.darwinSystem {
system = "x86_64-darwin";
modules = [ ./hosts/darwinHosts/prometheus/configuration.nix ];
specialArgs = { inherit inputs outputs; };
};
lib = import ./lib { inherit (nixpkgs) lib; };
formatter = forAllSystems (system: treefmtEval.${system}.config.build.wrapper);
checks = forAllSystems (system: {
formatting = treefmtEval.${system}.config.build.check self;
});
nixosModules.default = ./modules/nixos;
homeManagerModules.default = ./modules/home-manager;
# packages = forAllSystems (
# system:
# let
# makeNixosConfigWithSystemOverride =
# configName:
# nixpkgs.lib.nixosSystem {
# specialArgs = { inherit inputs outputs; };
# modules = [
# ./hosts/nixos/${configName}/configuration.nix
# { nixpkgs.hostPlatform = nixpkgs.lib.mkForce system; }
# ];
# };
# dotfilesMachineNames = [
# "ymir"
# "pochita"
# "tartarus"
# "wallfacer"
# ];
# in
# builtins.listToAttrs (
# map (name: {
# name = "${name}-dotfiles";
# value = (makeNixosConfigWithSystemOverride name).config.home-manager.users.osbm.home-files;
# }) dotfilesMachineNames
# )
# );
};
}

View file

@ -1,58 +1,39 @@
# Do not modify this file! It was generated by nixos-generate-config
# and may be overwritten by future invocations. Please make changes
# to /etc/nixos/configuration.nix instead.
{
config,
lib,
modulesPath,
...
}:
{
imports = [
(modulesPath + "/installer/scan/not-detected.nix")
];
{ config, lib, pkgs, modulesPath, ... }:
boot = {
initrd = {
availableKernelModules = [
"xhci_pci"
"ahci"
"nvme"
"usbhid"
"usb_storage"
"sd_mod"
];
kernelModules = [ ];
};
kernelModules = [ "kvm-intel" ];
extraModulePackages = [ ];
};
fileSystems = {
"/" = {
device = "/dev/disk/by-uuid/fd6792b4-d1ec-493c-a686-64dbeaac3371";
{
imports =
[ (modulesPath + "/installer/scan/not-detected.nix")
];
boot.initrd.availableKernelModules = [ "xhci_pci" "ahci" "nvme" "usb_storage" "sd_mod" ];
boot.initrd.kernelModules = [ ];
boot.kernelModules = [ "kvm-intel" ];
boot.extraModulePackages = [ ];
fileSystems."/" =
{ device = "/dev/disk/by-uuid/246d3df7-3578-44b2-8aee-c1ed33581184";
fsType = "ext4";
};
"/boot" = {
device = "/dev/disk/by-uuid/383D-1E8A";
fileSystems."/boot" =
{ device = "/dev/disk/by-uuid/33D0-6524";
fsType = "vfat";
options = [
"fmask=0077"
"dmask=0077"
];
options = [ "fmask=0077" "dmask=0077" ];
};
};
swapDevices = [
{ device = "/dev/disk/by-uuid/33c6277d-eb0a-487d-8be0-829829a8a308"; }
];
swapDevices =
[ { device = "/dev/disk/by-uuid/b74b04db-c7ea-44e7-b3fe-15cd6d0cd851"; }
];
# Enables DHCP on each ethernet and wireless interface. In case of scripted networking
# (the default) this is the recommended approach. When using systemd-networkd it's
# still possible to use this option, but it's recommended to use it in conjunction
# with explicit per-interface declarations with `networking.interfaces.<interface>.useDHCP`.
networking.useDHCP = lib.mkDefault true;
# networking.interfaces.enp3s0.useDHCP = lib.mkDefault true;
# networking.interfaces.eno1.useDHCP = lib.mkDefault true;
# networking.interfaces.wlo1.useDHCP = lib.mkDefault true;
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";

View file

@ -1,57 +0,0 @@
{
pkgs,
inputs,
...
}:
{
imports = [
../../../modules/nixos/options.nix
../../../modules/nixos/programs/command-line.nix
../../../modules/nixos/programs/neovim.nix
../../../modules/nixos/system/nix-settings.nix
inputs.home-manager.darwinModules.home-manager
];
osbmModules = {
programs.neovim.enable = true;
};
home-manager = {
useGlobalPkgs = true;
useUserPackages = true;
verbose = true;
backupFileExtension = "hmbak";
users.osbm = {
imports = [ ../../../modules/home-manager ];
home.stateVersion = "24.11";
};
};
services.tailscale = {
enable = true;
};
programs.fish.enable = true;
# osbmModules.setUsers = false;
users.users.osbm = {
description = "osbm";
shell = pkgs.fish;
home = "/Users/osbm";
};
environment.systemPackages = with pkgs; [
alacritty
# ghostty
kitty
vscode
# anki
# blender
# libreoffice
# ungoogled-chromium
code-cursor
claude-code
# ollama
];
system.stateVersion = 6;
nixpkgs.hostPlatform = "x86_64-darwin";
}

View file

@ -1,112 +0,0 @@
{
pkgs,
inputs,
...
}:
{
user.userName = "osbm";
# Read the changelog before changing this value
system.stateVersion = "24.05";
# Set up nix for flakes
nix.extraOptions = ''
experimental-features = nix-command flakes
'';
# Configure home-manager
home-manager = {
backupFileExtension = "hm-bak";
useGlobalPkgs = true;
config = {
home.stateVersion = "24.05";
# Add your home-manager config here
};
};
build.activation.sshd = ''
if [ ! -e /etc/ssh/ssh_host_ed25519_key ]; then
$VERBOSE_ECHO "Generating host keys..."
$DRY_RUN_CMD ${pkgs.openssh}/bin/ssh-keygen -t ed25519 -a 32 -f "/etc/ssh/ssh_host_ed25519_key" -N ""
fi
'';
environment = {
packages = with pkgs; [
vim # or some other editor, e.g. nano or neovim
procps
inetutils
findutils
utillinux
tzdata
hostname
man
gnugrep
zip
unzip
fish
tmux
nano
ripgrep
git
openssh
just
nh
# agenix tools
inputs.agenix.packages.${pkgs.stdenv.hostPlatform.system}.agenix
age
(pkgs.writeShellScriptBin "lg-rerouting" ''
${pkgs.lazygit}/bin/lazygit --path /storage/emulated/0/Documents/rerouting
'')
(pkgs.writeShellScriptBin "sshd-start" ''
echo "Starting sshd on port 8022"
${pkgs.openssh}/bin/sshd
'')
(pkgs.writeShellScriptBin "wake-ymir" ''
echo waking up ymir
${pkgs.wakeonlan}/bin/wakeonlan 04:7c:16:e6:d9:13
'')
# obsidian tools
# i need a background process that can just keep pulling and pushing changes just like the obsidian git plugin
# (pkgs.writeShellScriptBin "rerouting-sync-start" ''
# cd /storage/emulated/0/Documents/rerouting
# git pull
# git add --all
# git commit -m "Android sync"
# git push
# '')
(pkgs.writeShellScriptBin "rerouting-status" ''
cd /storage/emulated/0/Documents/rerouting
git fetch
git status
'')
(pkgs.writeShellScriptBin "rerouting-pull" ''
cd /storage/emulated/0/Documents/rerouting
git pull
'')
(pkgs.writeShellScriptBin "rerouting-push" ''
cd /storage/emulated/0/Documents/rerouting
git add --all
git commit -m "Android sync"
git push
'')
ani-cli
];
# Backup etc files instead of failing to activate generation if a file already exists in /etc
etcBackupExtension = ".bak";
etc."ssh/sshd_config".text = ''
AcceptEnv LANG LC_*
KbdInteractiveAuthentication no
PasswordAuthentication no
PermitRootLogin no
Port 8022
PrintMotd no
AuthorizedKeysFile /etc/ssh/authorized_keys.d/%u
'';
etc."ssh/authorized_keys.d/osbm".text = ''
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPfnV+qqUCJf92npNW4Jy0hIiepCJFBDJHXBHnUlNX0k
'';
};
}

View file

@ -1,112 +0,0 @@
{
pkgs,
inputs,
...
}:
{
user.userName = "osbm";
# Read the changelog before changing this value
system.stateVersion = "24.05";
# Set up nix for flakes
nix.extraOptions = ''
experimental-features = nix-command flakes
'';
# Configure home-manager
home-manager = {
backupFileExtension = "hm-bak";
useGlobalPkgs = true;
config = {
home.stateVersion = "24.05";
# Add your home-manager config here
};
};
build.activation.sshd = ''
if [ ! -e /etc/ssh/ssh_host_ed25519_key ]; then
$VERBOSE_ECHO "Generating host keys..."
$DRY_RUN_CMD ${pkgs.openssh}/bin/ssh-keygen -t ed25519 -a 32 -f "/etc/ssh/ssh_host_ed25519_key" -N ""
fi
'';
environment = {
packages = with pkgs; [
vim # or some other editor, e.g. nano or neovim
procps
inetutils
findutils
utillinux
tzdata
hostname
man
gnugrep
zip
unzip
fish
tmux
nano
ripgrep
git
openssh
just
nh
# agenix tools
inputs.agenix.packages.${pkgs.stdenv.hostPlatform.system}.agenix
age
(pkgs.writeShellScriptBin "lg-rerouting" ''
${pkgs.lazygit}/bin/lazygit --path /storage/emulated/0/Documents/rerouting
'')
(pkgs.writeShellScriptBin "sshd-start" ''
echo "Starting sshd on port 8022"
${pkgs.openssh}/bin/sshd
'')
(pkgs.writeShellScriptBin "wake-ymir" ''
echo waking up ymir
${pkgs.wakeonlan}/bin/wakeonlan 04:7c:16:e6:d9:13
'')
# obsidian tools
# i need a background process that can just keep pulling and pushing changes just like the obsidian git plugin
# (pkgs.writeShellScriptBin "rerouting-sync-start" ''
# cd /storage/emulated/0/Documents/rerouting
# git pull
# git add --all
# git commit -m "Android sync"
# git push
# '')
(pkgs.writeShellScriptBin "rerouting-status" ''
cd /storage/emulated/0/Documents/rerouting
git fetch
git status
'')
(pkgs.writeShellScriptBin "rerouting-pull" ''
cd /storage/emulated/0/Documents/rerouting
git pull
'')
(pkgs.writeShellScriptBin "rerouting-push" ''
cd /storage/emulated/0/Documents/rerouting
git add --all
git commit -m "Android sync"
git push
'')
ani-cli
];
# Backup etc files instead of failing to activate generation if a file already exists in /etc
etcBackupExtension = ".bak";
etc."ssh/sshd_config".text = ''
AcceptEnv LANG LC_*
KbdInteractiveAuthentication no
PasswordAuthentication no
PermitRootLogin no
Port 8022
PrintMotd no
AuthorizedKeysFile /etc/ssh/authorized_keys.d/%u
'';
etc."ssh/authorized_keys.d/osbm".text = ''
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPfnV+qqUCJf92npNW4Jy0hIiepCJFBDJHXBHnUlNX0k
'';
};
}

View file

@ -1,120 +0,0 @@
{ lib, ... }:
{
imports = [
../../../modules/nixos
];
osbmModules = {
services = {
glance.enable = true;
# anubis.enable = true;
mailserver.enable = true;
nginx.enable = true;
forgejo.enable = true;
vaultwarden.enable = true;
immich.enable = true;
actual.enable = true;
# seafile.enable = true;
# Backup server - exposes data for pull-based backups
backup-server = {
enable = true;
zfsSnapshots = {
enable = true;
# Keep snapshots for point-in-time recovery
frequent = 4; # 4 x 15min = 1 hour of frequent snapshots
hourly = 24; # 24 hours
daily = 7; # 1 week
weekly = 4; # 1 month
monthly = 12; # 1 year
};
};
};
hardware = {
sound.enable = false;
hibernation.enable = false;
disko = {
enable = true;
fileSystem = "zfs";
initrd-ssh = {
enable = true;
authorizedKeys = [
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPfnV+qqUCJf92npNW4Jy0hIiepCJFBDJHXBHnUlNX0k"
];
ethernetDrivers = [
"virtio_pci" # QEMU support
"virtio_net"
"virtio_pci"
"virtio_blk"
"virtio_balloon"
"virtio_console"
"virtio_gpu"
];
};
zfs = {
enable = true;
hostID = "0f7de22e";
root = {
useTmpfs = false; # Use ZFS root, not tmpfs
encrypt = true;
disk1 = "vda";
impermanenceRoot = true; # Wipe root on boot with ZFS snapshots
};
};
};
};
};
system.stateVersion = "25.11";
networking.hostName = "apollo";
# Enable zram swap
zramSwap.enable = true;
users.mutableUsers = false;
# Disable sudo lecture message
security.sudo.extraConfig = ''
Defaults lecture = never
'';
# server is in germany
time.timeZone = "Europe/Berlin"; # or "Europe/Amsterdam"
# Network configuration
networking = {
useDHCP = false;
interfaces.eth0 = {
useDHCP = false;
ipv4.addresses = [
{
address = "152.53.152.129";
prefixLength = 22;
}
];
ipv6.addresses = [
{
address = "2a00:11c0:47:3b2a::1";
prefixLength = 64;
}
];
};
defaultGateway = "152.53.152.1";
defaultGateway6 = {
address = "fe80::1";
interface = "eth0";
};
nameservers = [
"1.1.1.1"
"8.8.8.8"
]; # Cloudflare and Google DNS
};
# Override initrd kernel params for static IP
boot.kernelParams = [ "ip=152.53.152.129::152.53.152.1:255.255.252.0::eth0:none" ];
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
}

View file

@ -1,53 +0,0 @@
{ inputs, lib, ... }:
{
imports = [
./hardware-configuration.nix
../../../modules/nixos
inputs.jovian-nixos.nixosModules.default
];
osbmModules = {
desktopEnvironment = {
plasma.enable = true;
};
familyUser.enable = true;
programs = {
steam.enable = true;
};
hardware = {
# hibernation.enable = false;
sound.enable = true;
};
i18n.enable = true;
};
jovian = {
devices.steamdeck = {
enable = true;
autoUpdate = true;
};
steam = {
enable = true;
autoStart = true;
user = "osbm";
desktopSession = "plasma";
};
decky-loader.enable = true;
};
# Disable SDDM since Jovian manages its own display manager
services.displayManager.sddm.enable = lib.mkForce false;
networking = {
hostName = "ares";
firewall.allowedTCPPorts = [
8889
8000
];
networkmanager.enable = true;
};
system.stateVersion = "26.05"; # changing this is a great taboo of the nixos world
}

View file

@ -1,48 +0,0 @@
# Do not modify this file! It was generated by nixos-generate-config
# and may be overwritten by future invocations. Please make changes
# to /etc/nixos/configuration.nix instead.
{
config,
lib,
modulesPath,
...
}:
{
imports = [
(modulesPath + "/installer/scan/not-detected.nix")
];
boot.initrd.availableKernelModules = [
"nvme"
"xhci_pci"
"usb_storage"
"usbhid"
"sd_mod"
"sdhci_pci"
];
boot.initrd.kernelModules = [ ];
boot.kernelModules = [ "kvm-amd" ];
boot.extraModulePackages = [ ];
fileSystems."/" = {
device = "/dev/disk/by-uuid/ec49c05b-3d2b-4d9f-8634-ab4f3597b843";
fsType = "ext4";
};
fileSystems."/boot" = {
device = "/dev/disk/by-uuid/E3FC-D8F9";
fsType = "vfat";
options = [
"fmask=0077"
"dmask=0077"
];
};
swapDevices = [
{ device = "/dev/disk/by-uuid/f4477674-7104-43d7-b28c-6a8a8a86e8db"; }
];
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
hardware.cpu.amd.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
}

View file

@ -1,61 +0,0 @@
{
inputs,
pkgs,
lib,
...
}:
{
imports = [
(import "${inputs.mobile-nixos}/lib/configuration.nix" { device = "oneplus-enchilada"; })
../../../modules/nixos
];
osbmModules = {
desktopEnvironment.gnome.enable = true;
hardware.systemd-boot.enable = false; # Mobile devices use different bootloader
programs.graphical.enable = false;
services = {
# Backup client - pulls vaultwarden backup from apollo
backup-client = {
enable = true;
backups = {
apollo-vaultwarden = {
remoteHost = "apollo";
localPath = "/var/backups/apollo-vaultwarden";
services = [ "vaultwarden" ];
};
};
};
};
};
# mobile-nixos needs aliases (uses nettools instead of net-tools)
nixpkgs = {
config = {
allowAliases = true;
allowUnfreePredicate =
pkg:
builtins.elem (lib.getName pkg) [
"oneplus-sdm845-firmware-zstd"
"oneplus-sdm845-firmware"
];
};
system = "aarch64-linux";
};
# Minimal essential packages
environment.systemPackages = with pkgs; [
git
vim
wget
curl
lazygit
asciiquarium
neovim
kitty
];
system.stateVersion = "25.11";
}

View file

@ -1,47 +0,0 @@
{
inputs,
...
}:
{
imports = [
./sd-image.nix
"${inputs.nixpkgs}/nixos/modules/installer/sd-card/sd-image-aarch64.nix"
./hardware-configuration.nix
../../../modules/nixos
];
osbmModules = {
fonts.enable = false;
services.tailscale.enable = true;
hardware.systemd-boot.enable = false; # SD card uses extlinux
};
system.stateVersion = "25.05";
networking.hostName = "harmonica";
networking = {
interfaces."wlan0".useDHCP = true;
wireless = {
enable = true;
interfaces = [ "wlan0" ];
networks = {
"House_Bayram" = {
psk = "PASSWORD";
};
"it_hurts_when_IP" = {
psk = "PASSWORD";
};
};
};
};
# NTP time sync.
services.timesyncd.enable = true;
security.sudo = {
enable = true;
wheelNeedsPassword = false;
};
services.getty.autologinUser = "osbm";
}

View file

@ -1,85 +0,0 @@
{
pkgs,
lib,
...
}:
{
# Some packages (ahci fail... this bypasses that) https://discourse.nixos.org/t/does-pkgs-linuxpackages-rpi3-build-all-required-kernel-modules/42509
nixpkgs.overlays = [
(_final: super: {
makeModulesClosure = x: super.makeModulesClosure (x // { allowMissing = true; });
})
];
zramSwap = {
enable = true;
algorithm = "zstd";
};
image.fileName = "zero2.img";
sdImage = {
# bzip2 compression takes loads of time with emulation, skip it. Enable this if you're low on space.
compressImage = false;
extraFirmwareConfig = {
# Give up VRAM for more Free System Memory
# - Disable camera which automatically reserves 128MB VRAM
start_x = 0;
# - Reduce allocation of VRAM to 16MB minimum for non-rotated (32MB for rotated)
gpu_mem = 16;
# Configure display to 800x600 so it fits on most screens
# * See: https://elinux.org/RPi_Configuration
hdmi_group = 2;
hdmi_mode = 8;
};
};
hardware = {
enableRedistributableFirmware = lib.mkForce false;
firmware = [ pkgs.raspberrypiWirelessFirmware ]; # Keep this to make sure wifi works
i2c.enable = true;
deviceTree.filter = "bcm2837-rpi-zero*.dtb";
deviceTree.overlays = [
{
name = "enable-i2c";
dtsText = ''
/dts-v1/;
/plugin/;
/ {
compatible = "brcm,bcm2837";
fragment@0 {
target = <&i2c1>;
__overlay__ {
status = "okay";
};
};
};
'';
}
];
};
boot = {
kernelPackages = pkgs.linuxKernel.packages.linux_rpi3;
initrd.availableKernelModules = [
"xhci_pci"
"usbhid"
"usb_storage"
];
loader = {
grub.enable = false;
generic-extlinux-compatible.enable = true;
};
extraModprobeConfig = ''
options brcmfmac roamoff=1 feature_disable=0x82000
'';
# Avoids warning: mdadm: Neither MAILADDR nor PROGRAM has been set. This will cause the `mdmon` service to crash.
# See: https://github.com/NixOS/nixpkgs/issues/254807
swraid.enable = lib.mkForce false;
};
nixpkgs.hostPlatform = "aarch64-linux";
}

View file

@ -1,39 +0,0 @@
# This module extends the official sd-image.nix with the following:
# - ability to add options to the config.txt firmware
{
config,
lib,
...
}:
{
options.sdImage = with lib; {
extraFirmwareConfig = mkOption {
type = types.attrs;
default = { };
description = lib.mdDoc ''
Extra configuration to be added to config.txt.
'';
};
};
config = {
sdImage.populateFirmwareCommands =
lib.mkIf ((lib.length (lib.attrValues config.sdImage.extraFirmwareConfig)) > 0)
(
let
# Convert the set into a string of lines of "key=value" pairs.
keyValueMap = name: value: name + "=" + toString value;
keyValueList = lib.mapAttrsToList keyValueMap config.sdImage.extraFirmwareConfig;
extraFirmwareConfigString = lib.concatStringsSep "\n" keyValueList;
in
lib.mkAfter ''
config=firmware/config.txt
# The initial file has just been created without write permissions. Add them to be able to append the file.
chmod u+w $config
echo "\n# Extra configuration" >> $config
echo "${extraFirmwareConfigString}" >> $config
chmod u-w $config
''
);
};
}

View file

@ -1,23 +0,0 @@
{
imports = [
./hardware-configuration.nix
../../../modules/nixos
];
osbmModules = {
hardware.systemd-boot.enable = false; # Uses extlinux bootloader
};
system.stateVersion = "25.05";
networking.hostName = "harmonica";
# NTP time sync.
services.timesyncd.enable = true;
security.sudo = {
enable = true;
wheelNeedsPassword = false;
};
# services.getty.autologinUser = "osbm";
}

View file

@ -1,71 +0,0 @@
{
pkgs,
lib,
...
}:
{
# Some packages (ahci fail... this bypasses that) https://discourse.nixos.org/t/does-pkgs-linuxpackages-rpi3-build-all-required-kernel-modules/42509
nixpkgs.overlays = [
(_final: super: {
makeModulesClosure = x: super.makeModulesClosure (x // { allowMissing = true; });
})
];
zramSwap = {
enable = true;
algorithm = "zstd";
};
fileSystems."/" = {
device = "/dev/disk/by-uuid/44444444-4444-4444-8888-888888888888";
fsType = "ext4";
};
hardware = {
enableRedistributableFirmware = lib.mkForce false;
firmware = [ pkgs.raspberrypiWirelessFirmware ]; # Keep this to make sure wifi works
i2c.enable = true;
deviceTree.filter = "bcm2837-rpi-zero*.dtb";
deviceTree.overlays = [
{
name = "enable-i2c";
dtsText = ''
/dts-v1/;
/plugin/;
/ {
compatible = "brcm,bcm2837";
fragment@0 {
target = <&i2c1>;
__overlay__ {
status = "okay";
};
};
};
'';
}
];
};
boot = {
kernelPackages = pkgs.linuxKernel.packages.linux_rpi3;
initrd.availableKernelModules = [
"xhci_pci"
"usbhid"
"usb_storage"
];
loader = {
grub.enable = false;
generic-extlinux-compatible.enable = true;
};
extraModprobeConfig = ''
options brcmfmac roamoff=1 feature_disable=0x82000
'';
# Avoids warning: mdadm: Neither MAILADDR nor PROGRAM has been set. This will cause the `mdmon` service to crash.
# See: https://github.com/NixOS/nixpkgs/issues/254807
swraid.enable = lib.mkForce false;
};
nixpkgs.hostPlatform = "aarch64-linux";
}

View file

@ -1,39 +0,0 @@
{
lib,
pkgs,
inputs,
...
}:
{
imports = [
"${inputs.nixpkgs}/nixos/modules/installer/cd-dvd/installation-cd-graphical-calamares-plasma6.nix"
];
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
nix.settings.experimental-features = [
"nix-command"
"flakes"
];
nixpkgs.config.allowUnfree = true;
# Set environment variable for allowing non-free packages
environment.sessionVariables = {
NIXPKGS_ALLOW_UNFREE = "1";
};
environment.systemPackages = with pkgs; [
# Do not forget to add an editor to edit configuration.nix! The Nano editor is also installed by default.
git
curl
parted
nano
comma
just
age
neovim
wget
];
services.openssh.enable = true;
}

View file

@ -1,57 +0,0 @@
{
pkgs,
inputs,
...
}:
{
imports = [
inputs.raspberry-pi-nix.nixosModules.raspberry-pi
inputs.raspberry-pi-nix.nixosModules.sd-image
];
# bcm2711 for rpi 3, 3+, 4, zero 2 w
# bcm2712 for rpi 5
# See the docs at:
# https://www.raspberrypi.com/documentation/computers/linux_kernel.html#native-build-configuration
raspberry-pi-nix.board = "bcm2712";
time.timeZone = "America/Chicago";
users.users.root = {
initialPassword = "root";
};
# Define a user account. Don't forget to set a password with passwd.
users.users.osbm = {
isNormalUser = true;
extraGroups = [ "wheel" ]; # Enable sudo for the user.
initialPassword = "changeme";
};
networking = {
hostName = "pochita";
};
environment.systemPackages = with pkgs; [
neovim
git-lfs
git
wakeonlan
htop
unzip
zip
wget
];
nix.settings.experimental-features = [
"nix-command"
"flakes"
];
nix.settings.trusted-users = [
"root"
"osbm"
];
nixpkgs.hostPlatform = "aarch64-linux";
services.openssh = {
enable = true;
};
system.stateVersion = "25.05";
}

View file

@ -1,57 +0,0 @@
{
pkgs,
inputs,
...
}:
{
imports = [
./hardware-configuration.nix
../../../modules/nixos
inputs.raspberry-pi-nix.nixosModules.raspberry-pi
inputs.nixos-hardware.nixosModules.raspberry-pi-5
];
osbmModules = {
hardware.systemd-boot.enable = false; # Raspberry Pi uses init-script bootloader
familyUser.enable = true;
services = {
wanikani-bypass-lessons.enable = true;
wanikani-fetch-data.enable = true;
wanikani-stats.enable = true;
# Backup client - pulls full backup from apollo
backup-client = {
enable = true;
backups = {
apollo-full = {
remoteHost = "apollo"; # Tailscale hostname
localPath = "/var/backups/apollo";
fullBackup = true;
};
};
};
};
desktopEnvironment.plasma.enable = true;
programs.graphical.enable = false;
};
zramSwap.enable = true;
networking.hostName = "pochita";
networking.networkmanager.enable = true;
# log of shame: osbm blamed nix when he wrote "hostname" instead of "hostName"
environment.systemPackages = [
pkgs.raspberrypi-eeprom
];
# The board and wanted kernel version
raspberry-pi-nix = {
board = "bcm2712";
libcamera-overlay.enable = false;
#kernel-version = "v6_10_12";
};
system.stateVersion = "25.05";
}

View file

@ -1,47 +0,0 @@
{
lib,
modulesPath,
...
}:
{
imports = [
(modulesPath + "/installer/scan/not-detected.nix")
];
boot = {
initrd = {
availableKernelModules = [ ];
kernelModules = [ ];
};
kernelModules = [ ];
extraModulePackages = [ ];
};
fileSystems = {
"/" = {
device = "/dev/disk/by-uuid/44444444-4444-4444-8888-888888888888";
fsType = "ext4";
};
"/boot/firmware" = {
device = "/dev/disk/by-uuid/2178-694E";
fsType = "vfat";
options = [
"fmask=0022"
"dmask=0022"
];
};
};
swapDevices = [ ];
# Enables DHCP on each ethernet and wireless interface. In case of scripted networking
# (the default) this is the recommended approach. When using systemd-networkd it's
# still possible to use this option, but it's recommended to use it in conjunction
# with explicit per-interface declarations with `networking.interfaces.<interface>.useDHCP`.
networking.useDHCP = lib.mkDefault true;
# networking.interfaces.end0.useDHCP = lib.mkDefault true;
# networking.interfaces.wlan0.useDHCP = lib.mkDefault true;
nixpkgs.hostPlatform = lib.mkDefault "aarch64-linux";
}

View file

@ -1,35 +0,0 @@
{
imports = [
./hardware-configuration.nix
../../../modules/nixos
];
osbmModules = {
desktopEnvironment.plasma.enable = true;
familyUser.enable = true;
emulation.aarch64.enable = true;
hardware.sound.enable = true;
programs.steam.enable = true;
services = {
# Backup client - pulls vaultwarden backup from apollo
backup-client = {
enable = true;
backups = {
apollo-vaultwarden = {
remoteHost = "apollo";
localPath = "/var/backups/apollo-vaultwarden";
services = [ "vaultwarden" ];
};
};
};
};
};
networking.hostName = "tartarus";
# Enable networking
networking.networkmanager.enable = true;
system.stateVersion = "24.05"; # lalalalala
}

View file

@ -1,53 +0,0 @@
{
config,
lib,
modulesPath,
...
}:
{
imports = [
(modulesPath + "/installer/scan/not-detected.nix")
];
boot = {
initrd.availableKernelModules = [
"xhci_pci"
"ahci"
"nvme"
"usb_storage"
"sd_mod"
];
initrd.kernelModules = [ ];
kernelModules = [ "kvm-intel" ];
extraModulePackages = [ ];
};
fileSystems."/" = {
device = "/dev/disk/by-uuid/246d3df7-3578-44b2-8aee-c1ed33581184";
fsType = "ext4";
};
fileSystems."/boot" = {
device = "/dev/disk/by-uuid/33D0-6524";
fsType = "vfat";
options = [
"fmask=0077"
"dmask=0077"
];
};
swapDevices = [
{ device = "/dev/disk/by-uuid/b74b04db-c7ea-44e7-b3fe-15cd6d0cd851"; }
];
# Enables DHCP on each ethernet and wireless interface. In case of scripted networking
# (the default) this is the recommended approach. When using systemd-networkd it's
# still possible to use this option, but it's recommended to use it in conjunction
# with explicit per-interface declarations with `networking.interfaces.<interface>.useDHCP`.
networking.useDHCP = lib.mkDefault true;
# networking.interfaces.eno1.useDHCP = lib.mkDefault true;
# networking.interfaces.wlo1.useDHCP = lib.mkDefault true;
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
}

View file

@ -1,28 +0,0 @@
{
imports = [
./hardware-configuration.nix
../../../modules/nixos
];
osbmModules = {
services = {
hydra.enable = true;
atticd.enable = true;
cloudflared.enable = true;
# Backup client - pulls full backup from apollo
backup-client = {
enable = true;
backups = {
apollo-full = {
remoteHost = "apollo"; # Tailscale hostname
localPath = "/var/backups/apollo";
fullBackup = true;
};
};
};
};
};
networking.hostName = "wallfacer";
system.stateVersion = "25.05";
}

View file

@ -1,68 +0,0 @@
{
config,
lib,
modulesPath,
...
}:
{
imports = [
(modulesPath + "/installer/scan/not-detected.nix")
];
boot = {
initrd.availableKernelModules = [
"ahci"
"ehci_pci"
"megaraid_sas"
"nvme"
"usbhid"
"usb_storage"
"sd_mod"
];
initrd.kernelModules = [ ];
kernelModules = [ "kvm-intel" ];
extraModulePackages = [ ];
};
fileSystems = {
"/" = {
device = "/dev/disk/by-uuid/8270dba5-6d89-438a-90bd-d9f29b20cb5b";
fsType = "ext4";
};
"/boot" = {
device = "/dev/disk/by-uuid/A1EB-43F8";
fsType = "vfat";
options = [
"fmask=0077"
"dmask=0077"
];
};
"/data/atreus" = {
device = "/dev/disk/by-uuid/1840c2af-fdb2-48b6-8555-2cecd1afb106";
fsType = "ext4";
};
# 500 gb nvme pcie
"/data/kasio" = {
device = "/dev/disk/by-uuid/af74aa48-8513-4e31-a4b7-9fdd138e0002";
fsType = "ext4";
};
};
swapDevices = [
{ device = "/dev/disk/by-uuid/534ea30c-2664-498b-915f-41b037eba01b"; }
];
# Enables DHCP on each ethernet and wireless interface. In case of scripted networking
# (the default) this is the recommended approach. When using systemd-networkd it's
# still possible to use this option, but it's recommended to use it in conjunction
# with explicit per-interface declarations with `networking.interfaces.<interface>.useDHCP`.
networking.useDHCP = lib.mkDefault true;
# networking.interfaces.eno1np0.useDHCP = lib.mkDefault true;
# networking.interfaces.eno2np1.useDHCP = lib.mkDefault true;
# networking.interfaces.eno3.useDHCP = lib.mkDefault true;
# networking.interfaces.eno4.useDHCP = lib.mkDefault true;
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
}

View file

@ -1,58 +0,0 @@
{
imports = [
./hardware-configuration.nix
../../../modules/nixos
];
osbmModules = {
desktopEnvironment = {
plasma.enable = true;
niri.enable = true;
# hyprland.enable = true; # im insane so i enable every DE
};
familyUser.enable = true;
programs = {
adbFastboot.enable = true;
steam.enable = true;
};
emulation.aarch64.enable = true;
virtualisation.docker.enable = true;
hardware = {
hibernation.enable = false;
wakeOnLan.enable = true;
sound.enable = true;
nvidia.enable = true;
};
services = {
ollama.enable = true;
# Backup client - pulls vaultwarden backup from apollo
backup-client = {
enable = true;
backups = {
apollo-vaultwarden = {
remoteHost = "apollo";
localPath = "/var/backups/apollo-vaultwarden";
services = [ "vaultwarden" ];
};
};
};
};
i18n.enable = true;
};
networking = {
hostName = "ymir";
firewall.allowedTCPPorts = [
8889
8000
];
networkmanager.enable = true;
};
virtualisation.waydroid.enable = true;
system.stateVersion = "25.05"; # changing this is a great taboo of the nixos world
}

View file

@ -1,78 +0,0 @@
_default:
@just --list --unsorted
check-git:
# git must be clean
test -z "$(git status --porcelain)"
[linux]
build *args: check-git
sudo nixos-rebuild build --flake . {{args}} |& nom
nvd diff /run/current-system ./result
[linux]
switch *args: check-git remove-hm-backup-files
#!/usr/bin/env sh
if [[ "$(hostname)" == "localhost" ]]; then
nix-on-droid switch --flake . {{args}}
else
nh os switch . {{args}} --accept-flake-config
fi
[macos]
switch *args: check-git
nh darwin switch . -- --accept-flake-config {{args}}
remove-hm-backup-files:
#!/usr/bin/env sh
if [ -f ~/.gtkrc-2.0.hmbak ]; then
rm ~/.gtkrc-2.0.hmbak
fi
test:
nh os test .
update:
nix flake update
check:
nix flake check
repl:
nix repl -f flake:nixpkgs
collect-garbage:
sudo nix-env --profile /nix/var/nix/profiles/system --delete-generations old
# home-manager expire-generations now
sudo nix-collect-garbage --delete-older-than 3d
list-generations:
nixos-rebuild list-generations
build-sd-image-harmonica: check-git
nom build -L .#nixosConfigurations.harmonica-sd.config.system.build.sdImage
build-sd-image-pochita: check-git
nom build -L .#nixosConfigurations.pochita-sd.config.system.build.sdImage
build-iso: check-git
nom build -L .#nixosConfigurations.iso.config.system.build.isoImage
flash-sd-image-harmonica:
# raise error because this command should be edited before running
false
nom build -L .#nixosConfigurations.harmonica-sd.config.system.build.sdImage
sudo dd if=result/sd-image/nixos-image-sd-card-25.05.20250224.0196c01-aarch64-linux.img of=/dev/sda bs=4M status=progress conv=fsync
setup-apollo-nixos:
nano /tmp/secret.key
sudo nix --experimental-features "nix-command flakes" run github:nix-community/disko -- --mode destroy,format,mount --flake github:osbm/flake#apollo
sudo mkdir -p /mnt/etc/ssh
sudo ssh-keygen -t ed25519 -N "" -f /mnt/etc/ssh/initrd
sudo nixos-install --flake github:osbm/flake#apollo --root /mnt --no-root-passwd
sweep *args:
nix run github:jzbor/nix-sweep -- {{args}}

View file

@ -1,9 +0,0 @@
{ lib, ... }:
{
importList = lib.mapAttrsToList (name: _path: ./. + "/${name}") (
lib.filterAttrs (
filename: kind: filename != "default.nix" && (kind == "regular" || kind == "directory")
) (builtins.readDir ./.)
);
}

View file

@ -1,7 +0,0 @@
# Main module entry point
# Import the new NixOS module system
{
imports = [
./nixos
];
}

View file

@ -1,35 +0,0 @@
{ lib, pkgs, ... }:
{
# Import all home-manager modules
imports = [
./programs
# no home manager services yet
# ./services
];
# Basic home-manager configuration
home.sessionVariables = {
EDITOR = lib.mkDefault "nvim";
};
home.packages = [ pkgs.forgejo-cli ];
home.shellAliases = {
c = "code .";
l = "eza --all --long --git --icons --group --sort size --header --group-directories-first";
ll = "eza --all --long --git --icons --group --sort name --header --group-directories-first";
free = "free -h";
df = "df -h";
du = "du -h";
lg = "lazygit";
onefetch = "onefetch -T prose -T programming -T data";
fj = "${lib.getExe pkgs.forgejo-cli} --host git.osbm.dev";
};
# Don't set stateVersion here - let it be set by the system configuration
# home.stateVersion should be set in the system's home-manager configuration
# Enable basic programs that most users want
programs.home-manager.enable = true;
}

View file

@ -1,40 +0,0 @@
{
lib,
pkgs,
nixosConfig,
...
}:
{
config = lib.mkMerge [
(lib.mkIf (nixosConfig != null && !nixosConfig.osbmModules.desktopEnvironment.none) {
# Set enableAlacritty to true by default when there's a desktop environment
programs.alacritty.enable = lib.mkDefault true;
})
{
programs.alacritty = {
settings = {
font = {
size = 14.0;
normal.family = "Cascadia Code";
};
terminal.shell = {
args = [
"new-session"
"-A"
"-s"
"general"
];
program = lib.getExe pkgs.tmux;
};
window = {
decorations = "None";
opacity = 1;
startup_mode = "Maximized";
};
env.TERM = "xterm-256color";
};
};
}
];
}

View file

@ -1,10 +0,0 @@
{
programs.bash = {
enable = true;
bashrcExtra = ''
if [ "dumb" == "$TERM" ] ; then
export TERM=xterm-256color
fi
'';
};
}

View file

@ -1,20 +0,0 @@
{
imports = [
./alacritty.nix
./bash.nix
./direnv.nix
./firefox.nix
./fish.nix
./ghostty.nix
./git.nix
./mpv.nix
./ssh.nix
./starship.nix
./swww.nix
./tlrc.nix
./tmux.nix
./waybar.nix
./wezterm.nix
./zoxide.nix
];
}

View file

@ -1,6 +0,0 @@
{
programs.direnv = {
enable = true;
nix-direnv.enable = true;
};
}

View file

@ -1,115 +0,0 @@
{
lib,
nixosConfig ? null, # Receive the NixOS config
pkgs,
...
}:
{
config = lib.mkMerge [
# Auto-enable Firefox if system has a desktop environment
(lib.mkIf (nixosConfig != null && !nixosConfig.osbmModules.desktopEnvironment.none) {
# Set enableFirefox to true by default when there's a desktop environment
programs.firefox.enable = lib.mkDefault true;
})
# Firefox configuration
{
programs.firefox = {
# TODO Firefox fails as the closure contains a reference to stdenv.cc
# Relax this assertion until the underlying issue is fixed
# https://github.com/NixOS/nixpkgs/pull/457424
package = pkgs.firefox.overrideAttrs { disallowedRequisites = [ ]; };
languagePacks = [
"ja"
"tr"
"en-US"
];
policies = {
DisableTelemetry = true;
DisableFirefoxStudies = true;
EnableTrackingProtection = {
Value = true;
Locked = true;
Cryptomining = true;
Fingerprinting = true;
};
# DisablePocket = true;
DisableFirefoxAccounts = true;
DisableAccounts = true;
DisableFirefoxScreenshots = true;
StartPage = "previous-session";
# OverrideFirstRunPage = "";
# OverridePostUpdatePage = "";
# DontCheckDefaultBrowser = true;
DisplayBookmarksToolbar = "always"; # alternatives: "never" or "newtab"
# DisplayMenuBar = "default-off"; # alternatives: "always", "never" or "default-on"
# SearchBar = "unified"; # alternat
ExtensionSettings =
let
extension = shortId: uuid: {
name = uuid;
value = {
install_url = "https://addons.mozilla.org/en-US/firefox/downloads/latest/${shortId}/latest.xpi";
installation_mode = "normal_installed"; # i dont want to get the packages from a non-open source source
};
};
in
builtins.listToAttrs [
(extension "tree-style-tab" "treestyletab@piro.sakura.ne.jp")
(extension "ublock-origin" "uBlock0@raymondhill.net")
(extension "bitwarden-password-manager" "{446900e4-71c2-419f-a6a7-df9c091e268b}")
(extension "motivation-new-tab" "")
(extension "return-youtube-dislikes" "{762f9885-5a13-4abd-9c77-433dcd38b8fd}")
(extension "violentmonkey" "{aecec67f-0d10-4fa7-b7c7-609a2db280cf}")
(extension "vimium-ff" "{d7742d87-e61d-4b78-b8a1-b469842139fa}")
(extension "i-dont-care-about-cookies" "jid1-KKzOGWgsW3Ao4Q@jetpack")
# (extension "tabliss" "extension@tabliss.io")
# (extension "umatrix" "uMatrix@raymondhill.net")
# (extension "libredirect" "7esoorv3@alefvanoon.anonaddy.me")
(extension "clearurls" "{74145f27-f039-47ce-a470-a662b129930a}")
(extension "youtube-shorts-block" "")
];
# To add additional extensions, find it on addons.mozilla.org, find
# the short ID in the url (like https://addons.mozilla.org/en-US/firefox/addon/!SHORT_ID!/)
# Then, download the XPI by filling it in to the install_url template, unzip it,
# run `jq .browser_specific_settings.gecko.id manifest.json` or
# `jq .applications.gecko.id manifest.json` to get the UUID
};
profiles.default = {
id = 0;
name = "osbm";
userChrome = ''
#tabbrowser-tabs {
visibility: collapse;
}
'';
settings = {
# "Open previous windows and tabs"
"browser.startup.page" = 3;
"browser.contentblocking.category" = true;
"extensions.pocket.enabled" = false;
"extensions.screenshots.disabled" = true;
"browser.topsites.contile.enabled" = false;
"browser.formfill.enable" = false;
"browser.search.suggest.enabled" = false;
"browser.search.suggest.enabled.private" = false;
"browser.urlbar.suggest.searches" = false;
"browser.urlbar.showSearchSuggestionsFirst" = false;
"browser.newtabpage.activity-stream.feeds.section.topstories" = false;
"browser.newtabpage.activity-stream.feeds.snippets" = false;
"browser.newtabpage.activity-stream.section.highlights.includePocket" = false;
"browser.newtabpage.activity-stream.section.highlights.includeBookmarks" = false;
"browser.newtabpage.activity-stream.section.highlights.includeDownloads" = false;
"browser.newtabpage.activity-stream.section.highlights.includeVisited" = false;
"browser.newtabpage.activity-stream.showSponsored" = false;
"browser.newtabpage.activity-stream.system.showSponsored" = false;
"browser.newtabpage.activity-stream.showSponsoredTopSites" = false;
"toolkit.legacyUserProfileCustomizations.stylesheets" = true;
"ui.key.menuAccessKeyFocuses" = false;
};
};
};
}
];
}

View file

@ -1,15 +0,0 @@
{
programs.fish = {
enable = true;
interactiveShellInit = ''
set -g fish_greeting
'';
functions = {
gitu = ''
git add --all
git commit -m "$argv"
git push
'';
};
};
}

View file

@ -1,9 +0,0 @@
{ lib, nixosConfig, ... }:
{
config = lib.mkMerge [
(lib.mkIf (nixosConfig != null && !nixosConfig.osbmModules.desktopEnvironment.none) {
# Set enableGhostty to true by default when there's a desktop environment
programs.ghostty.enable = lib.mkDefault true;
})
];
}

View file

@ -1,58 +0,0 @@
{ pkgs, ... }:
{
programs.git = {
enable = true;
package = pkgs.gitFull.override {
osxkeychainSupport = false;
};
signing = {
format = "openpgp";
};
ignores = [
"*.pyc" # python
"*.swp" # vim
"__pycache__" # python
".DS_Store" # macOS
"result" # nix
"node_modules" # node
];
settings = {
user = {
email = "osbm@osbm.dev";
name = "osbm";
};
credential = {
helper = "store";
};
core = {
editor = "vim";
pager = "cat";
};
diff = {
wsErrorHighlight = "all";
};
init = {
defaultBranch = "main";
};
http = {
postBuffer = 1048576000;
};
https = {
postBuffer = 1048576000;
};
push = {
autoSetupRemote = true;
};
filter.lfs = {
clean = "git-lfs clean -- %f";
smudge = "git-lfs smudge -- %f";
process = "git-lfs filter-process";
required = true;
};
signing = {
signByDefault = true;
key = "3A264839184185CF";
};
};
};
}

View file

@ -1,23 +0,0 @@
{ lib, nixosConfig, ... }:
{
config = lib.mkMerge [
(lib.mkIf (nixosConfig != null && !nixosConfig.osbmModules.desktopEnvironment.none) {
programs.mpv.enable = lib.mkDefault true;
})
{
programs.mpv = {
config = {
hwdec = "auto";
vo = "gpu";
};
};
}
# Raspberry Pi 5 specific: Use OpenGL to avoid Vulkan memory issues
(lib.mkIf (nixosConfig != null && nixosConfig.networking.hostName == "pochita") {
programs.mpv.config.gpu-api = "opengl";
})
];
}

View file

@ -1,63 +0,0 @@
let
# define a block that just takes a hostname and returns attrset to not repeat the same fields
sshBlock = hostname: {
inherit hostname;
user = "osbm";
identityFile = "~/.ssh/id_ed25519";
extraOptions = {
# [ERROR] - (starship::print): Under a 'dumb' terminal (TERM=dumb).
"RemoteCommand" = "fish";
"RequestTTY" = "force";
};
hashKnownHosts = true;
compression = true;
};
# sshBlockDroid is the same as sshBlock but with 8090 as the port
sshBlockDroid = hostname: {
inherit hostname;
user = "osbm";
identityFile = "~/.ssh/id_ed25519";
port = 8022;
hashKnownHosts = true;
compression = true;
# fish not found error ???
};
in
{
programs.ssh = {
enable = true;
enableDefaultConfig = false;
matchBlocks = {
ymir = sshBlock "192.168.0.2";
ymir-ts = sshBlock "ymir.curl-boga.ts.net";
atreus = sshBlockDroid "192.168.0.3";
atreus-ts = sshBlockDroid "atreus.curl-boga.ts.net";
tartarus = sshBlock "192.168.0.4";
tartarus-ts = sshBlock "tartarus.curl-boga.ts.net";
pochita = sshBlock "192.168.0.9";
pochita-ts = sshBlock "pochita.curl-boga.ts.net";
harmonica = sshBlock "192.168.0.11";
harmonica-ts = sshBlock "harmonica.curl-boga.ts.net";
wallfacer = sshBlock "192.168.0.5";
wallfacer-ts = sshBlock "wallfacer.curl-boga.ts.net";
prometheus = sshBlock "192.168.0.12";
prometheus-ts = sshBlock "prometheus.curl-boga.ts.net";
apollo = sshBlock "152.53.152.129";
apollo-ts = sshBlock "apollo.curl-boga.ts.net";
apollo-initrd = {
hostname = "152.53.152.129";
port = 2222;
user = "root";
identityFile = "~/.ssh/id_ed25519";
hashKnownHosts = true;
compression = true;
};
ares = sshBlock "192.168.0.6";
ares-ts = sshBlock "ares.curl-boga.ts.net";
luoji = sshBlockDroid "192.168.0.7";
luoji-ts = sshBlockDroid "luoji.curl-boga.ts.net";
# artemis
};
};
}

View file

@ -1,18 +0,0 @@
{
programs.starship = {
enable = true;
enableFishIntegration = true;
settings = {
add_newline = false;
dart.disabled = true;
python.disabled = true;
nodejs.disabled = true;
c.disabled = true;
gradle.disabled = true;
java.disabled = true;
ruby.disabled = true;
rust.disabled = true;
typst.disabled = true;
};
};
}

View file

@ -1,21 +0,0 @@
{ pkgs, lib, ... }:
{
config = lib.mkIf pkgs.stdenv.isLinux {
home.packages = [ pkgs.swww ];
systemd.user.services.swww = {
Unit = {
Description = "Wayland wallpaper daemon";
PartOf = [ "graphical-session.target" ];
After = [ "graphical-session.target" ];
};
Service = {
ExecStart = "${pkgs.swww}/bin/swww-daemon";
Restart = "on-failure";
};
Install.WantedBy = [ "graphical-session.target" ];
};
};
}

View file

@ -1,94 +0,0 @@
{
pkgs,
config,
...
}:
# stolen from https://github.com/dmarcoux/dotfiles
{
home.packages = [ pkgs.tlrc ];
xdg.configFile."tlrc/config.toml".text = ''
[cache]
dir = "${config.xdg.cacheHome}/tlrc"
mirror = "https://github.com/tldr-pages/tldr/releases/latest/download"
auto_update = true
max_age = 336
languages = ["en", "tr", "ja"]
[output]
show_title = false
platform_title = false
show_hyphens = false
example_prefix = "- "
compact = true
raw_markdown = false
[indent]
title = 2
description = 2
bullet = 2
example = 4
[style.title]
color = "magenta"
background = "default"
bold = true
underline = false
italic = false
dim = false
strikethrough = false
[style.description]
color = "magenta"
background = "default"
bold = false
underline = false
italic = false
dim = false
strikethrough = false
[style.bullet]
color = "green"
background = "default"
bold = false
underline = false
italic = false
dim = false
strikethrough = false
[style.example]
color = "cyan"
background = "default"
bold = false
underline = false
italic = false
dim = false
strikethrough = false
[style.url]
color = "red"
background = "default"
bold = false
underline = false
italic = true
dim = false
strikethrough = false
[style.inline_code]
color = "yellow"
background = "default"
bold = false
underline = false
italic = true
dim = false
strikethrough = false
[style.placeholder]
color = "red"
background = "default"
bold = false
underline = false
italic = true
dim = false
strikethrough = false
'';
}

View file

@ -1,38 +0,0 @@
{
pkgs,
lib,
...
}:
{
programs.tmux = {
enable = true;
historyLimit = 100000;
baseIndex = 1;
shortcut = "s";
mouse = true;
shell = lib.getExe pkgs.fish;
plugins = with pkgs; [
tmuxPlugins.sensible
tmuxPlugins.better-mouse-mode
{
plugin = tmuxPlugins.dracula;
extraConfig = ''
set -g @dracula-plugins "cpu-usage ram-usage gpu-usage battery"
set -g @dracula-show-left-icon hostname
set -g @dracula-git-show-current-symbol
set -g @dracula-git-no-repo-message "no-git"
set -g @dracula-show-timezone false
set -g @dracula-ignore-lspci true
'';
}
];
extraConfig = ''
# Automatically renumber windows
set -g renumber-windows on
set -g allow-passthrough on
set -ga update-environment TERM
set -ga update-environment TERM_PROGRAM
set-option -g default-command "${lib.getExe pkgs.fish} -l"
'';
};
}

View file

@ -1,17 +0,0 @@
{
lib,
pkgs,
nixosConfig,
...
}:
{
config = lib.mkMerge [
# Auto-enable Waybar only if system has a desktop environment and is Linux
(lib.mkIf (pkgs.stdenv.isLinux && nixosConfig != null && !nixosConfig.osbmModules.desktopEnvironment.none) {
programs.waybar = {
enable = lib.mkDefault true;
systemd.enable = true;
};
})
];
}

View file

@ -1,36 +0,0 @@
{
lib,
pkgs,
nixosConfig,
...
}:
{
config = lib.mkMerge [
(lib.mkIf (nixosConfig != null && !nixosConfig.osbmModules.desktopEnvironment.none) {
programs.wezterm.enable = lib.mkDefault true;
})
{
programs.wezterm = {
extraConfig = ''
_G.shells = {
fish = '${lib.getExe pkgs.fish}'
};
wezterm.on('gui-startup', function(cmd)
local tab, pane, window = wezterm.mux.spawn_window(cmd or {})
window:gui_window():maximize()
end)
return {
default_prog = { _G.shells.fish },
window_decorations = "NONE",
hide_tab_bar_if_only_one_tab = true,
enable_wayland = false,
}
'';
};
}
];
}

View file

@ -1,6 +0,0 @@
{
programs.zoxide = {
enable = true;
enableFishIntegration = true;
};
}

View file

@ -1,9 +0,0 @@
{
imports = [
./options.nix
./programs
./hardware
./services
./system
];
}

View file

@ -1,7 +0,0 @@
{ config, lib, ... }:
{
config = lib.mkIf config.osbmModules.hardware.bluetooth.enable {
hardware.bluetooth.enable = true;
hardware.bluetooth.powerOnBoot = true;
};
}

View file

@ -1,8 +0,0 @@
{ config, lib, ... }:
{
config = lib.mkIf config.osbmModules.hardware.systemd-boot.enable {
boot.loader.systemd-boot.enable = true;
boot.loader.efi.canTouchEfiVariables = true;
};
}

View file

@ -1,11 +0,0 @@
{
imports = [
./bluetooth.nix
./boot.nix
./sound.nix
./nvidia.nix
./hibernation.nix
./wakeOnLan.nix
./disko.nix
];
}

View file

@ -1,341 +0,0 @@
{
config,
inputs,
lib,
pkgs,
...
}:
let
cfg = config.osbmModules.hardware.disko;
# Default authorized keys for initrd SSH
defaultAuthorizedKeys = [
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDF1TFwXbqdC1UyG75q3HO1n7/L3yxpeRLIq2kQ9DalI"
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHYSJ9ywFRJ747tkhvYWFkx/Y9SkLqv3rb7T1UuXVBWo"
];
authorizedKeys =
if cfg.initrd-ssh.authorizedKeys != [ ] then
cfg.initrd-ssh.authorizedKeys
else
defaultAuthorizedKeys;
in
{
imports = [
inputs.disko.nixosModules.default
];
config = lib.mkMerge [
# Initrd SSH for remote unlocking
(lib.mkIf (cfg.enable && cfg.initrd-ssh.enable) {
boot = {
kernelParams = [ "ip=152.53.152.129::152.53.152.1:255.255.252.0::eth0:none" ];
initrd = {
network.enable = true;
availableKernelModules = cfg.initrd-ssh.ethernetDrivers;
network.ssh = {
enable = true;
port = 2222; # different port to avoid conflicts
shell = "/bin/cryptsetup-askpass";
inherit authorizedKeys;
hostKeys = [ "/etc/ssh/initrd" ];
};
secrets = {
"/etc/ssh/initrd" = "/etc/ssh/initrd";
};
};
};
})
# ZFS Configuration
(lib.mkIf (cfg.enable && cfg.zfs.enable) {
networking.hostId = cfg.zfs.hostID;
environment.systemPackages = [ pkgs.zfs-prune-snapshots ];
boot = {
# ZFS does not support swapfiles
kernelParams = [
"nohibernate"
"zfs.zfs_arc_max=17179869184" # 16GB ARC max
];
supportedFilesystems = [
"vfat"
"zfs"
];
zfs = {
devNodes = "/dev/disk/by-id/";
forceImportAll = true;
requestEncryptionCredentials = cfg.zfs.root.encrypt;
};
};
services.zfs = {
autoScrub.enable = true;
trim.enable = true;
};
# Disko configuration for ZFS
disko.devices = {
disk = lib.mkMerge [
# Storage pool disks (if enabled and not reinstalling)
(lib.mkIf (cfg.zfs.storage.enable && !cfg.amReinstalling) (
lib.mkMerge (
map (diskname: {
"${diskname}" = {
type = "disk";
device = "/dev/${diskname}";
content = {
type = "gpt";
partitions = {
luks = {
size = "100%";
content = {
type = "luks";
name = "stg${diskname}";
settings.allowDiscards = true;
passwordFile = "/tmp/secret.key";
content = {
type = "zfs";
pool = "zstorage";
};
};
};
};
};
};
}) cfg.zfs.storage.disks
)
))
# Root disk 1 (primary)
{
one = lib.mkIf (cfg.zfs.root.disk1 != "") {
type = "disk";
device = "/dev/${cfg.zfs.root.disk1}";
content = {
type = "gpt";
partitions = {
ESP = {
label = "EFI";
name = "ESP";
size = "2048M";
type = "EF00";
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
mountOptions = [
"defaults"
"umask=0077"
];
};
};
# Encrypted root partition
luks = lib.mkIf cfg.zfs.root.encrypt {
size = "100%";
content = {
type = "luks";
name = "crypted1";
settings.allowDiscards = true;
passwordFile = "/tmp/secret.key";
content = {
type = "zfs";
pool = "zroot";
};
};
};
# Unencrypted root partition
notluks = lib.mkIf (!cfg.zfs.root.encrypt) {
size = "100%";
content = {
type = "zfs";
pool = "zroot";
};
};
};
};
};
# Root disk 2 (mirror)
two = lib.mkIf (cfg.zfs.root.disk2 != "") {
type = "disk";
device = "/dev/${cfg.zfs.root.disk2}";
content = {
type = "gpt";
partitions = {
luks = {
size = "100%";
content = {
type = "luks";
name = "crypted2";
settings.allowDiscards = true;
passwordFile = "/tmp/secret.key";
content = {
type = "zfs";
pool = "zroot";
};
};
};
};
};
};
}
];
# ZFS pools
zpool = {
# Root pool
zroot = {
type = "zpool";
mode = lib.mkIf cfg.zfs.root.mirror "mirror";
rootFsOptions = {
canmount = "off";
checksum = "edonr";
compression = "zstd";
dnodesize = "auto";
mountpoint = "none";
normalization = "formD";
relatime = "on";
"com.sun:auto-snapshot" = "false";
};
options = {
ashift = "12";
autotrim = "on";
};
datasets = {
# Reserved space for ZFS CoW operations
reserved = {
type = "zfs_fs";
options = {
canmount = "off";
mountpoint = "none";
inherit (cfg.zfs.root) reservation;
};
};
# SSH keys dataset
etcssh = {
type = "zfs_fs";
options.mountpoint = "legacy";
mountpoint = "/etc/ssh";
options."com.sun:auto-snapshot" = "false";
postCreateHook = "zfs snapshot zroot/etcssh@empty";
};
# Persistent data
persist = {
type = "zfs_fs";
options.mountpoint = "legacy";
mountpoint = "/persist";
options."com.sun:auto-snapshot" = "false";
postCreateHook = "zfs snapshot zroot/persist@empty";
};
# Persistent save data
persistSave = {
type = "zfs_fs";
options.mountpoint = "legacy";
mountpoint = "/persist/save";
options."com.sun:auto-snapshot" = "false";
postCreateHook = "zfs snapshot zroot/persistSave@empty";
};
# Nix store
nix = {
type = "zfs_fs";
options.mountpoint = "legacy";
mountpoint = "/nix";
options = {
atime = "off";
canmount = "on";
"com.sun:auto-snapshot" = "false";
};
postCreateHook = "zfs snapshot zroot/nix@empty";
};
# Root filesystem
root = {
type = "zfs_fs";
options.mountpoint = "legacy";
options."com.sun:auto-snapshot" = "false";
mountpoint = "/";
postCreateHook = "zfs snapshot zroot/root@empty";
};
};
};
# Storage pool (if enabled and not reinstalling)
zstorage = lib.mkIf (cfg.zfs.storage.enable && !cfg.amReinstalling) {
type = "zpool";
mode = lib.mkIf cfg.zfs.storage.mirror "mirror";
rootFsOptions = {
canmount = "off";
checksum = "edonr";
compression = "zstd";
dnodesize = "auto";
mountpoint = "none";
normalization = "formD";
relatime = "on";
"com.sun:auto-snapshot" = "false";
};
options = {
ashift = "12";
autotrim = "on";
};
datasets = {
# Reserved space
reserved = {
type = "zfs_fs";
options = {
canmount = "off";
mountpoint = "none";
inherit (cfg.zfs.storage) reservation;
};
};
# Main storage
storage = {
type = "zfs_fs";
mountpoint = "/storage";
options = {
atime = "off";
canmount = "on";
"com.sun:auto-snapshot" = "false";
};
};
# Persistent save in storage
persistSave = {
type = "zfs_fs";
mountpoint = "/storage/save";
options = {
atime = "off";
canmount = "on";
"com.sun:auto-snapshot" = "false";
};
};
};
};
};
};
fileSystems = {
# Needed for agenix - SSH keys must be available before ZFS mounts
"/etc/ssh".neededForBoot = true;
# Needed for impermanence
"/persist".neededForBoot = true;
"/persist/save".neededForBoot = true;
};
})
# Impermanence: wipe root on boot
(lib.mkIf (cfg.enable && cfg.zfs.enable && cfg.zfs.root.impermanenceRoot) {
boot.initrd.postResumeCommands = lib.mkAfter ''
zfs rollback -r zroot/root@empty
'';
})
];
}

View file

@ -1,12 +0,0 @@
{ lib, config, ... }:
{
config = lib.mkIf (!config.osbmModules.hardware.hibernation.enable) {
# Disable hibernation/suspend
systemd.targets = {
sleep.enable = false;
suspend.enable = false;
hibernate.enable = false;
hybrid-sleep.enable = false;
};
};
}

View file

@ -1,68 +0,0 @@
{
lib,
config,
pkgs,
...
}:
{
config = lib.mkIf config.osbmModules.hardware.nvidia.enable {
# Enable OpenGL
hardware.graphics = {
enable = true;
};
nixpkgs.config = {
cudaSupport = true;
cudaCapabilities = [ "8.9" ]; # 4090
};
# Load nvidia driver for Xorg and Wayland
services.xserver.videoDrivers = [ "nvidia" ];
hardware = {
nvidia = {
# Modesetting is required.
modesetting.enable = true;
# Nvidia power management. Experimental, and can cause sleep/suspend to fail.
# Enable this if you have graphical corruption issues or application crashes after waking
# up from sleep. This fixes it by saving the entire VRAM memory to /tmp/ instead
# of just the bare essentials.
powerManagement = {
enable = false;
# Fine-grained power management. Turns off GPU when not in use.
# Experimental and only works on modern Nvidia GPUs (Turing or newer).
finegrained = false;
};
# Use the NVidia open source kernel module (not to be confused with the
# independent third-party "nouveau" open source driver).
# Support is limited to the Turing and later architectures. Full list of
# supported GPUs is at:
# https://github.com/NVIDIA/open-gpu-kernel-modules#compatible-gpus
# Only available from driver 515.43.04+
# Currently alpha-quality/buggy, so false is currently the recommended setting.
open = false;
# Enable the Nvidia settings menu,
# accessible via `nvidia-settings`
nvidiaSettings = true;
# Optionally, you may need to select the appropriate driver version for your specific GPU.
package = config.boot.kernelPackages.nvidiaPackages.latest;
};
nvidia-container-toolkit.enable = lib.mkIf config.osbmModules.virtualisation.docker.enable true;
};
environment.systemPackages = [
pkgs.nvidia-container-toolkit
];
# TODO explain why this is needed
programs.nix-required-mounts = {
enable = true;
presets.nvidia-gpu.enable = true;
};
};
}

View file

@ -1,20 +0,0 @@
{ lib, config, ... }:
{
config = lib.mkIf config.osbmModules.hardware.sound.enable {
# Disable PulseAudio
services.pulseaudio.enable = false;
# Enable rtkit for realtime audio
security.rtkit.enable = true;
# Enable PipeWire
services.pipewire = {
enable = true;
alsa.enable = true;
alsa.support32Bit = true;
pulse.enable = true;
# If you want to use JACK applications:
# jack.enable = true;
};
};
}

View file

@ -1,25 +0,0 @@
{
pkgs,
lib,
config,
...
}:
{
config = lib.mkMerge [
(lib.mkIf config.osbmModules.hardware.wakeOnLan.enable {
networking.interfaces.enp3s0.wakeOnLan.enable = true;
# The services doesn't actually work atm, define an additional service
# see https://github.com/NixOS/nixpkgs/issues/91352
systemd.services.wakeonlan = {
description = "Reenable wake on lan every boot";
after = [ "network.target" ];
serviceConfig = {
Type = "simple";
RemainAfterExit = "true";
ExecStart = "${pkgs.ethtool}/sbin/ethtool -s enp3s0 wol g";
};
wantedBy = [ "default.target" ];
};
})
];
}

View file

@ -1,493 +0,0 @@
{ lib, config, ... }:
{
options.osbmModules = {
# Desktop Environment
desktopEnvironment = {
plasma.enable = lib.mkEnableOption "plasma";
gnome.enable = lib.mkEnableOption "gnome";
niri.enable = lib.mkEnableOption "niri";
hyprland.enable = lib.mkEnableOption "hyprland";
# none is true if all above are false, just for easier checks
none = lib.mkOption {
type = lib.types.bool;
default =
!(
config.osbmModules.desktopEnvironment.plasma.enable
|| config.osbmModules.desktopEnvironment.gnome.enable
|| config.osbmModules.desktopEnvironment.niri.enable
|| config.osbmModules.desktopEnvironment.hyprland.enable
);
description = "True if no desktop environment is enabled";
};
};
defaultUser = lib.mkOption {
type = lib.types.str;
default = "osbm";
description = "Default user for the system";
};
familyUser.enable = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Enable family user account";
};
# Home Manager
homeManager = {
enable = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Enable home-manager integration";
};
};
# Agenix
agenix = {
enable = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Enable agenix for secrets management";
};
};
# Nix Settings
nixSettings = {
enable = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Enable custom nix settings";
};
};
# Programs
programs = {
steam = {
enable = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Enable Steam gaming platform";
};
};
graphical = {
enable = lib.mkOption {
type = lib.types.bool;
default = !config.osbmModules.desktopEnvironment.none;
description = "Enable graphical applications";
};
};
commandLine = {
enable = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Enable common command line tools";
};
};
neovim = {
enable = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Enable neovim with custom configuration";
};
};
arduino = {
enable = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Enable Arduino IDE and development tools";
};
};
adbFastboot = {
enable = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Enable ADB and Fastboot for Android development";
};
};
};
# Services
services = {
openssh = {
enable = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Enable OpenSSH server";
};
};
tailscale = {
enable = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Enable Tailscale VPN";
};
};
actual.enable = lib.mkEnableOption "actual";
anubis.enable = lib.mkEnableOption "anubis";
syncthing.enable = lib.mkEnableOption "syncthing";
jellyfin.enable = lib.mkEnableOption "jellyfin";
mailserver.enable = lib.mkEnableOption "mailserver";
vaultwarden.enable = lib.mkEnableOption "vaultwarden";
nginx.enable = lib.mkEnableOption "nginx";
ollama.enable = lib.mkEnableOption "ollama";
forgejo.enable = lib.mkEnableOption "forgejo";
atticd.enable = lib.mkEnableOption "atticd";
cloudflared.enable = lib.mkEnableOption "cloudflared";
glance.enable = lib.mkEnableOption "glance";
hydra.enable = lib.mkEnableOption "hydra";
immich.enable = lib.mkEnableOption "immich";
vscode-server.enable = lib.mkEnableOption "vscode-server";
wanikani-bypass-lessons.enable = lib.mkEnableOption "wanikani-bypass-lessons";
wanikani-fetch-data.enable = lib.mkEnableOption "wanikani-fetch-data";
wanikani-stats.enable = lib.mkEnableOption "wanikani-stats";
# Backup server - exposes data for pull-based backups
backup-server = {
enable = lib.mkEnableOption "backup server (exposes data for pull-based backups)";
zfsSnapshots = {
enable = lib.mkEnableOption "automatic ZFS snapshots of /persist";
frequent = lib.mkOption {
type = lib.types.int;
default = 4;
description = "Number of frequent (15-min) snapshots to keep";
};
hourly = lib.mkOption {
type = lib.types.int;
default = 24;
description = "Number of hourly snapshots to keep";
};
daily = lib.mkOption {
type = lib.types.int;
default = 7;
description = "Number of daily snapshots to keep";
};
weekly = lib.mkOption {
type = lib.types.int;
default = 4;
description = "Number of weekly snapshots to keep";
};
monthly = lib.mkOption {
type = lib.types.int;
default = 12;
description = "Number of monthly snapshots to keep";
};
};
};
# Backup client - pulls backups from remote servers
backup-client = {
enable = lib.mkEnableOption "backup client (pulls data from remote servers)";
backups = lib.mkOption {
type = lib.types.attrsOf (
lib.types.submodule {
options = {
remoteHost = lib.mkOption {
type = lib.types.str;
description = "Remote host to backup from (e.g., apollo.tail-scale.ts.net)";
};
remoteUser = lib.mkOption {
type = lib.types.str;
default = "root";
description = "Remote user for SSH connection";
};
localPath = lib.mkOption {
type = lib.types.str;
description = "Local path where backups will be stored";
};
fullBackup = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Whether to backup the entire /persist directory";
};
services = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ ];
description = "List of services to backup (e.g., ['vaultwarden', 'immich'])";
example = [
"vaultwarden"
"immich"
"forgejo"
];
};
schedule = lib.mkOption {
type = lib.types.str;
default = "daily";
description = "Backup schedule (systemd timer format)";
example = "daily";
};
};
}
);
default = { };
description = "Backup configurations";
};
};
};
# Hardware
hardware = {
bluetooth = {
enable = lib.mkOption {
type = lib.types.bool;
default = !config.osbmModules.desktopEnvironment.none;
description = "Enable Bluetooth support";
};
};
sound = {
enable = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Enable sound with pipewire";
};
};
nvidia = {
enable = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Enable NVIDIA proprietary drivers";
};
};
hibernation = {
enable = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Enable hibernation support";
};
};
wakeOnLan = {
enable = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Enable wake-on-LAN support";
};
};
systemd-boot.enable = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Use systemd-boot bootloader";
};
# Disko configuration (inspired by ZFS.nix)
disko = {
enable = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Enable custom disk configuration with disko";
};
amReinstalling = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Am I reinstalling and want to save the storage pool";
};
fileSystem = lib.mkOption {
type = lib.types.enum [
"zfs"
"ext4"
];
default = "ext4";
description = "Root filesystem type";
};
initrd-ssh = {
enable = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Enable SSH in initrd for remote unlocking";
};
authorizedKeys = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ ];
description = "SSH public keys for initrd access";
};
ethernetDrivers = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ ];
description = "Ethernet drivers to load in initrd";
};
};
zfs = {
enable = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Enable ZFS filesystem";
};
hostID = lib.mkOption {
type = lib.types.str;
default = "";
description = "ZFS host ID (8 hex characters)";
};
root = {
useTmpfs = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Use tmpfs for root instead of ZFS (with ZFS datasets for /nix and /persist)";
};
tmpfsSize = lib.mkOption {
type = lib.types.str;
default = "2G";
description = "Size of tmpfs root filesystem";
};
encrypt = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Encrypt root ZFS pool";
};
disk1 = lib.mkOption {
type = lib.types.str;
default = "";
description = "First disk device name (e.g., nvme0n1)";
};
disk2 = lib.mkOption {
type = lib.types.str;
default = "";
description = "Second disk device name for mirroring";
};
reservation = lib.mkOption {
type = lib.types.str;
default = "20G";
description = "ZFS reservation size";
};
mirror = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Mirror the root ZFS pool";
};
impermanenceRoot = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Wipe the root directory on boot (impermanence)";
};
};
storage = {
enable = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Enable separate storage ZFS pool";
};
disks = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ ];
description = "Storage pool disk device names";
};
reservation = lib.mkOption {
type = lib.types.str;
default = "20G";
description = "Storage pool ZFS reservation";
};
mirror = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Mirror the storage ZFS pool";
};
};
};
};
};
virtualisation = {
docker = {
enable = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Enable Docker";
};
};
podman = {
enable = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Enable Podman";
};
};
libvirt = {
enable = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Enable libvirt/KVM";
};
};
};
# Emulation
emulation = {
aarch64 = {
enable = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Enable aarch64 emulation via binfmt";
};
};
};
# Internationalization
i18n = {
enable = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Enable internationalization settings";
};
};
# Fonts
fonts = {
enable = lib.mkOption {
type = lib.types.bool;
default = !config.osbmModules.desktopEnvironment.none;
description = "Enable custom fonts";
};
};
# Nix Index
nixIndex = {
enable = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Enable nix-index for command-not-found";
};
};
};
}

View file

@ -1,9 +0,0 @@
{ lib, config, ... }:
{
config = lib.mkIf config.osbmModules.programs.adbFastboot.enable {
programs.adb.enable = true;
# Add default user to adbusers group
users.users.${config.osbmModules.defaultUser}.extraGroups = [ "adbusers" ];
};
}

View file

@ -1,20 +0,0 @@
{
pkgs,
lib,
config,
...
}:
{
config = lib.mkIf config.osbmModules.programs.arduino.enable {
environment.systemPackages = with pkgs; [
arduino-ide
adafruit-nrfutil
python3 # some arduino libraries require python3
];
services.udev.extraRules = ''
KERNEL=="ttyUSB[0-9]*",MODE="0666"
KERNEL=="ttyACM[0-9]*",MODE="0666"
'';
};
}

View file

@ -1,78 +0,0 @@
{
pkgs,
lib,
config,
...
}:
{
config = lib.mkIf config.osbmModules.programs.commandLine.enable {
environment.systemPackages = with pkgs; [
# networking
wget
curl
dig
rclone
# text editors
nano
# version control
(pkgs.gitFull.override {
osxkeychainSupport = false;
})
git-lfs
lazygit
gh
# nix tools
nix-output-monitor
nixd
nix-inspect
comma
nh
# information and vanity
neofetch
onefetch
pfetch
htop
btop
cloc
inxi
tlrc
pciutils
# basic quality of life
eza
dysk
trash-cli
zoxide
lazysql
jq
ripgrep
dust
bat
just
tree
fd
yazi
duf
# archives
zip
unzip
# shell
fish
starship
# multiplexers
tmux
(pkgs.writeShellScriptBin "wake-ymir" ''
echo waking up ymir
${pkgs.wakeonlan}/bin/wakeonlan 04:7c:16:e6:d9:13
'')
];
};
}

View file

@ -1,10 +0,0 @@
{
imports = [
./adb-fastboot.nix
./arduino.nix
./command-line.nix
./graphical.nix
./neovim.nix
./steam.nix
];
}

View file

@ -1,24 +0,0 @@
{
pkgs,
lib,
config,
...
}:
{
config = lib.mkIf config.osbmModules.programs.graphical.enable {
environment.systemPackages = with pkgs; [
mpv
gimp
inkscape
libreoffice
discord
telegram-desktop
obs-studio
blender
vscode
chromium
thunderbird
claude-code
];
};
}

View file

@ -1,19 +0,0 @@
{
lib,
inputs,
config,
pkgs,
...
}:
{
config = lib.mkIf config.osbmModules.programs.neovim.enable {
environment.systemPackages = [
inputs.osbm-nvim.packages."${pkgs.stdenv.hostPlatform.system}".default
];
# Environment variables
environment.variables = {
EDITOR = "nvim";
VISUAL = "nvim";
};
};
}

View file

@ -1,14 +0,0 @@
{ lib, config, ... }:
{
config = lib.mkIf config.osbmModules.programs.steam.enable {
programs.steam = {
enable = true;
# Open ports in the firewall for Steam Remote Play
remotePlay.openFirewall = true;
# Open ports in the firewall for Source Dedicated Server
dedicatedServer.openFirewall = true;
# Open ports in the firewall for Steam Local Network Game Transfers
localNetworkGameTransfers.openFirewall = true;
};
};
}

View file

@ -1,54 +0,0 @@
{ config, lib, ... }:
{
config = lib.mkMerge [
(lib.mkIf config.osbmModules.services.actual.enable {
services.actual = {
enable = true;
settings = {
port = 51514;
# dataDir = "/var/lib/actual"
};
};
})
# actual and nginx
(lib.mkIf (config.osbmModules.services.nginx.enable && config.osbmModules.services.actual.enable) {
services.nginx.virtualHosts."actual.osbm.dev" = {
forceSSL = true;
enableACME = true;
locations."/" = {
proxyPass = "http://localhost:${toString config.services.actual.settings.port}";
proxyWebsockets = true;
};
};
})
# # impermanence and immich
# (lib.mkIf
# (
# config.osbmModules.services.immich.enable
# && config.osbmModules.hardware.disko.zfs.root.impermanenceRoot
# )
# {
# environment.persistence."/persist" = {
# directories = [
# {
# directory = "/var/lib/immich";
# user = config.services.immich.user;
# group = config.services.immich.group;
# mode = "0750";
# }
# {
# directory = "/var/lib/postgresql";
# user = "postgres";
# group = "postgres";
# mode = "0750";
# }
# ];
# };
# }
# )
];
}

View file

@ -1,14 +0,0 @@
{
config,
lib,
...
}:
{
config = lib.mkMerge [
(lib.mkIf config.osbmModules.services.anubis.enable {
services.anubis = {
# enable = true;
};
})
];
}

View file

@ -1,43 +0,0 @@
{
config,
lib,
...
}:
let
atticdPort = 7080;
in
{
config = lib.mkMerge [
(lib.mkIf config.osbmModules.services.atticd.enable {
services.atticd = {
enable = true;
environmentFile = "/persist/attic.env";
settings = {
listen = "[::]:${toString atticdPort}";
compression = {
type = "zstd";
level = 9;
};
# jwt = { };
# storage = {
# type = "local";
# # path = "/data/atreus/attic";
# # there is an issue
# };
};
};
networking.firewall.allowedTCPPorts = [ atticdPort ];
services.cloudflared.tunnels = {
"fa301a21-b259-4149-b3d0-b1438c7c81f8" = {
default = "http_status:404";
credentialsFile = "/home/osbm/.cloudflared/fa301a21-b259-4149-b3d0-b1438c7c81f8.json";
ingress = {
"cache.osbm.dev" = {
service = "http://localhost:${toString atticdPort}";
};
};
};
};
})
];
}

View file

@ -1,96 +0,0 @@
# Backup client - pulls backups from remote servers via rsync
# Supports full backups and selective service backups over Tailscale
{
config,
lib,
pkgs,
...
}:
let
cfg = config.osbmModules.services.backup-client;
# Create a backup job for each configured backup
makeBackupService =
name: backupCfg:
let
# Build rsync paths based on what we're backing up
sourcePaths =
if backupCfg.fullBackup then
[ "/persist" ] # Full backup of everything
else if backupCfg.services != [ ] then
# Selective service backups
map (service: "/persist/var/lib/${service}") backupCfg.services
++ lib.optional (builtins.elem "vaultwarden" backupCfg.services) "/persist/backup/vaultwarden"
else
[ ]; # Empty list if nothing to backup
# Rsync command for each source path
rsyncCommands = map (source: ''
echo "Backing up ${source} from ${backupCfg.remoteHost}..."
${pkgs.rsync}/bin/rsync -avz --delete \
--chown=root:root \
-e "${pkgs.openssh}/bin/ssh -o StrictHostKeyChecking=accept-new" \
${backupCfg.remoteUser}@${backupCfg.remoteHost}:${source}/ \
${backupCfg.localPath}/${builtins.baseNameOf source}/
'') sourcePaths;
in
{
description = "Backup ${name} from ${backupCfg.remoteHost}";
wants = [ "network-online.target" ];
after = [ "network-online.target" ];
serviceConfig = {
Type = "oneshot";
User = "root";
};
script = ''
set -e
# Create backup directory if it doesn't exist
mkdir -p ${backupCfg.localPath}
# Run rsync for each source path
${lib.concatStringsSep "\n" rsyncCommands}
echo "Backup ${name} completed successfully at $(date)"
'';
};
makeBackupTimer = name: backupCfg: {
description = "Timer for ${name} backup";
wantedBy = [ "timers.target" ];
timerConfig = {
OnCalendar = backupCfg.schedule;
Persistent = true;
RandomizedDelaySec = "30m"; # Randomize to avoid all backups running at once
};
};
in
{
config = lib.mkIf cfg.enable {
# Create systemd services for each backup
systemd.services = lib.mapAttrs' (
name: backupCfg: lib.nameValuePair "backup-${name}" (makeBackupService name backupCfg)
) cfg.backups;
# Create systemd timers for each backup
systemd.timers = lib.mapAttrs' (
name: backupCfg: lib.nameValuePair "backup-${name}" (makeBackupTimer name backupCfg)
) cfg.backups;
# Ensure rsync and openssh are available
environment.systemPackages = with pkgs; [
rsync
openssh
];
# Ensure Tailscale is enabled for secure connections
assertions = [
{
assertion = config.services.tailscale.enable;
message = "backup-client requires Tailscale to be enabled for secure connections";
}
];
};
}

View file

@ -1,37 +0,0 @@
# Backup server - exposes data for backup clients to pull
# Enables ZFS snapshots for local point-in-time recovery
{
config,
lib,
...
}:
let
cfg = config.osbmModules.services.backup-server;
in
{
config = lib.mkIf cfg.enable {
# Enable ZFS auto-snapshots if requested
services.zfs.autoSnapshot = lib.mkIf cfg.zfsSnapshots.enable {
enable = true;
inherit (cfg.zfsSnapshots)
frequent
hourly
daily
weekly
monthly
;
};
# Allow root SSH login for backup clients to pull data
# Note: Root's authorized keys are already configured in system/users.nix
services.openssh.settings.PermitRootLogin = lib.mkForce "prohibit-password";
# Ensure SSH is enabled for backup access
assertions = [
{
assertion = config.services.openssh.enable;
message = "backup-server requires openssh to be enabled";
}
];
};
}

View file

@ -1,15 +0,0 @@
{
config,
lib,
...
}:
{
config = lib.mkMerge [
(lib.mkIf config.osbmModules.services.cloudflared.enable {
services.cloudflared = {
enable = true;
certificateFile = "/home/osbm/.cloudflared/cert.pem";
};
})
];
}

View file

@ -1,29 +0,0 @@
{
imports = [
./actual.nix
./anubis.nix
./atticd.nix
./backup-client.nix
./backup-server.nix
./cloudflared.nix
./ollama.nix
./openssh.nix
./forgejo.nix
./glance.nix
./hydra.nix
./immich.nix
./jellyfin.nix
./mailserver.nix
./nginx.nix
./syncthing.nix
./tailscale.nix
./vaultwarden.nix
./vscode-server.nix
# custom services
./system-logger
./wanikani-bypass-lessons.nix
./wanikani-fetch-data
./wanikani-stats
];
}

View file

@ -1,103 +0,0 @@
{
lib,
config,
...
}:
{
config = lib.mkMerge [
(lib.mkIf config.osbmModules.services.forgejo.enable {
services.forgejo = {
enable = true;
lfs.enable = true;
secrets.mailer.PASSWD = config.age.secrets."forgejo-mail".path;
dump = {
enable = true;
type = "zip";
interval = "01:01";
};
settings = {
DEFAULT = {
APP_NAME = "osbm's self hosted git service";
};
server = {
DOMAIN = "git.osbm.dev";
ROOT_URL = "https://git.osbm.dev/";
};
"ui.meta" = {
AUTHOR = "osbm";
DESCRIPTION = "\"After all, all devices have their dangers. The discovery of speech introduced communication and lies.\" -Isaac Asimov";
KEYWORDS = "git,self-hosted,gitea,forgejo,osbm,open-source,nix,nixos";
};
service = {
DISABLE_REGISTRATION = true;
LANDING_PAGE = "/osbm";
};
other = {
SHOW_FOOTER_VERSION = false;
SHOW_FOOTER_TEMPLATE_LOAD_TIME = false;
ENABLE_FEED = false;
};
mailer = {
ENABLED = true;
PROTOCOL = "smtps";
SMTP_ADDR = "osbm.dev";
USER = "forgejo@osbm.dev";
};
};
};
})
(lib.mkIf
(config.osbmModules.services.cloudflared.enable && config.osbmModules.services.forgejo.enable)
{
services.cloudflared.tunnels = {
"eb9052aa-9867-482f-80e3-97a7d7e2ef04" = {
default = "http_status:404";
credentialsFile = "/home/osbm/.cloudflared/eb9052aa-9867-482f-80e3-97a7d7e2ef04.json";
ingress = {
"${config.services.forgejo.settings.server.DOMAIN}" = {
service = "http://localhost:3000";
};
};
};
};
}
)
(lib.mkIf (config.osbmModules.services.nginx.enable && config.osbmModules.services.forgejo.enable) {
services.nginx.virtualHosts."${config.services.forgejo.settings.server.DOMAIN}" = {
forceSSL = true;
enableACME = true;
locations."/".proxyPass = "http://localhost:3000";
locations."/".proxyWebsockets = true;
};
})
(lib.mkIf
(
config.osbmModules.services.forgejo.enable
&& config.osbmModules.hardware.disko.zfs.root.impermanenceRoot
)
{
# environment.persistence."/persist" = {
# directories = [
# {
# directory = "/var/lib/forgejo";
# user = config.services.forgejo.user;
# group = config.services.forgejo.group;
# mode = "0750";
# }
# ];
# };
# # forgejo-secrets service keep giving error
# systemd.services."forgejo-secrets" = {
# wants = [ "var-lib-forgejo.mount" ];
# after = [ "var-lib-forgejo.mount" ];
# };
# fuckass thing
services.forgejo.stateDir = "/persist/var/lib/forgejo";
}
)
];
}

View file

@ -1,191 +0,0 @@
{
lib,
config,
...
}:
{
config = lib.mkMerge [
(lib.mkIf config.osbmModules.services.glance.enable {
services.glance = {
enable = true;
openFirewall = true;
settings = {
server = {
port = 3838;
host = "0.0.0.0";
};
branding = {
# stolen from notohh/snowflake but i love it so much
custom-footer = ''<b><p></p></b>'';
};
pages = [
{
columns = [
{
size = "small";
widgets = [
{ type = "calendar"; }
{
type = "bookmarks";
groups = [
{
title = "My Profiles";
same-tab = true;
color = "200 50 50";
links = [
{
title = "GitHub";
url = "https://github.com/osbm";
}
{
title = "Gitlab";
url = "https://gitlab.com/osbm";
}
{
title = "Crates.io";
url = "https://crates.io/users/osbm";
}
{
title = "HuggingFace";
url = "https://huggingface.co/osbm";
}
{
title = "Bluesky";
url = "https://bsky.app/profile/osbm.dev";
}
{
title = "Docker Hub";
url = "https://hub.docker.com/u/osbm";
}
{
title = "Kaggle";
url = "https://www.kaggle.com/osmanf";
}
];
}
{
title = "Documents";
links = [
{
title = "Nixos Search";
url = "https://search.nixos.org";
}
];
}
];
}
];
}
{
size = "full";
widgets = [
{
type = "search";
search-engine = "google";
bangs = [
{
title = "youtube";
shortcut = "!yt";
url = "https://www.youtube.com/results?search_query={QUERY}";
}
{
title = "nixpkgs";
shortcut = "!np";
url = "https://search.nixos.org/packages?channel=unstable&query={QUERY}";
}
{
title = "nixos";
shortcut = "!no";
url = "https://search.nixos.org/options?channel=unstable&query={QUERY}";
}
];
}
{
cache = "1m";
sites = [
{
icon = "sh:forgejo";
title = "Forgejo git server";
url = "https://git.osbm.dev";
}
{
icon = "sh:bitwarden";
title = "Bitwarden Vault";
url = "https://bitwarden.osbm.dev";
}
{
icon = "sh:immich";
title = "Immich Photo Server";
url = "https://immich.osbm.dev";
}
{
icon = "sh:actual-budget";
title = "Actual Budget";
url = "https://actual.osbm.dev"; # todo migrate to budget.osbm.dev
}
{
icon = "sh:visual-studio-code";
title = "Ymir Remote VSCode";
url = "http://ymir.curl-boga.ts.net:4444/";
}
{
icon = "sh:visual-studio-code";
title = "Tartarus Remote VSCode";
url = "http://tartarus.curl-boga.ts.net:4444/";
}
{
icon = "sh:visual-studio-code";
title = "Wallfacer Remote VSCode";
url = "http://wallfacer.curl-boga.ts.net:4444/";
}
{
icon = "si:json";
title = "Wanikani Stats";
url = "http://pochita:8501";
}
];
title = "Services";
type = "monitor";
}
];
}
];
name = "Home";
content = "Welcome to osbm's home page!";
}
];
};
};
networking.firewall.allowedTCPPorts = [ config.services.glance.settings.server.port ];
})
(lib.mkIf
(config.osbmModules.services.cloudflared.enable && config.osbmModules.services.glance.enable)
{
services.cloudflared.tunnels = {
"91b13f9b-81be-46e1-bca0-db2640bf2d0a" = {
default = "http_status:404";
credentialsFile = "/home/osbm/.cloudflared/91b13f9b-81be-46e1-bca0-db2640bf2d0a.json";
ingress = {
"home.osbm.dev" = {
service = "http://localhost:${toString config.services.glance.settings.server.port}";
};
};
};
};
}
)
# if nginx and glance are both enabled, set up a reverse proxy
(lib.mkIf (config.osbmModules.services.nginx.enable && config.osbmModules.services.glance.enable) {
services.nginx.virtualHosts."home.osbm.dev" = {
forceSSL = true;
enableACME = true;
locations."/" = {
proxyPass = "http://localhost:${toString config.services.glance.settings.server.port}";
proxyWebsockets = true;
};
};
})
];
}

View file

@ -1,22 +0,0 @@
{
config,
lib,
...
}:
{
config = lib.mkMerge [
(lib.mkIf config.osbmModules.services.hydra.enable {
services.hydra = {
enable = true;
port = 3000;
hydraURL = "http://${config.networking.hostName}.curl-boga.ts.net/hydra/";
notificationSender = "hydra@localhost";
buildMachinesFiles = [ ];
useSubstitutes = true;
};
networking.firewall.allowedTCPPorts = [
config.services.hydra.port
];
})
];
}

View file

@ -1,48 +0,0 @@
{ config, lib, ... }:
{
config = lib.mkMerge [
(lib.mkIf config.osbmModules.services.immich.enable {
services.immich = {
enable = true;
};
})
# immich and nginx
(lib.mkIf (config.osbmModules.services.nginx.enable && config.osbmModules.services.immich.enable) {
services.nginx.virtualHosts."immich.osbm.dev" = {
forceSSL = true;
enableACME = true;
locations."/" = {
proxyPass = "http://localhost:${toString config.services.immich.port}";
proxyWebsockets = true;
};
};
})
# impermanence and immich
(lib.mkIf
(
config.osbmModules.services.immich.enable
&& config.osbmModules.hardware.disko.zfs.root.impermanenceRoot
)
{
environment.persistence."/persist" = {
directories = [
{
directory = "/var/lib/immich";
inherit (config.services.immich) user group;
mode = "0750";
}
{
directory = "/var/lib/postgresql";
user = "postgres";
group = "postgres";
mode = "0750";
}
];
};
}
)
];
}

View file

@ -1,20 +0,0 @@
{
config,
lib,
...
}:
{
config = lib.mkMerge [
(lib.mkIf config.osbmModules.services.jellyfin.enable {
services.jellyfin = {
enable = true;
openFirewall = true;
user = "osbm";
group = "users";
dataDir = "/home/osbm/.local/share/jellyfin";
};
networking.firewall.allowedTCPPorts = [ 8096 ];
})
];
}

View file

@ -1,120 +0,0 @@
{
config,
inputs,
lib,
...
}:
{
imports = [
inputs.simple-nixos-mailserver.nixosModule
];
config = lib.mkMerge [
(lib.mkIf config.osbmModules.services.mailserver.enable {
mailserver = {
enable = true;
stateVersion = 3;
fqdn = "mail.osbm.dev";
domains = [ "osbm.dev" ];
# Set all no-reply addresses
rejectRecipients = [ "noreply@osbm.dev" ];
# A list of all login accounts. To create the password hashes, use
# nix-shell -p mkpasswd --run 'mkpasswd -sm bcrypt'
loginAccounts = {
"osbm@osbm.dev" = {
hashedPasswordFile = config.age.secrets."osbm-mail".path;
aliases = [
"osbm"
"osman@osbm.dev"
"postmaster@osbm.dev"
"root@osbm.dev"
"mastercontrol@osbm.dev"
"admin@osbm.dev"
];
};
"forgejo@osbm.dev" = {
hashedPasswordFile = config.age.secrets."forgejo-mail".path;
};
"vaultwarden@osbm.dev" = {
hashedPasswordFile = config.age.secrets."vaultwarden-mail".path;
};
"noreply@osbm.dev" = {
hashedPasswordFile = config.age.secrets."noreply-mail".path;
};
};
# reference an existing ACME configuration
x509.useACMEHost = config.mailserver.fqdn;
};
})
# Configure ACME certificate via nginx
(lib.mkIf
(config.osbmModules.services.nginx.enable && config.osbmModules.services.mailserver.enable)
{
services.nginx.virtualHosts."${config.mailserver.fqdn}" = {
forceSSL = true;
enableACME = true;
locations."/".return = "404"; # Just for ACME, no actual web content
};
}
)
# mailserver and impermanence
(lib.mkIf
(
config.osbmModules.services.mailserver.enable
&& config.osbmModules.hardware.disko.zfs.root.impermanenceRoot
)
{
environment.persistence."/persist" = {
directories = [
# Dovecot is an open source IMAP and POP3 server
# which means it handles email retrieval for users.
"/var/lib/dovecot" # owned by root
# Postfix is a open-source mail transfer agent (MTA)
"/var/lib/postfix" # owned by root
# Rspamd is a open-source spam filtering system.
{
directory = "/var/lib/rspamd";
user = "rspamd";
group = "rspamd";
mode = "0700";
}
# redis-rspamd is just a redis instance used by rspamd for caching
# TODO: what is the /var/spool folder?
{
directory = "/var/spool/redis-rspamd";
user = "redis-rspamd";
group = "redis-rspamd";
mode = "0750";
}
# Sieve is a scripting language for filtering email messages.
{
directory = config.mailserver.sieveDirectory; # /var/sieve by default
user = "virtualMail";
group = "virtualMail";
mode = "0770";
}
# Mail folder
{
directory = config.mailserver.mailDirectory; # /var/vmail by default
user = config.mailserver.vmailUserName;
group = config.mailserver.vmailGroupName;
mode = "0700";
}
# DKIM is used to sign outgoing emails to verify they are from the claimed domain.
{
directory = config.mailserver.dkimKeyDirectory; # /var/dkim by default
user = "rspamd";
group = "rspamd";
mode = "0755";
}
];
};
}
)
];
}

View file

@ -1,43 +0,0 @@
{
config,
lib,
...
}:
{
config = lib.mkMerge [
(lib.mkIf config.osbmModules.services.nginx.enable {
services.nginx = {
enable = true;
};
networking.firewall.allowedTCPPorts = [
80
443
];
security.acme = {
acceptTerms = true;
defaults.email = "osbm@osbm.dev";
};
})
(lib.mkIf
(
config.osbmModules.services.nginx.enable
&& config.osbmModules.hardware.disko.zfs.root.impermanenceRoot
)
{
# environment.persistence."/persist" = {
# directories = [
# {
# directory = "/var/lib/acme";
# user = "acme";
# group = "nginx";
# mode = "0750";
# }
# ];
# };
}
)
];
}

View file

@ -1,34 +0,0 @@
{
lib,
config,
pkgs,
...
}:
{
config = lib.mkMerge [
(lib.mkIf config.osbmModules.services.ollama.enable {
services.ollama = {
enable = true;
package = pkgs.ollama-cuda;
# loadModels = [
# "deepseek-r1:7b"
# "deepseek-r1:14b"
# ];
};
services.open-webui = {
enable = false; # TODO gives error fix later
port = 7070;
host = "0.0.0.0";
openFirewall = true;
environment = {
SCARF_NO_ANALYTICS = "True";
DO_NOT_TRACK = "True";
ANONYMIZED_TELEMETRY = "False";
WEBUI_AUTH = "False";
ENABLE_LOGIN_FORM = "False";
};
};
})
];
}

View file

@ -1,31 +0,0 @@
{
config,
lib,
...
}:
{
config = lib.mkMerge [
(lib.mkIf config.osbmModules.services.openssh.enable {
services.openssh = {
enable = true;
startWhenNeeded = true;
settings = {
PermitRootLogin = "no";
# only allow key based logins and not password
PasswordAuthentication = false;
KbdInteractiveAuthentication = false;
AuthenticationMethods = "publickey";
PubkeyAuthentication = "yes";
ChallengeResponseAuthentication = "no";
UsePAM = false;
# kick out inactive sessions
ClientAliveCountMax = 5;
ClientAliveInterval = 60;
};
};
})
];
}

View file

@ -1,16 +0,0 @@
{
config,
lib,
...
}:
{
config = lib.mkMerge [
(lib.mkIf config.osbmModules.services.syncthing.enable {
services.syncthing = {
enable = true;
openDefaultPorts = true;
# port is 8384
};
})
];
}

View file

@ -1,68 +0,0 @@
{
pkgs,
config,
lib,
...
}:
let
system-logger = pkgs.writeShellApplication {
name = "system-logger";
runtimeInputs = with pkgs; [
curl
jq
zip
gawk
systemd
];
text = builtins.readFile ./system-logger.sh;
};
in
{
options.services.system-logger = {
enable = lib.mkEnableOption {
description = "Enable System Logger Service";
default = false;
};
logDirectory = lib.mkOption {
type = lib.types.path;
default = "/var/lib/system-logger";
description = "Directory to store log archives";
};
maxSizeMB = lib.mkOption {
type = lib.types.int;
default = 1;
description = "Maximum size of daily log archive in megabytes";
};
retentionDays = lib.mkOption {
type = lib.types.int;
default = 30;
description = "Number of days to retain log archives";
};
};
config = lib.mkIf config.services.system-logger.enable {
systemd.timers.system-logger = {
description = "System Logger Timer";
wantedBy = [ "timers.target" ];
timerConfig = {
OnCalendar = "daily";
Persistent = true;
};
};
systemd.services.system-logger = {
description = "System Logger Service";
serviceConfig = {
Type = "oneshot";
ExecStart = "${lib.getExe system-logger}";
Restart = "on-failure";
RestartSec = 60;
User = "root";
Group = "root";
};
};
};
}

View file

@ -1,135 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
# Configuration
LOG_DIR="/var/lib/system-logger"
MAX_SIZE_MB=1
RETENTION_DAYS=30
DATE=$(date +%Y-%m-%d)
HOSTNAME=$(hostname)
TEMP_DIR=$(mktemp -d)
# Create log directory if it doesn't exist
mkdir -p "$LOG_DIR"
# Check if today's log already exists
if [ -f "$LOG_DIR/${DATE}-logs-${HOSTNAME}.zip" ]; then
echo "Logs for today already exist. Exiting..."
exit 0
fi
echo "Starting system log collection for $DATE"
# Function to collect logs with size limit
collect_logs() {
local source="$1"
local output="$2"
local max_lines="$3"
if [ -f "$source" ]; then
# Get the last N lines to stay within size limit
tail -n "$max_lines" "$source" > "$output" 2>/dev/null || true
echo "Collected from $source"
else
echo "Source $source not found, skipping..."
fi
}
# Function to get journal logs with filtering
get_journal_logs() {
local output="$1"
local filter="$2"
local max_lines="$3"
journalctl --since "00:00:00" --until "23:59:59" \
--no-pager --output=short \
| grep -i "$filter" | tail -n "$max_lines" > "$output" 2>/dev/null || true
echo "Collected journal logs for $filter"
}
# Calculate approximate lines per log type to stay under 1MB
# Assuming average line is ~100 bytes, we aim for ~10,000 total lines
SSH_LINES=2000
KERNEL_LINES=2000
LOGIN_LINES=1000
SYSTEM_LINES=2000
AUTH_LINES=1000
FAILED_LOGIN_LINES=500
DISK_LINES=500
NETWORK_LINES=500
MEMORY_LINES=500
# Collect SSH connections
get_journal_logs "$TEMP_DIR/ssh.log" "sshd" "$SSH_LINES"
# Collect kernel warnings and errors
get_journal_logs "$TEMP_DIR/kernel.log" "kernel.*warning\|kernel.*error" "$KERNEL_LINES"
# Collect login/logout events
get_journal_logs "$TEMP_DIR/login.log" "session.*opened\|session.*closed\|login\|logout" "$LOGIN_LINES"
# Collect system messages
get_journal_logs "$TEMP_DIR/system.log" "systemd\|daemon" "$SYSTEM_LINES"
# Collect authentication events
get_journal_logs "$TEMP_DIR/auth.log" "authentication\|auth" "$AUTH_LINES"
# Collect failed login attempts
get_journal_logs "$TEMP_DIR/failed_login.log" "failed\|failure\|denied" "$FAILED_LOGIN_LINES"
# Collect disk usage and errors
get_journal_logs "$TEMP_DIR/disk.log" "disk\|storage\|iostat" "$DISK_LINES"
# Collect network events
get_journal_logs "$TEMP_DIR/network.log" "network\|connection\|interface" "$NETWORK_LINES"
# Collect memory usage
get_journal_logs "$TEMP_DIR/memory.log" "memory\|oom\|swap" "$MEMORY_LINES"
# Collect traditional log files if they exist
collect_logs "/var/log/auth.log" "$TEMP_DIR/auth_traditional.log" 1000
collect_logs "/var/log/syslog" "$TEMP_DIR/syslog_traditional.log" 1000
collect_logs "/var/log/messages" "$TEMP_DIR/messages_traditional.log" 1000
# Create a summary file
{
echo "=== System Log Summary for $DATE ==="
echo "Hostname: $HOSTNAME"
echo "Collection time: $(date)"
echo "Total lines collected:"
wc -l "$TEMP_DIR"/*.log 2>/dev/null || true
echo ""
echo "=== System Information ==="
echo "Uptime: $(uptime)"
echo "Load average: $(cat /proc/loadavg)"
echo "Memory usage:"
free -h
echo ""
echo "Disk usage:"
df -h
echo ""
echo "Active users:"
who
} > "$TEMP_DIR/summary.txt"
# Create the zip file
cd "$TEMP_DIR"
zip -r "$LOG_DIR/${DATE}-logs-${HOSTNAME}.zip" ./* > /dev/null
# Check file size and warn if too large
FILE_SIZE=$(stat -c%s "$LOG_DIR/${DATE}-logs-${HOSTNAME}.zip")
FILE_SIZE_MB=$((FILE_SIZE / 1024 / 1024))
if [ "$FILE_SIZE_MB" -gt "$MAX_SIZE_MB" ]; then
echo "WARNING: Log file size ($FILE_SIZE_MB MB) exceeds limit ($MAX_SIZE_MB MB)"
fi
echo "Log collection completed: $LOG_DIR/${DATE}-logs-${HOSTNAME}.zip ($FILE_SIZE_MB MB)"
# Clean up old logs (older than RETENTION_DAYS)
find "$LOG_DIR" -name "*-logs-*.zip" -type f -mtime +$RETENTION_DAYS -delete 2>/dev/null || true
# Clean up temporary directory
rm -rf "$TEMP_DIR"
echo "System log collection finished successfully"

View file

@ -1,32 +0,0 @@
{
config,
lib,
pkgs,
...
}:
{
config = lib.mkMerge [
(lib.mkIf config.osbmModules.services.tailscale.enable {
services.tailscale = {
enable = true;
port = 51513;
};
networking.firewall.allowedUDPPorts = [ config.services.tailscale.port ];
environment.systemPackages = [ pkgs.tailscale ];
})
# tailscale and impermanence
(lib.mkIf
(
config.osbmModules.services.tailscale.enable
&& config.osbmModules.hardware.disko.zfs.root.impermanenceRoot
)
{
environment.persistence."/persist".directories = [
"/var/lib/tailscale"
];
}
)
];
}

View file

@ -1,75 +0,0 @@
{
config,
lib,
...
}:
{
config = lib.mkMerge [
(lib.mkIf config.osbmModules.services.vaultwarden.enable {
services.vaultwarden = {
enable = true;
backupDir = "/persist/backup/vaultwarden";
# in order to avoid having ADMIN_TOKEN in the nix store it can be also set with the help of an environment file
# be aware that this file must be created by hand (or via secrets management like sops)
environmentFile = config.age.secrets.vaultwarden.path;
config = {
# Refer to https://github.com/dani-garcia/vaultwarden/blob/main/.env.template
DOMAIN = "https://bitwarden.osbm.dev";
SIGNUPS_ALLOWED = false;
ROCKET_ADDRESS = "127.0.0.1";
ROCKET_PORT = 8222;
ROCKET_LOG = "critical";
# This example assumes a mailserver running on localhost,
# thus without transport encryption.
# If you use an external mail server, follow:
# https://github.com/dani-garcia/vaultwarden/wiki/SMTP-configuration
SMTP_HOST = "127.0.0.1";
SMTP_PORT = 25;
SMTP_SSL = false;
SMTP_FROM = "vaultwarden@osbm.dev";
SMTP_FROM_NAME = "osbm.dev Bitwarden server";
};
};
})
# vaultwarden reverse proxy via nginx
(lib.mkIf
(config.osbmModules.services.nginx.enable && config.osbmModules.services.vaultwarden.enable)
{
services.nginx.virtualHosts."bitwarden.osbm.dev" = {
forceSSL = true;
enableACME = true;
locations."/" = {
proxyPass = "http://localhost:${toString config.services.vaultwarden.config.ROCKET_PORT}";
};
};
}
)
# impermanence with vaultwarden
(lib.mkIf
(
config.osbmModules.services.vaultwarden.enable
&& config.osbmModules.hardware.disko.zfs.root.impermanenceRoot
)
{
systemd.services.vaultwarden.serviceConfig.ReadWritePaths = [ "/var/lib/vaultwarden" ];
# TODO try if not using bindMounts fixes this
environment.persistence."/persist" = {
directories = [
{
directory = "/var/lib/vaultwarden";
user = config.systemd.services.vaultwarden.serviceConfig.User;
group = config.systemd.services.vaultwarden.serviceConfig.Group;
mode = "0750";
}
];
};
}
)
];
}

View file

@ -1,56 +0,0 @@
{
config,
pkgs,
lib,
...
}:
{
config = lib.mkIf config.osbmModules.services.vscode-server.enable {
services.code-server = {
# only true if the machine is not pochita
enable = config.networking.hostName != "pochita";
port = 4444;
disableTelemetry = true;
disableUpdateCheck = true;
user = "osbm";
group = "users";
# auth = "none";
host = "${config.networking.hostName}.curl-boga.ts.net";
hashedPassword = "$argon2i$v=19$m=4096,t=3,p=1$dGc0TStGMDNzSS9JRkJYUFp3d091Q2p0bXlzPQ$zvdE9BkclkJmyFaenzPy2E99SEqsyDMt4IQNZfcfFFQ";
package = pkgs.vscode-with-extensions.override {
vscode = pkgs.code-server;
vscodeExtensions =
with pkgs.vscode-extensions;
[
bbenoist.nix
catppuccin.catppuccin-vsc
catppuccin.catppuccin-vsc-icons
charliermarsh.ruff
davidanson.vscode-markdownlint
esbenp.prettier-vscode
foxundermoon.shell-format
github.copilot
github.vscode-github-actions
github.vscode-pull-request-github
jnoortheen.nix-ide
kamadorueda.alejandra
ms-azuretools.vscode-docker
ms-python.python
# ms-vscode-remote.remote-ssh
timonwong.shellcheck
tyriar.sort-lines
]
++ pkgs.vscode-utils.extensionsFromVscodeMarketplace [
{
# Available in nixpkgs, but outdated (0.4.0) at the time of adding
name = "vscode-tailscale";
publisher = "tailscale";
sha256 = "sha256-MKiCZ4Vu+0HS2Kl5+60cWnOtb3udyEriwc+qb/7qgUg=";
version = "1.0.0";
}
];
};
};
networking.firewall.allowedTCPPorts = [ config.services.code-server.port ];
};
}

View file

@ -1,109 +0,0 @@
{
lib,
config,
pkgs,
...
}:
let
waniKani-bypass-lessons = pkgs.writeShellApplication {
name = "wanikani-bypass-lessons";
runtimeInputs = with pkgs; [
curl
jq
];
text = ''
#!/usr/bin/env bash
# this token that starts with "2da24" is read only so i am keeping it public, i have nothing secret on my wanikani account
# but i need a write token for the second part of this script
# i am going to read it from /persist/wanikani
[ ! -e /persist/wanikani ] && echo "/persist/wanikani doesnt exist here :(" && exit 1
WANIKANI_TOKEN=$(< /persist/wanikani)
# Maximum number of reviews to maintain
MAX_REVIEWS=200
echo "=== Checking current reviews ==="
# Get current reviews (SRS stages 0-4)
current_reviews=0
for i in {0..4}; do
stage_count=$(curl -s -H "Authorization: Bearer 2da24e4a-ba89-4c4a-9047-d08f21e9dd01" "https://api.wanikani.com/v2/assignments?srs_stages=$i" | jq '.total_count')
current_reviews=$((current_reviews + stage_count))
echo "SRS stage $i: $stage_count items"
done
echo "Current total reviews: $current_reviews"
echo "Maximum reviews target: $MAX_REVIEWS"
if [ "$current_reviews" -ge "$MAX_REVIEWS" ]; then
echo "Reviews ($current_reviews) >= max ($MAX_REVIEWS). No lessons to bypass."
sleep 3600
exit 0
fi
lessons_to_bypass=$((MAX_REVIEWS - current_reviews))
echo "Need to bypass $lessons_to_bypass lessons to reach $MAX_REVIEWS total"
# Get available lessons (limited to what we need)
ASSIGNMENT_IDS=$(curl -s -H "Authorization: Bearer 2da24e4a-ba89-4c4a-9047-d08f21e9dd01" "https://api.wanikani.com/v2/assignments?immediately_available_for_lessons=true" | jq -r ".data[] | .id" | head -n "$lessons_to_bypass")
available_lessons=$(echo "$ASSIGNMENT_IDS" | wc -l)
echo "Available lessons: $available_lessons"
if [ "$available_lessons" -eq 0 ]; then
echo "No lessons available to bypass."
sleep 3600
exit 0
fi
# Limit to what we actually need
actual_bypass=$(echo "$ASSIGNMENT_IDS" | wc -l)
echo "Will bypass $actual_bypass lessons"
# "2017-09-05T23:41:28.980679Z" i need to create this from current time
TIME_STRING=$(date -u +"%Y-%m-%dT%H:%M:%S.%6NZ")
echo "Current time: $TIME_STRING"
echo "=== Starting assignments ==="
for assignment_id in $ASSIGNMENT_IDS; do
echo "Starting assignment $assignment_id"
curl -s "https://api.wanikani.com/v2/assignments/$assignment_id/start" \
-X "PUT" \
-H "Wanikani-Revision: 20170710" \
-H "Content-Type: application/json; charset=utf-8" \
-H "Authorization: Bearer $WANIKANI_TOKEN" \
-d "{\"assignment\": {\"started_at\": \"$TIME_STRING\" }}"
echo
sleep 1
done
echo "Successfully bypassed $actual_bypass lessons"
echo "New total should be approximately: $((current_reviews + actual_bypass))"
sleep 3600
'';
};
in
{
options.services.wanikani-bypass-lessons.enable = lib.mkEnableOption {
description = "Enable WaniKani Bypass Lessons";
default = config.osbmModules.services.wanikani-bypass-lessons.enable or false;
};
config = lib.mkIf config.services.wanikani-bypass-lessons.enable {
systemd.services.wanikani-bypass-lessons = {
description = "WaniKani Bypass Lessons";
wantedBy = [ "multi-user.target" ];
serviceConfig = {
Type = "simple";
ExecStart = "${lib.getExe waniKani-bypass-lessons}";
Restart = "always";
RestartSec = 60 * 60;
};
};
};
}

View file

@ -1,42 +0,0 @@
{
pkgs,
config,
lib,
...
}:
let
wanikani-fetcher = pkgs.writeShellApplication {
name = "wanikani-fetcher";
runtimeInputs = with pkgs; [
curl
jq
zip
];
text = builtins.readFile ./wanikani-fetcher.sh;
};
in
{
options.services.wanikani-fetch-data.enable = lib.mkEnableOption {
description = "Enable WaniKani Fetch Data";
default = config.osbmModules.services.wanikani-fetch-data.enable or false;
};
config = lib.mkIf config.services.wanikani-fetch-data.enable {
systemd.timers.wanikani-fetch-data = {
description = "WaniKani Fetch Data";
wantedBy = [ "timers.target" ];
timerConfig = {
OnCalendar = "02:00";
};
};
systemd.services.wanikani-fetch-data = {
description = "WaniKani Fetch Data";
serviceConfig = {
Type = "oneshot";
ExecStart = "${lib.getExe wanikani-fetcher}";
Restart = "on-failure";
RestartSec = 60;
};
};
};
}

View file

@ -1,82 +0,0 @@
#!/usr/bin/env bash
shopt -s nullglob
API_TOKEN="2da24e4a-ba89-4c4a-9047-d08f21e9dd01"
date=$(date +%Y-%m-%d)
# check if todays date is already in the logs folder
if [ -f "/var/lib/wanikani-logs/wanikani_data_$date.zip" ]; then
echo "Data for today already exists. Exiting..."
exit 0
fi
tmp_dir=$(mktemp -d)
echo "Temporary directory created at $tmp_dir"
mkdir "$tmp_dir/data"
mkdir -p "/var/lib/wanikani-logs"
fetch_and_merge() {
local topic="$1"
local counter=0
local url="https://api.wanikani.com/v2/$topic"
local output_file="$tmp_dir/data/$topic.json"
local next_url="$url"
echo "Fetching from $url..."
while [[ -n "$next_url" ]]; do
local resp_file="$tmp_dir/$topic-page-$counter.json"
curl -s "$next_url" \
-H "Wanikani-Revision: 20170710" \
-H "Authorization: Bearer $API_TOKEN" \
-o "$resp_file"
echo -e "\n--- Page $((counter + 1)) (First 20 lines) ---"
head -n 20 <(jq . "$resp_file")
# jq . "$resp_file" 2>/dev/null | head -n 20
next_url=$(jq -r '.pages.next_url // empty' "$resp_file")
counter=$((counter + 1))
done
echo "Merging data..."
local meta
meta=$(jq '{object, total_count, data_updated_at}' "$resp_file")
local files=("$tmp_dir/$topic-page-"*.json)
jq -cn \
--argjson meta "$meta" \
--slurpfile data <(jq -s '[.[] | .data[]]' "${files[@]}") \
'$meta + {data: $data[0]}' > "$output_file"
echo "Saved to $output_file"
}
fetch_and_merge assignments
fetch_and_merge level_progressions
fetch_and_merge resets
fetch_and_merge reviews
fetch_and_merge review_statistics
fetch_and_merge spaced_repetition_systems
fetch_and_merge study_materials
fetch_and_merge subjects
curl -s "https://api.wanikani.com/v2/summary" \
-H "Wanikani-Revision: 20170710" \
-H "Authorization: Bearer $API_TOKEN" \
-o "$tmp_dir/data/summary.json"
curl -s "https://api.wanikani.com/v2/user" \
-H "Wanikani-Revision: 20170710" \
-H "Authorization: Bearer $API_TOKEN" \
-o "$tmp_dir/data/user.json"
# get the date as a variable and use it to zip the data folder
zip -j -r "/var/lib/wanikani-logs/wanikani_data_$date.zip" "$tmp_dir/data"
echo "Data zipped to /var/lib/wanikani-logs/wanikani_data_$date.zip"
echo "Cleaning up temporary files..."
rm -r "$tmp_dir"

View file

@ -1,475 +0,0 @@
import zipfile
import json
from pathlib import Path
from flask import Flask, render_template_string, Response
import pandas as pd
import plotly.graph_objects as go
import plotly.express as px
from plotly.subplots import make_subplots
import plotly.io as pio
import functools
# Set Plotly dark theme
pio.templates.default = "plotly_dark"
app = Flask(__name__)
DATA_DIR = Path("/var/lib/wanikani-logs")
def get_zip_file_names():
"""Get a list of zip files in the data directory."""
return [f for f in DATA_DIR.glob("*.zip") if f.is_file()]
# this is an expensive function so we will cache the results
@functools.lru_cache(maxsize=None)
def load_zip(zip_path):
print(f"Processing {zip_path}")
"""Load a zip file and return its contents as a dictionary."""
with zipfile.ZipFile(zip_path, "r") as z:
data = {}
# just read summary.json
with z.open("summary.json") as f:
summary_data = json.load(f)
num_reviews = len(summary_data["data"]["reviews"][0]["subject_ids"])
num_lessons = len(summary_data["data"]["lessons"][0]["subject_ids"])
data["num_reviews"] = num_reviews
data["num_lessons"] = num_lessons
# wanikani_data_2025-05-18.zip
data["date"] = zip_path.stem.split("_")[-1].replace(".zip", "")
# with z.open("subjects.json") as f:
# subjects_data = json.load(f)
# print(f"Found total data subjects: {subjects_data['total_count']}")
# data["total_subjects"] = subjects_data['total_count']
# so the subjects.json file is about 50 mb so we are just not gonna care if this value changes (doesnt change much)
data["total_subjects"] = 9300
with z.open("assignments.json") as f:
assignments_data = json.load(f)
print(f"Found total assignments: {assignments_data['total_count']}")
data["total_assignments"] = assignments_data["total_count"]
# now the data key will give us all the srs stages
srs_stages = [0 for _ in range(10)] # 10 SRS stages
for assignment in assignments_data["data"]:
srs_stage = assignment["data"]["srs_stage"]
srs_stages[srs_stage] += 1
# add srs stages to data
for i, count in enumerate(srs_stages):
data[f"srs_stage_{i}"] = count
print(data)
return data
def get_dataframe(list_of_daily_data):
"""Convert a list of daily data dictionaries into a pandas DataFrame."""
df = pd.DataFrame(list_of_daily_data)
df["progression"] = df.apply(
lambda row: sum(row[f"srs_stage_{i}"] * (i + 1) for i in range(10))
/ (row["total_subjects"] * 10)
* 100,
axis=1,
)
df["apprentice"] = df.apply(
lambda row: row["srs_stage_1"]
+ row["srs_stage_2"]
+ row["srs_stage_3"]
+ row["srs_stage_4"],
axis=1,
)
# Individual apprentice stages for distribution analysis
df["apprentice_1"] = df["srs_stage_1"]
df["apprentice_2"] = df["srs_stage_2"]
df["apprentice_3"] = df["srs_stage_3"]
df["apprentice_4"] = df["srs_stage_4"]
df["unlocked"] = df["srs_stage_0"]
df["guru"] = df.apply(lambda row: row["srs_stage_5"] + row["srs_stage_6"], axis=1)
df["master"] = df["srs_stage_7"]
df["enlightened"] = df["srs_stage_8"]
df["burned"] = df["srs_stage_9"]
return df
def get_plotly_html(df, column, title, ylabel):
"""Generate an interactive Plotly HTML for a given DataFrame column."""
fig = go.Figure()
fig.add_trace(
go.Scatter(
x=df["date"],
y=df[column],
mode="lines+markers",
name=column.capitalize(),
line=dict(width=2),
marker=dict(size=6),
)
)
fig.update_layout(
title=title,
xaxis_title="Date",
yaxis_title=ylabel,
template="plotly_dark",
plot_bgcolor="#151519",
paper_bgcolor="#151519",
width=1200,
height=600,
margin=dict(l=50, r=50, t=50, b=50),
)
# Show every 10th date label for better readability
date_indices = list(range(0, len(df), 10))
fig.update_xaxes(
tickmode="array",
tickvals=[df.iloc[i]["date"] for i in date_indices],
ticktext=[df.iloc[i]["date"] for i in date_indices],
tickangle=45,
)
return fig.to_html(include_plotlyjs=True, div_id=f"plot_{column}")
def get_apprentice_distribution_html(df):
"""Generate a stacked area chart showing apprentice stage distribution over time."""
fig = go.Figure()
# Add stacked area traces
fig.add_trace(
go.Scatter(
x=df["date"],
y=df["apprentice_1"],
mode="lines",
name="Apprentice I",
stackgroup="one",
fillcolor="rgba(255, 107, 107, 0.8)",
line=dict(width=0.5, color="#ff6b6b"),
)
)
fig.add_trace(
go.Scatter(
x=df["date"],
y=df["apprentice_2"],
mode="lines",
name="Apprentice II",
stackgroup="one",
fillcolor="rgba(78, 205, 196, 0.8)",
line=dict(width=0.5, color="#4ecdc4"),
)
)
fig.add_trace(
go.Scatter(
x=df["date"],
y=df["apprentice_3"],
mode="lines",
name="Apprentice III",
stackgroup="one",
fillcolor="rgba(69, 183, 209, 0.8)",
line=dict(width=0.5, color="#45b7d1"),
)
)
fig.add_trace(
go.Scatter(
x=df["date"],
y=df["apprentice_4"],
mode="lines",
name="Apprentice IV",
stackgroup="one",
fillcolor="rgba(150, 206, 180, 0.8)",
line=dict(width=0.5, color="#96ceb4"),
)
)
fig.update_layout(
title="Apprentice Stage Distribution Over Time",
xaxis_title="Date",
yaxis_title="Number of Items",
template="plotly_dark",
plot_bgcolor="#151519",
paper_bgcolor="#151519",
width=1200,
height=600,
margin=dict(l=50, r=50, t=50, b=50),
legend=dict(orientation="h", yanchor="bottom", y=1.02, xanchor="right", x=1),
)
# Show every 10th date label for better readability
date_indices = list(range(0, len(df), 10))
fig.update_xaxes(
tickmode="array",
tickvals=[df.iloc[i]["date"] for i in date_indices],
ticktext=[df.iloc[i]["date"] for i in date_indices],
tickangle=45,
)
return fig.to_html(include_plotlyjs=True, div_id="apprentice_distribution")
def generate_standalone_html(df, output_path=None):
"""Generate a completely self-contained HTML file with all charts."""
# Generate all chart HTML
reviews_html = get_plotly_html(
df, "num_reviews", "Daily Reviews", "Number of Reviews"
)
lessons_html = get_plotly_html(
df, "num_lessons", "Daily Lessons", "Number of Lessons"
)
progression_html = get_plotly_html(
df, "progression", "SRS Progression", "Progression (%)"
)
apprentice_distribution_html = get_apprentice_distribution_html(df)
srs_stage_apprentice_html = get_plotly_html(
df, "apprentice", "Apprentice Stage", "Number of Subjects"
)
srs_stage_guru_html = get_plotly_html(
df, "guru", "Guru Stage", "Number of Subjects"
)
srs_stage_master_html = get_plotly_html(
df, "master", "Master Stage", "Number of Subjects"
)
srs_stage_enlightened_html = get_plotly_html(
df, "enlightened", "Enlightened Stage", "Number of Subjects"
)
srs_stage_burned_html = get_plotly_html(
df, "burned", "Burned Stage", "Number of Subjects"
)
# Create complete standalone HTML
html_content = f"""
<!DOCTYPE html>
<html>
<head>
<title>WaniKani Statistics Dashboard</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
body {{
background-color: #151519;
color: #8b8b9c;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
margin: 0;
padding: 20px;
line-height: 1.6;
}}
.chart-container {{
margin: 20px auto;
padding: 15px;
border-radius: 8px;
border: 1px solid #1e1e24;
background-color: #1a1a1f;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
}}
h1 {{
text-align: center;
color: #ffffff;
margin-bottom: 40px;
font-size: 2.5em;
font-weight: 300;
}}
.dashboard-info {{
text-align: center;
margin-bottom: 30px;
color: #888;
font-size: 0.9em;
}}
</style>
</head>
<body>
<h1>WaniKani Statistics Dashboard</h1>
<div class="dashboard-info">
Interactive dashboard showing your WaniKani learning progress over time
</div>
<div class="chart-container">{reviews_html}</div>
<div class="chart-container">{lessons_html}</div>
<div class="chart-container">{progression_html}</div>
<div class="chart-container">{apprentice_distribution_html}</div>
<div class="chart-container">{srs_stage_apprentice_html}</div>
<div class="chart-container">{srs_stage_guru_html}</div>
<div class="chart-container">{srs_stage_master_html}</div>
<div class="chart-container">{srs_stage_enlightened_html}</div>
<div class="chart-container">{srs_stage_burned_html}</div>
</body>
</html>
"""
# Save to file if output_path is provided
if output_path:
with open(output_path, "w", encoding="utf-8") as f:
f.write(html_content)
print(f"Standalone HTML dashboard saved to: {output_path}")
return html_content
@app.route("/download")
def download_dashboard():
"""Route to download a standalone HTML file."""
file_names = get_zip_file_names()
print(f"Found {len(file_names)} zip files in {DATA_DIR}")
list_of_daily_data = []
for file_name in file_names:
daily_data = load_zip(file_name)
list_of_daily_data.append(daily_data)
df = get_dataframe(list_of_daily_data)
df.sort_values(by="date", inplace=True)
html_content = generate_standalone_html(df)
response = Response(html_content, content_type="text/html")
response.headers["Content-Disposition"] = (
"attachment; filename=wanikani_dashboard.html"
)
return response
def render_html(df):
"""Render the DataFrame as HTML with interactive Plotly charts."""
reviews_html = get_plotly_html(
df, "num_reviews", "Daily Reviews", "Number of Reviews"
)
lessons_html = get_plotly_html(
df, "num_lessons", "Daily Lessons", "Number of Lessons"
)
progression_html = get_plotly_html(
df, "progression", "SRS Progression", "Progression (%)"
)
# apprentice distribution chart
apprentice_distribution_html = get_apprentice_distribution_html(df)
# srs stages
srs_stage_apprentice_html = get_plotly_html(
df, "apprentice", "Apprentice Stage", "Number of Subjects"
)
srs_stage_guru_html = get_plotly_html(
df, "guru", "Guru Stage", "Number of Subjects"
)
srs_stage_master_html = get_plotly_html(
df, "master", "Master Stage", "Number of Subjects"
)
srs_stage_enlightened_html = get_plotly_html(
df, "enlightened", "Enlightened Stage", "Number of Subjects"
)
srs_stage_burned_html = get_plotly_html(
df, "burned", "Burned Stage", "Number of Subjects"
)
# Render HTML with embedded Plotly charts
html_content = f"""
<!DOCTYPE html>
<html>
<head>
<title>WaniKani Stats</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
body {{
background-color: #151519;
color: #8b8b9c;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
margin: 0;
padding: 20px;
}}
.chart-container {{
margin: 20px auto;
padding: 10px;
border-radius: 5px;
border: 1px solid #1e1e24;
background-color: #151519;
}}
h1 {{
text-align: center;
color: #8b8b9c;
margin-bottom: 30px;
}}
</style>
</head>
<body>
<h1>WaniKani Statistics Dashboard</h1>
<div class="chart-container">{reviews_html}</div>
<div class="chart-container">{lessons_html}</div>
<div class="chart-container">{progression_html}</div>
<div class="chart-container">{apprentice_distribution_html}</div>
<div class="chart-container">{srs_stage_apprentice_html}</div>
<div class="chart-container">{srs_stage_guru_html}</div>
<div class="chart-container">{srs_stage_master_html}</div>
<div class="chart-container">{srs_stage_enlightened_html}</div>
<div class="chart-container">{srs_stage_burned_html}</div>
</body>
</html>
"""
return html_content
@app.route("/")
def index():
"""Index route"""
file_names = get_zip_file_names()
print(f"Found {len(file_names)} zip files in {DATA_DIR}")
list_of_daily_data = []
for file_name in file_names:
daily_data = load_zip(file_name)
list_of_daily_data.append(daily_data)
df = get_dataframe(list_of_daily_data)
# sort by date string
df.sort_values(by="date", inplace=True)
response = Response(render_html(df), content_type="text/html")
response.headers["Widget-Content-Type"] = "html"
response.headers["Widget-Title"] = "WaniKani Statistics"
return response
@app.route("/health")
def health():
"""Health check endpoint"""
return {"status": "ok", "service": "wanikani-stats"}
if __name__ == "__main__":
import sys
# Check if user wants to generate standalone HTML
if len(sys.argv) > 1 and sys.argv[1] == "generate":
output_file = sys.argv[2] if len(sys.argv) > 2 else "wanikani_dashboard.html"
print("Generating standalone HTML dashboard...")
file_names = get_zip_file_names()
print(f"Found {len(file_names)} zip files in {DATA_DIR}")
list_of_daily_data = []
for file_name in file_names:
daily_data = load_zip(file_name)
list_of_daily_data.append(daily_data)
df = get_dataframe(list_of_daily_data)
df.sort_values(by="date", inplace=True)
generate_standalone_html(df, output_file)
print(f"✅ Standalone HTML dashboard generated: {output_file}")
print(
"📊 You can now open this file in any web browser to view your interactive WaniKani stats!"
)
else:
# Start Flask server
port = int(sys.argv[1]) if len(sys.argv) > 1 else 8501
print(f"Starting WaniKani Stats Flask app on port {port}")
print(f"📊 View dashboard at: http://localhost:{port}")
print(f"💾 Download standalone HTML at: http://localhost:{port}/download")
app.run(host="0.0.0.0", port=port, debug=False)

View file

@ -1,115 +0,0 @@
{
pkgs,
config,
lib,
...
}:
let
python =
# let
# packageOverrides = self: super: {
# imageio = super.imageio.overridePythonAttrs (old: {
# disabledTests = [
# "test_read_stream"
# "test_uri_reading"
# "test_trim_filter"
# "test_process_termination"
# ];
# });
# plotly = super.plotly.overridePythonAttrs (old: {
# disabledTestPaths = (old.disabledTestPaths or [ ]) ++ [
# "tests/test_optional/test_kaleido/test_kaleido.py"
# ];
# });
# };
# in
pkgs.python3.override {
# inherit packageOverrides;
self = python;
};
wanikani-stats-flask = pkgs.writeShellApplication {
name = "wanikani-stats-flask";
runtimeInputs = [
(python.withPackages (
ppkgs: with ppkgs; [
flask
pandas
numpy
jinja2
matplotlib
seaborn
plotly
]
))
];
text = ''
#!/usr/bin/env bash
echo "Starting WaniKani Stats Flask app..."
exec python ${./app.py} ${toString config.services.wanikani-stats.port}
'';
};
in
{
options.services.wanikani-stats = {
enable = lib.mkEnableOption {
description = "Enable WaniKani Stats Service";
default = config.osbmModules.services.wanikani-stats.enable or false;
};
logDirectory = lib.mkOption {
type = lib.types.path;
default = "/var/lib/wanikani-logs";
description = "Directory to get the log archives";
};
port = lib.mkOption {
type = lib.types.port;
default = 8501;
description = "Port for the WaniKani Stats service";
};
};
config = lib.mkIf config.services.wanikani-stats.enable {
networking.firewall.allowedTCPPorts = [
config.services.wanikani-stats.port
];
systemd = {
services = {
wanikani-stats = {
description = "WaniKani Stats Service";
after = [ "network.target" ];
wantedBy = [ "multi-user.target" ];
serviceConfig = {
Type = "simple";
ExecStart = "${lib.getExe wanikani-stats-flask}";
StateDirectory = "/var/lib/wanikani-stats";
Restart = "on-failure";
User = "root";
Group = "root";
};
};
# Timer to restart the service every 12 hours
wanikani-stats-restart = {
description = "Restart WaniKani Stats Service";
serviceConfig = {
Type = "oneshot";
ExecStart = "${pkgs.systemd}/bin/systemctl restart wanikani-stats.service";
User = "root";
};
};
};
timers.wanikani-stats-restart = {
description = "Timer to restart WaniKani Stats Service every 12 hours";
wantedBy = [ "timers.target" ];
timerConfig = {
OnCalendar = "*-*-* 00,12:00:00";
Persistent = true;
RandomizedDelaySec = "5m";
};
};
};
};
}

View file

@ -1,59 +0,0 @@
{
lib,
pkgs,
inputs,
config,
...
}:
{
imports = [
inputs.agenix.nixosModules.default
];
config = lib.mkIf config.osbmModules.agenix.enable {
environment.systemPackages = [
inputs.agenix.packages.${pkgs.stdenv.hostPlatform.system}.agenix
pkgs.age
];
age.secrets = {
vaultwarden.file = ../../../secrets/vaultwarden.age;
network-manager.file = ../../../secrets/network-manager.age;
osbm-mail.file = ../../../secrets/osbm-mail.age;
forgejo-mail.file = ../../../secrets/forgejo-mail.age;
vaultwarden-mail.file = ../../../secrets/vaultwarden-mail.age;
noreply-mail.file = ../../../secrets/noreply-mail.age;
ssh-key-private = {
file = ../../../secrets/ssh-key-private.age;
path = "/home/osbm/.ssh/id_ed25519";
owner = "osbm";
group = "users";
mode = "600";
};
ssh-key-public = {
file = ../../../secrets/ssh-key-public.age;
path = "/home/osbm/.ssh/id_ed25519.pub";
owner = "osbm";
group = "users";
mode = "644";
};
# Deploy same SSH key to root for backups
root-ssh-key-private = {
file = ../../../secrets/ssh-key-private.age;
path = "/root/.ssh/id_ed25519";
owner = "root";
group = "root";
mode = "600";
};
root-ssh-key-public = {
file = ../../../secrets/ssh-key-public.age;
path = "/root/.ssh/id_ed25519.pub";
owner = "root";
group = "root";
mode = "644";
};
};
};
}

View file

@ -1,15 +0,0 @@
{
imports = [
./users.nix
./desktop-environment.nix
./nix-settings.nix
./agenix.nix
./home-manager.nix
./virtualisation.nix
./emulation.nix
./i18n.nix
./impermanence.nix
./fonts.nix
./nix-index.nix
];
}

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