Compare commits

..

1 commit

Author SHA1 Message Date
sid
951182f6df initial commit
All checks were successful
Flake check / flake-check (pull_request) Successful in 4m23s
2026-03-30 12:35:50 +02:00
164 changed files with 1778 additions and 3412 deletions

1
.envrc
View file

@ -1,2 +1 @@
# shellcheck shell=bash
use flake use flake

View file

@ -1,4 +1,3 @@
---
name: Build tests name: Build tests
on: on:

View file

@ -1,4 +1,3 @@
---
name: Deploy docs name: Deploy docs
on: on:

View file

@ -1,8 +1,6 @@
---
name: Flake check name: Flake check
on: on: [pull_request]
pull_request:
jobs: jobs:
flake-check: flake-check:
@ -13,8 +11,5 @@ jobs:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Check formatting
run: nix fmt -- --check
- name: Run flake check - name: Run flake check
run: nix flake check --impure --all-systems run: nix flake check --impure

View file

@ -27,7 +27,7 @@ Add this repo to your flake inputs:
inputs.synix.url = "git+https://git.sid.ovh/sid/synix.git"; inputs.synix.url = "git+https://git.sid.ovh/sid/synix.git";
``` ```
See the [documentation](https://doc.sid.ovh) for a full setup guide. See the [documentation](https://doc.sid.ovh/synix) for a full setup guide.
## Templates ## Templates

View file

@ -11,6 +11,9 @@ HOSTNAME=""
# Templates with Home Manager configurations # Templates with Home Manager configurations
HM_CONFIGS=("hyprland") HM_CONFIGS=("hyprland")
# This will get overwritten by the derivation
TEMPLATES_DIR=""
# Print usage information # Print usage information
usage() { usage() {
cat <<EOF cat <<EOF

View file

@ -6,7 +6,7 @@
home-manager, home-manager,
hostname, hostname,
nix, nix,
nixos-rebuild-ng, nixos-rebuild,
... ...
}: }:
@ -25,6 +25,6 @@ writeShellApplication {
home-manager home-manager
hostname hostname
nix nix
nixos-rebuild-ng nixos-rebuild
]; ];
} }

View file

@ -1,21 +1,15 @@
#!/usr/bin/env bash
# NixOS and standalone Home Manager rebuild script # NixOS and standalone Home Manager rebuild script
# Defaults # Defaults
FLAKE_PATH="${REBUILD_FLAKE_PATH:-${FLAKE_PATH:-$HOME/.config/nixos}}" # Default flake path FLAKE_PATH="$HOME/.config/nixos" # Default flake path
USER="${REBUILD_USER:-$(whoami)}" # Default username HOME_USER="$(whoami)" # Default username. Used to identify the Home Manager configuration
HOST="${REBUILD_HOST:-$(hostname)}" # Default hostname NIXOS_HOST="$(hostname)" # Default hostname. Used to identify the NixOS and Home Manager configuration
BUILD_HOST="${REBUILD_BUILD_HOST:-}" # Default build host BUILD_HOST="" # Default build host. Empty means localhost
TARGET_HOST="${REBUILD_TARGET_HOST:-}" # Default target host TARGET_HOST="" # Default target host. Empty means localhost
UPDATE="${REBUILD_UPDATE:-0}" # Default to not update UPDATE=0 # Default to not update flake repositories
UPDATE_INPUTS="${REBUILD_UPDATE_INPUTS:-}" # Default list of inputs UPDATE_INPUTS="" # Default list of inputs to update. Empty means all
ROLLBACK="${REBUILD_ROLLBACK:-0}" # Default to not rollback ROLLBACK=0 # Default to not rollback
SHOW_TRACE="${REBUILD_SHOW_TRACE:-0}" # Default to not show trace SHOW_TRACE=0 # Default to not show detailed error messages
# Output functions
_status() { echo -e "\033[0;34m> $1\033[0m"; }
success() { echo -e "\033[0;32m$1\033[0m"; }
error() { echo -e "\033[0;31mError: $1\033[0m" >&2; exit 1; }
# Function to display the help message # Function to display the help message
Help() { Help() {
@ -29,7 +23,7 @@ Help() {
echo " help Show this help message" echo " help Show this help message"
echo echo
echo "Options (for NixOS and Home Manager):" echo "Options (for NixOS and Home Manager):"
echo " -H, --host <host> Specify the hostname (as in 'nixosConfiguraions.<host>'). Default: $HOST" echo " -H, --host <host> Specify the hostname (as in 'nixosConfiguraions.<host>'). Default: $NIXOS_HOST"
echo " -p, --path <path> Set the path to the flake directory. Default: $FLAKE_PATH" echo " -p, --path <path> Set the path to the flake directory. Default: $FLAKE_PATH"
echo " -U, --update [inputs] Update all flake inputs. Optionally provide comma-separated list of inputs to update instead." echo " -U, --update [inputs] Update all flake inputs. Optionally provide comma-separated list of inputs to update instead."
echo " -r, --rollback Don't build the new configuration, but use the previous generation instead" echo " -r, --rollback Don't build the new configuration, but use the previous generation instead"
@ -40,50 +34,47 @@ Help() {
echo " -T, --target-host <user@example.com> Deploy the configuration to a remote host via SSH. If '--host' is specified, it will be used as the target host." echo " -T, --target-host <user@example.com> Deploy the configuration to a remote host via SSH. If '--host' is specified, it will be used as the target host."
echo echo
echo "Home Manager only options:" echo "Home Manager only options:"
echo " -u, --user <user> Specify the username (as in 'homeConfigurations.<user>@<host>'). Default: $USER" echo " -u, --user <user> Specify the username (as in 'homeConfigurations.<user>@<host>'). Default: $HOME_USER"
}
# Function to handle errors
error() {
echo "Error: $1"
exit 1
} }
# Function to rebuild NixOS configuration # Function to rebuild NixOS configuration
Rebuild_nixos() { Rebuild_nixos() {
local FLAKE="$FLAKE_PATH#$HOST" local FLAKE="$FLAKE_PATH#$NIXOS_HOST"
# Construct rebuild command # Construct rebuild command
local CMD=("nixos-rebuild" "switch" "--sudo") local CMD=("nixos-rebuild" "switch" "--sudo")
[[ -n "$TARGET_HOST" || -n "$BUILD_HOST" ]] && CMD+=("--ask-sudo-password")
CMD+=("--flake" "$FLAKE") CMD+=("--flake" "$FLAKE")
[ "$ROLLBACK" = 1 ] && CMD+=("--rollback") [ "$ROLLBACK" = 1 ] && CMD+=("--rollback")
[ "$SHOW_TRACE" = 1 ] && CMD+=("--show-trace") [ "$SHOW_TRACE" = 1 ] && CMD+=("--show-trace")
[ -n "$BUILD_HOST" ] && CMD+=("--build-host" "$BUILD_HOST") [ -n "$BUILD_HOST" ] && CMD+=("--build-host" "$BUILD_HOST")
if [ "$HOST" != "$(hostname)" ] && [ -z "$TARGET_HOST" ]; then if [ "$NIXOS_HOST" != "$(hostname)" ] && [ -z "$TARGET_HOST" ]; then
TARGET_HOST="$HOST" TARGET_HOST="$NIXOS_HOST"
_status "Using '$TARGET_HOST' as target host." echo "Using '$TARGET_HOST' as target host."
fi fi
[ -n "$TARGET_HOST" ] && CMD+=("--target-host" "$TARGET_HOST") [ -n "$TARGET_HOST" ] && CMD+=("--target-host" "$TARGET_HOST")
[[ -n "$TARGET_HOST" || -n "$BUILD_HOST" ]] && CMD+=("--ask-sudo-password")
# Build config first so we can diff it # Rebuild NixOS configuration
local BUILD_CMD=("nixos-rebuild" "build" "--no-build-output" "--flake" "$FLAKE") if [ "$ROLLBACK" = 0 ]; then
[ "$SHOW_TRACE" = 1 ] && BUILD_CMD+=("--show-trace") echo "Rebuilding NixOS configuration '$FLAKE'..."
[ -n "$BUILD_HOST" ] && BUILD_CMD+=("--build-host" "$BUILD_HOST")
if [ "$ROLLBACK" = 0 ]; then
_status "Building NixOS configuration '$FLAKE'..."
_status "Executing command: ${BUILD_CMD[*]}"
"${BUILD_CMD[@]}" || error "NixOS build failed"
_status "Switching to new NixOS configuration"
else else
_status "Rolling back to last NixOS generation" echo "Rolling back to last NixOS generation..."
fi fi
sudo -v echo "Executing command: ${CMD[*]}"
_status "Executing command: ${CMD[*]}"
"${CMD[@]}" || error "NixOS rebuild failed" "${CMD[@]}" || error "NixOS rebuild failed"
success "NixOS rebuild completed successfully." echo "NixOS rebuild completed successfully."
} }
# Function to rebuild Home Manager configuration # Function to rebuild Home Manager configuration
Rebuild_home() { Rebuild_home() {
local FLAKE="$FLAKE_PATH#$USER@$HOST" local FLAKE="$FLAKE_PATH#$HOME_USER@$NIXOS_HOST"
if [ -n "$BUILD_HOST" ] || [ -n "$TARGET_HOST" ]; then if [ -n "$BUILD_HOST" ] || [ -n "$TARGET_HOST" ]; then
error "Remote building is not supported for Home Manager." error "Remote building is not supported for Home Manager."
@ -100,27 +91,21 @@ Rebuild_home() {
[ "$SHOW_TRACE" = 1 ] && CMD+=("--show-trace") [ "$SHOW_TRACE" = 1 ] && CMD+=("--show-trace")
fi fi
# Build config first so we can diff it # Rebuild Home Manager configuration
if [ "$ROLLBACK" = 0 ]; then if [ "$ROLLBACK" = 0 ]; then
local BUILD_CMD=("home-manager" "build" "--no-out-link" "--flake" "$FLAKE") echo "Rebuilding Home Manager configuration '$FLAKE'..."
[ "$SHOW_TRACE" = 1 ] && BUILD_CMD+=("--show-trace")
_status "Building Home Manager configuration '$FLAKE'..."
_status "Executing command: ${BUILD_CMD[*]}"
"${BUILD_CMD[@]}" || error "Home Manager build failed"
_status "Switching to new Home Manager configuration"
else else
_status "Rolling back to last Home Manager generation" echo "Rolling back to last Home Manager generation..."
fi fi
_status "Executing command: ${CMD[*]}" echo "Executing command: ${CMD[*]}"
"${CMD[@]}" || error "Home Manager rebuild failed" "${CMD[@]}" || error "Home Manager rebuild failed"
success "Home Manager rebuild completed successfully." echo "Home Manager rebuild completed successfully."
} }
# Function to update flake repositories # Function to Update flake repositories
Update() { Update() {
_status "Updating flake inputs..." echo "Updating flake inputs..."
# Construct update command as an array # Construct update command as an array
local CMD=("nix" "flake" "update" "--flake" "$FLAKE_PATH") local CMD=("nix" "flake" "update" "--flake" "$FLAKE_PATH")
@ -132,18 +117,17 @@ Update() {
done done
fi fi
_status "Executing command: ${CMD[*]}" echo "Executing command: ${CMD[*]}"
"${CMD[@]}" || error "Failed to update flake repositories" "${CMD[@]}" || error "Failed to update flake repositories"
success "Flake repositories updated successfully." echo "Flake repositories updated successfully."
} }
# Parse command-line options # Parse command-line options
if [[ -z "${1:-}" ]]; then if [[ -z "${1:-}" ]]; then
echo -e "\033[0;31mError: No command specified. Printing help page.\033[0m" >&2 echo "Error: No command specified. Printing help page."
Help Help
exit 1 exit 1
fi fi
COMMAND=$1 COMMAND=$1
shift shift
@ -157,7 +141,7 @@ while [ $# -gt 0 ]; do
case "${1:-}" in case "${1:-}" in
-H|--host) -H|--host)
if [ -n "${2:-}" ]; then if [ -n "${2:-}" ]; then
HOST="$2" NIXOS_HOST="$2"
shift 2 shift 2
else else
error "-H|--host option requires an argument" error "-H|--host option requires an argument"
@ -165,7 +149,7 @@ while [ $# -gt 0 ]; do
;; ;;
-u|--user) -u|--user)
if [ -n "${2:-}" ]; then if [ -n "${2:-}" ]; then
USER="$2" HOME_USER="$2"
shift 2 shift 2
else else
error "-u|--user option requires an argument" error "-u|--user option requires an argument"
@ -214,7 +198,9 @@ while [ $# -gt 0 ]; do
fi fi
;; ;;
*) *)
error "Unknown option '$1'" echo "Error: Unknown option '$1'"
Help
exit 1
;; ;;
esac esac
done done
@ -241,10 +227,10 @@ fi
[ "$UPDATE" = 1 ] && Update [ "$UPDATE" = 1 ] && Update
case "$COMMAND" in case "$COMMAND" in
nixos|os) nixos)
Rebuild_nixos Rebuild_nixos
;; ;;
home|hm) home)
Rebuild_home Rebuild_home
;; ;;
all) all)
@ -252,6 +238,9 @@ case "$COMMAND" in
Rebuild_home Rebuild_home
;; ;;
*) *)
error "Unknown command '$COMMAND'" echo "Error: Unknown command '$COMMAND'"
echo "Printing help page:"
Help
exit 1
;; ;;
esac esac

View file

@ -1,4 +1,3 @@
#!/usr/bin/env bash
SYSTEM="x86_64-linux" SYSTEM="x86_64-linux"
IGNORE_PACKAGES=( IGNORE_PACKAGES=(
"pyman" "pyman"

View file

@ -114,8 +114,7 @@ git clone YOUR_GIT_REPO_URL ~/.config/nixos
Home Manager is not installed by default. Enter the development shell to apply the configuration: Home Manager is not installed by default. Enter the development shell to apply the configuration:
```bash ```bash
nix --experimental-features "nix-command flakes" develop git+https://git.sid.ovh/sid/synix#devShells.x86_64-linux.install-hm \ nix-shell ~/.config/nixos/shell.nix --run 'rebuild home'
--command "rebuild home"
``` ```
### 8. Reboot your System ### 8. Reboot your System

View file

@ -19,7 +19,7 @@ Provide the following entries to your `secrets.yaml`:
```yaml ```yaml
headplane: headplane:
cookie_secret: abc123 cookie_secret: abc123
api_key: abc123 agent_pre_authkey: abc123
``` ```
Generate your cookie secret with: Generate your cookie secret with:
@ -28,7 +28,7 @@ Generate your cookie secret with:
nix-shell -p openssl --run "openssl rand -hex 16" nix-shell -p openssl --run "openssl rand -hex 16"
``` ```
Generate your agent `api_key` with: Generate your agent pre-authkey with:
```bash ```bash
sudo headscale users create headplane-agent sudo headscale users create headplane-agent

View file

@ -0,0 +1,36 @@
# Tailscale
Private WireGuard networks made easy.
View the [*synix* NixOS module on Forgejo](https://git.sid.ovh/sid/synix/tree/master/modules/nixos/tailscale).
## References
- [Website](https://tailscale.com/)
- [GitHub](https://github.com/tailscale/tailscale)
- [Documents](https://tailscale.com/kb/1017/install)
## Sops
Provide the following entries to your `secrets.yaml`:
> Replace `abc123` with your actual secrets
```yaml
tailscale:
auth-key: abc123
```
## Config
```nix
{
imports = [ inputs.synix.nixosModules.tailscale ];
services.tailscale = {
enable = true;
enableSSH = true;
loginServer = "<your-headscale-instance>";
};
}
```

36
flake.lock generated
View file

@ -202,11 +202,11 @@
}, },
"flake-schemas": { "flake-schemas": {
"locked": { "locked": {
"lastModified": 1776384880, "lastModified": 1770913512,
"narHash": "sha256-28Cg9HI/IwFHUm5fZyMEGCQ94L3Il6K4OfHeRf0b+Zw=", "narHash": "sha256-jRC1qRoRCrMjDalVfUMHFlKSkkA2q0RZWTDW0LsquoA=",
"owner": "DeterminateSystems", "owner": "DeterminateSystems",
"repo": "flake-schemas", "repo": "flake-schemas",
"rev": "b852751d29c6e09919f25c1e10754646bdc181c9", "rev": "3c464b0e09cb44c6e073c41dd1d834980c3e0b24",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -258,11 +258,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1778507602, "lastModified": 1772024342,
"narHash": "sha256-kTwur1wV+01SdqskVMSo6JMEpg71ps3HpbFY2GsflKs=", "narHash": "sha256-+eXlIc4/7dE6EcPs9a2DaSY3fTA9AE526hGqkNID3Wg=",
"owner": "cachix", "owner": "cachix",
"repo": "git-hooks.nix", "repo": "git-hooks.nix",
"rev": "61ab0e80d9c7ab14c256b5b453d8b3fb0189ba0a", "rev": "6e34e97ed9788b17796ee43ccdbaf871a5c2b476",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -349,11 +349,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1779506708, "lastModified": 1772020340,
"narHash": "sha256-QOD/CNm196nCJRheux/URi4/HE66fthdOMqCJoPP1Y0=", "narHash": "sha256-aqBl3GNpCadMoJ/hVkWTijM1Aeilc278MjM+LA3jK6g=",
"owner": "nix-community", "owner": "nix-community",
"repo": "home-manager", "repo": "home-manager",
"rev": "3ee51fbdac8c8bdfe1e7e1fcaba6520a563f394f", "rev": "36e38ca0d9afe4c55405fdf22179a5212243eecc",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -465,11 +465,11 @@
}, },
"nixpkgs_2": { "nixpkgs_2": {
"locked": { "locked": {
"lastModified": 1779467186, "lastModified": 1771903837,
"narHash": "sha256-nOesoDCiXcUftqbRBMz9tt4blI5PvljMWbm3kuCA+0s=", "narHash": "sha256-sdaqdnsQCv3iifzxwB22tUwN/fSHoN7j2myFW5EIkGk=",
"owner": "nixos", "owner": "nixos",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "b77b3de8775677f84492abe84635f87b0e153f0f", "rev": "e764fc9a405871f1f6ca3d1394fb422e0a0c3951",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -511,11 +511,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1779900588, "lastModified": 1772140017,
"narHash": "sha256-b/yda4uMmjpw4uhXI4d0JNv09WtGoXis2JjD5l1Qbts=", "narHash": "sha256-wFUc9tn5Ik11oL009BkVnj4NxsY63UbZjhOyEqCsIQE=",
"owner": "nix-community", "owner": "nix-community",
"repo": "NUR", "repo": "NUR",
"rev": "dd8bdde89853bfb3eae0eb28e3d3a9320b3b8a3c", "rev": "be0fea0af0e8cfadb3995dd7bb3a167bc012e935",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -605,11 +605,11 @@
"tinted-zed": "tinted-zed" "tinted-zed": "tinted-zed"
}, },
"locked": { "locked": {
"lastModified": 1778680496, "lastModified": 1771788390,
"narHash": "sha256-tUq1WASV0dHLv3j18log8V6Esq0NYkXuzNH2EHsstcg=", "narHash": "sha256-RzBpBwn93GWxLjacTte+ngwwg0L/BVOg4G/sSIeK3Rw=",
"owner": "nix-community", "owner": "nix-community",
"repo": "stylix", "repo": "stylix",
"rev": "fc5bec2e44678eeaa221d566d447a0257a884737", "rev": "ebb238f14d6f930068be4718472da3105fd5d3bf",
"type": "github" "type": "github"
}, },
"original": { "original": {

224
flake.nix
View file

@ -3,6 +3,7 @@
nixpkgs.url = "github:nixos/nixpkgs/nixos-25.11"; nixpkgs.url = "github:nixos/nixpkgs/nixos-25.11";
nix.url = "github:DeterminateSystems/nix-src/flake-schemas"; nix.url = "github:DeterminateSystems/nix-src/flake-schemas";
flake-schemas.url = "github:DeterminateSystems/flake-schemas"; flake-schemas.url = "github:DeterminateSystems/flake-schemas";
git-hooks.url = "github:cachix/git-hooks.nix"; git-hooks.url = "github:cachix/git-hooks.nix";
@ -28,69 +29,50 @@
... ...
}@inputs: }@inputs:
let let
inherit (self) outputs; supportedSystems = [
systems = [
"x86_64-linux" "x86_64-linux"
"aarch64-linux" # For testing only. Use at your own risk. "aarch64-linux" # For testing only. Use at your own risk.
]; ];
lib = nixpkgs.lib.extend (_final: _prev: { inherit (self.lib) utils; }); forAllSystems = nixpkgs.lib.genAttrs supportedSystems;
mkPkgs = nixpkgsFor = forAllSystems (
{ system:
system,
config ? { },
}:
import nixpkgs { import nixpkgs {
inherit system; inherit system;
inherit config;
overlays = [ overlays = [
self.overlays.modifications self.overlays.default
self.overlays.additions
inputs.nix.overlays.default inputs.nix.overlays.default
(final: prev: { synix = self.overlays.additions final prev; })
]; ];
}; }
);
forAllSystems = test = {
function: system = "x86_64-linux";
lib.genAttrs systems ( lib = nixpkgs.lib.extend (final: prev: self.outputs.lib or { });
system: inputs = inputs // {
function (mkPkgs { synix = self;
inherit system;
})
);
mkNixosConfiguration =
system: modules:
nixpkgs.lib.nixosSystem {
inherit system modules;
specialArgs = {
inherit inputs outputs lib;
};
};
mkHomeConfiguration =
system: modules:
inputs.home-manager.lib.homeManagerConfiguration {
pkgs = mkPkgs { inherit system; };
inherit modules;
extraSpecialArgs = {
inherit inputs outputs;
};
}; };
outputs = { };
overlays = [
self.overlays.default
self.overlays.additions
self.overlays.modifications
(final: prev: { synix = self.packages."${final.system}"; })
];
};
in in
{ {
inherit (inputs.flake-schemas) schemas; inherit (inputs.flake-schemas) schemas;
apps = forAllSystems ( apps = forAllSystems (
pkgs: system:
let let
mkApp = name: description: { pkgs = nixpkgs.legacyPackages.${system};
mkApp = name: desc: {
type = "app"; type = "app";
program = pkgs.lib.getExe (pkgs.callPackage ./apps/${name} { }); program = pkgs.lib.getExe (pkgs.callPackage ./apps/${name} { });
meta.description = description; meta.description = desc;
}; };
in in
{ {
@ -104,17 +86,13 @@
); );
lib = { lib = {
utils = import ./lib/utils.nix { inherit (nixpkgs) lib; }; utils = import ./lib/utils.nix { lib = nixpkgs.lib; };
helpers = {
inherit mkPkgs;
};
}; };
packages = forAllSystems ( packages = forAllSystems (
pkgs: system:
let let
inherit (pkgs.stdenv.hostPlatform) system; allArchs = import ./pkgs { pkgs = nixpkgs.legacyPackages.${system}; };
allArchs = import ./pkgs { inherit pkgs; };
x64only = x64only =
if system == "x86_64-linux" then if system == "x86_64-linux" then
{ {
@ -133,97 +111,125 @@
# test configs # test configs
nixosConfigurations = { nixosConfigurations = {
nixos-hyprland = mkNixosConfiguration "x86_64-linux" [ ./tests/build/nixos-hyprland ]; nixos-hyprland = nixpkgs.lib.nixosSystem {
nixos-server = mkNixosConfiguration "x86_64-linux" [ ./tests/build/nixos-server ]; inherit (test) system;
modules = [
./tests/build/nixos-hyprland
{ nixpkgs.overlays = test.overlays; }
];
specialArgs = {
inherit (test) inputs outputs lib;
};
};
nixos-server = nixpkgs.lib.nixosSystem {
inherit (test) system;
modules = [
./tests/build/nixos-server
{ nixpkgs.overlays = test.overlays; }
];
specialArgs = {
inherit (test) inputs outputs lib;
};
};
}; };
homeConfigurations = { homeConfigurations = {
hm-hyprland = mkHomeConfiguration "x86_64-linux" [ ./tests/build/hm-hyprland ]; hm-hyprland = inputs.home-manager.lib.homeManagerConfiguration {
pkgs = import nixpkgs {
inherit (test) overlays system;
};
extraSpecialArgs = {
inherit (test) inputs outputs;
};
modules = [
./tests/build/hm-hyprland
];
};
}; };
devShells = forAllSystems ( devShells = forAllSystems (
pkgs: system:
let let
inherit (pkgs.stdenv.hostPlatform) system; pkgs = nixpkgsFor.${system};
inherit (self.checks.${system}.pre-commit-check) shellHook enabledPackages;
in in
{ {
default = pkgs.mkShell { default =
inherit shellHook; let
nativeBuildInputs = [ inherit (self.checks.${system}.pre-commit-check) shellHook enabledPackages;
enabledPackages in
pkgs.nix pkgs.mkShell {
] inherit shellHook;
++ (with pkgs; [ nativeBuildInputs = [
(python313.withPackages ( enabledPackages
p: with p; [ pkgs.nix
mkdocs ]
mkdocs-material ++ (with pkgs; [
mkdocs-material-extensions (python313.withPackages (
pygments p: with p; [
] mkdocs
)) mkdocs-material
]); mkdocs-material-extensions
}; pygments
nix-config = pkgs.mkShell { ]
inherit shellHook; ))
nativeBuildInputs = [ enabledPackages ]; ]);
}; };
install-hm = pkgs.mkShell {
NIX_CONFIG = "extra-experimental-features = nix-command flakes";
nativeBuildInputs = [ pkgs.home-manager ];
};
} }
); );
formatter = forAllSystems ( formatter = forAllSystems (
pkgs: system:
let let
inherit (pkgs.stdenv.hostPlatform) system; pkgs = nixpkgs.legacyPackages.${system};
inherit (self.checks.${system}.pre-commit-check.config) package configFile; config = self.checks.${system}.pre-commit-check.config;
inherit (config) package configFile;
script = ''
${pkgs.lib.getExe package} run --all-files --config ${configFile}
'';
in in
pkgs.writeShellScriptBin "pre-commit-run" "${pkgs.lib.getExe package} run --all-files --config ${configFile}" pkgs.writeShellScriptBin "pre-commit-run" script
); );
checks = forAllSystems ( checks = forAllSystems (
pkgs: system:
let let
inherit (pkgs.stdenv.hostPlatform) system; pkgs = nixpkgs.legacyPackages.${system};
flakePkgs = self.packages.${system};
overlaidPkgs = import nixpkgs {
inherit system;
overlays = [ self.overlays.modifications ];
};
in in
{ {
pre-commit-check = inputs.git-hooks.lib.${system}.run { pre-commit-check = inputs.git-hooks.lib.${system}.run {
src = ./.; src = ./.;
hooks = { hooks = {
actionlint.enable = true; nixfmt.enable = true;
nixfmt = {
enable = true;
settings.width = 120;
};
shellcheck.enable = true;
statix.enable = true;
yamllint = {
enable = true;
excludes = [ "secrets.yaml" ];
settings.configData = "{rules: {line-length: {max: 120}}}";
};
}; };
}; };
build-packages = pkgs.linkFarm "flake-packages-${system}" flakePkgs;
build-additions = pkgs.linkFarm "added-packages-${system}" self.packages.${system}; build-overlays = pkgs.linkFarm "flake-overlays-${system}" {
build-modifications = pkgs.linkFarm "modified-packages-${system}" ( kicad = overlaidPkgs.kicad;
lib.filterAttrs (_: v: lib.isDerivation v) (self.overlays.modifications pkgs pkgs) };
);
synapse-test = synapse-test =
let let
testPkgs = mkPkgs { testPkgs = import nixpkgs {
inherit system; inherit system;
config.permittedInsecurePackages = [ "olm-3.2.16" ]; config.permittedInsecurePackages = [ "olm-3.2.16" ];
}; };
in in
testPkgs.testers.runNixOSTest ./tests/run/synapse.nix; testPkgs.testers.runNixOSTest ./tests/run/synapse.nix;
open-webui-oci-test = pkgs.testers.runNixOSTest ./tests/run/open-webui-oci.nix;
# librechat-oci-test = pkgs.testers.runNixOSTest ./tests/run/librechat-oci.nix; # FIXME: unable to copy from source docker://quay.io/mongo:7.0 # NOTE: disabled for now since the test takes too long to execute
# open-webui-oci-test =
# let
# testPkgs = import nixpkgs {
# inherit system;
# };
# in
# testPkgs.testers.runNixOSTest ./tests/run/open-webui-oci.nix;
} }
); );
hydraJobs = { hydraJobs = {
@ -283,10 +289,6 @@
path = ./templates/dev/rs-hello; path = ./templates/dev/rs-hello;
description = "Rust hello world template."; description = "Rust hello world template.";
}; };
stm32-blink = {
path = ./templates/dev/stm32-blink;
description = "STM32G4 blink template with libopencm3.";
};
}; };
}; };
} }

View file

@ -1,4 +1,3 @@
---
site_name: synix docs site_name: synix docs
repo_url: https://git.sid.ovh/sid/synix repo_url: https://git.sid.ovh/sid/synix
site_url: https://doc.sid.ovh/synix site_url: https://doc.sid.ovh/synix
@ -21,7 +20,7 @@ markdown_extensions:
- pymdownx.superfences - pymdownx.superfences
nav: nav:
- Home: index.md # do not change - Home: index.md # do not change
- Introduction to Nix: - Introduction to Nix:
- Overview: introduction-to-nix/overview.md - Overview: introduction-to-nix/overview.md
- Install Nix: introduction-to-nix/install-nix.md - Install Nix: introduction-to-nix/install-nix.md
@ -59,6 +58,7 @@ nav:
- radicale: modules/nixos/radicale.md - radicale: modules/nixos/radicale.md
- rss-bridge: modules/nixos/rss-bridge.md - rss-bridge: modules/nixos/rss-bridge.md
- sops: modules/nixos/sops.md - sops: modules/nixos/sops.md
- tailscale: modules/nixos/tailscale.md
- virtualisation: modules/nixos/virtualisation.md - virtualisation: modules/nixos/virtualisation.md
- webPage: modules/nixos/webpage.md - webPage: modules/nixos/webpage.md
- Home Manager: - Home Manager:

View file

@ -1,4 +1,3 @@
#!/usr/bin/env bash
# change directory with fzf # change directory with fzf
# Usage: cdf [optional_relative_path] # Usage: cdf [optional_relative_path]
# - If no argument, searches from $HOME. # - If no argument, searches from $HOME.
@ -68,8 +67,7 @@ function cdf() {
"--preview=tree -C {} | head -50" "--preview=tree -C {} | head -50"
"--preview-window=right:50%:wrap" "--preview-window=right:50%:wrap"
) )
local selected local selected=$(find "${find_args[@]}" 2>/dev/null | fzf "${fzf_args[@]}")
selected=$(find "${find_args[@]}" 2>/dev/null | fzf "${fzf_args[@]}")
if [[ -n "$selected" ]]; then if [[ -n "$selected" ]]; then
cd "$selected" || echo "Failed to cd into '$selected'" cd "$selected" || echo "Failed to cd into '$selected'"

View file

@ -1,48 +0,0 @@
{
inputs,
config,
lib,
pkgs,
...
}:
let
cfg = config.wayland.windowManager.hyprland;
app = cfg.applications.password-manager.default;
inherit (lib) mkDefault mkIf;
in
{
imports = [ ../../../rofi-rbw ];
config = mkIf (cfg.enable && app == "rofi-rbw") {
programs = {
rbw = {
enable = true;
settings = {
# email = "you@example.tld"; # You have to set this in your config
pinentry = mkDefault pkgs.pinentry-gnome3;
lock_timeout = mkDefault 3600;
};
};
rofi-rbw = {
enable = true;
package = mkDefault pkgs.rofi-rbw-wayland;
settings = {
selector = mkDefault "bemenu";
selector-args = mkDefault "-i -l 20";
action = mkDefault "copy";
typing-key-delay = mkDefault 0;
};
};
librewolf = mkIf config.programs.librewolf.enable {
profiles.default.extensions.packages =
with inputs.nur.legacyPackages."${pkgs.stdenv.hostPlatform.system}".repos.rycee.firefox-addons; [
bitwarden
];
};
};
};
}

View file

@ -22,7 +22,7 @@ let
{ {
default = mkOption { default = mkOption {
type = types.str; type = types.str;
inherit default; default = default;
description = "The default application to use for the ${default}."; description = "The default application to use for the ${default}.";
}; };
bind = mkOption { bind = mkOption {
@ -38,8 +38,12 @@ let
}; };
# generate lists of all binds and window rules and remove empty strings # generate lists of all binds and window rules and remove empty strings
binds = filter (s: s != "") (builtins.concatLists (map (app: app.bind or [ "" ]) (attrValues apps))); binds = filter (s: s != "") (
windowrules = filter (s: s != "") (builtins.concatLists (map (app: app.windowrule or [ "" ]) (attrValues apps))); builtins.concatLists (map (app: app.bind or [ "" ]) (attrValues apps))
);
windowrules = filter (s: s != "") (
builtins.concatLists (map (app: app.windowrule or [ "" ]) (attrValues apps))
);
inherit (lib) inherit (lib)
attrValues attrValues
@ -52,10 +56,8 @@ let
in in
{ {
imports = [ imports = [
./bitwarden
./bemenu ./bemenu
./dmenu-bluetooth ./dmenu-bluetooth
./dunst-toggle
./element-desktop ./element-desktop
./feh ./feh
./kitty ./kitty
@ -133,11 +135,6 @@ in
bind = [ "$mod SHIFT, m, exec, ${terminal} -T ${musicplayer} -e ${musicplayer}" ]; bind = [ "$mod SHIFT, m, exec, ${terminal} -T ${musicplayer} -e ${musicplayer}" ];
}; };
notifications = mkAppAttrs {
default = "dunst-toggle";
bind = [ "$mod, Backspace, exec, ${notifications}" ];
};
networksettings = mkAppAttrs { networksettings = mkAppAttrs {
default = "networkmanager_dmenu"; default = "networkmanager_dmenu";
bind = [ "$mod SHIFT, n, exec, ${networksettings}" ]; bind = [ "$mod SHIFT, n, exec, ${networksettings}" ];

View file

@ -1,18 +0,0 @@
{
config,
lib,
pkgs,
...
}:
let
cfg = config.wayland.windowManager.hyprland;
app = cfg.applications.notifications.default;
inherit (lib) mkIf;
in
{
config = mkIf (cfg.enable && app == "dunst-toggle") {
home.packages = [ (import ./dunst-toggle.nix { inherit config pkgs; }) ];
};
}

View file

@ -1,11 +0,0 @@
{ config, pkgs, ... }:
let
dunst = "${pkgs.dunst}/bin/dunstctl";
pkill = "${pkgs.procps}/bin/pkill";
signal = "${toString config.programs.waybar.settings.mainBar."custom/notifications".signal}";
in
pkgs.writeShellScriptBin "dunst-toggle" ''
${dunst} set-paused toggle
${pkill} -RTMIN+${signal} waybar
''

View file

@ -9,7 +9,7 @@ let
cfg = config.wayland.windowManager.hyprland; cfg = config.wayland.windowManager.hyprland;
app = cfg.applications.rssreader.default; app = cfg.applications.rssreader.default;
reloadTime = "${toString config.programs.newsboat.reloadTime}"; reloadTime = "${toString config.programs.newsboat.reloadTime}";
newsboat-reload = import ./newsboat-reload.nix { inherit config pkgs; }; newsboat-reload = (import ./newsboat-reload.nix { inherit config pkgs; });
inherit (lib) mkIf; inherit (lib) mkIf;
in in
@ -27,11 +27,9 @@ in
timers.newsboat-reload = { timers.newsboat-reload = {
Unit.Description = "Reload newsboat every ${reloadTime} minutes"; Unit.Description = "Reload newsboat every ${reloadTime} minutes";
Timer = { Timer.OnBootSec = "10sec";
OnBootSec = "10sec"; Timer.OnUnitActiveSec = "${reloadTime}min";
OnUnitActiveSec = "${reloadTime}min"; Timer.Unit = "newsboat-reload.service";
Unit = "newsboat-reload.service";
};
Install.WantedBy = [ "timers.target" ]; Install.WantedBy = [ "timers.target" ];
}; };

View file

@ -5,6 +5,6 @@ let
notify = "${pkgs.libnotify}/bin/notify-send"; notify = "${pkgs.libnotify}/bin/notify-send";
signal = "${toString config.programs.waybar.settings.mainBar."custom/newsboat".signal}"; signal = "${toString config.programs.waybar.settings.mainBar."custom/newsboat".signal}";
in in
pkgs.writeShellScriptBin "newsboat-reload" '' (pkgs.writeShellScriptBin "newsboat-reload" ''
${notify} -u low 'Newsboat' 'Reloading RSS feeds...' && ${newsboat} -x reload && ${notify} -u low 'Newsboat' 'RSS feeds reloaded.' && pkill -RTMIN+${signal} waybar ${notify} -u low 'Newsboat' 'Reloading RSS feeds...' && ${newsboat} -x reload && ${notify} -u low 'Newsboat' 'RSS feeds reloaded.' && pkill -RTMIN+${signal} waybar
'' '')

View file

@ -14,7 +14,9 @@ in
{ {
config = mkIf (cfg.enable && app == "presentation-mode-bemenu") { config = mkIf (cfg.enable && app == "presentation-mode-bemenu") {
home.packages = [ home.packages = [
(pkgs.writeShellScriptBin "presentation-mode-bemenu" (builtins.readFile ./presentation-mode-bemenu.sh)) (pkgs.writeShellScriptBin "presentation-mode-bemenu" (
builtins.readFile ./presentation-mode-bemenu.sh
))
]; ];
}; };
} }

View file

@ -1,6 +1,5 @@
#!/usr/bin/env bash
# Variables # Variables
mapfile -t DISPLAYS < <(hyprctl monitors | grep -E '^Monitor' | awk '{print $2}') DISPLAYS=( $(hyprctl monitors | grep -E '^Monitor' | awk '{print $2}') )
EXTEND_RIGHT="Extend to right of main" EXTEND_RIGHT="Extend to right of main"
EXTEND_LEFT="Extend to left of main" EXTEND_LEFT="Extend to left of main"
MIRROR="Mirror main" MIRROR="Mirror main"

View file

@ -5,7 +5,7 @@
let let
screenshotDir = "${config.xdg.userDirs.pictures}/screenshots"; screenshotDir = "${config.xdg.userDirs.pictures}/screenshots";
in in
pkgs.writeShellScriptBin "screenshot" '' (pkgs.writeShellScriptBin "screenshot" ''
mkdir -p ${screenshotDir} mkdir -p ${screenshotDir}
${pkgs.hyprshot}/bin/hyprshot --mode $1 --output-folder ${screenshotDir} --filename screenshot_$(date +"%Y-%m-%d_%H-%M-%S").png ${pkgs.hyprshot}/bin/hyprshot --mode $1 --output-folder ${screenshotDir} --filename screenshot_$(date +"%Y-%m-%d_%H-%M-%S").png
'' '')

View file

@ -27,7 +27,7 @@ in
wayland.windowManager.hyprland = { wayland.windowManager.hyprland = {
settings = { settings = {
bind = binds; bind = binds;
bindm = import ./mouse.nix; bindm = (import ./mouse.nix);
}; };
}; };
}; };

View file

@ -9,19 +9,17 @@ let
inherit (lib) mkForce; inherit (lib) mkForce;
in in
{ {
home = { home.pointerCursor = {
pointerCursor = { name = mkForce "Bibata-Original-Ice";
name = mkForce "Bibata-Original-Ice"; size = mkForce 24;
size = mkForce 24; package = mkForce pkgs.bibata-cursors;
package = mkForce pkgs.bibata-cursors; };
};
packages = [ pkgs.hyprcursor ]; home.packages = [ pkgs.hyprcursor ];
sessionVariables = { home.sessionVariables = {
HYPRCURSOR_THEME = config.home.pointerCursor.name; HYPRCURSOR_THEME = config.home.pointerCursor.name;
HYPRCURSOR_SIZE = toString config.home.pointerCursor.size; HYPRCURSOR_SIZE = toString config.home.pointerCursor.size;
};
}; };
# wayland.windowManager.hyprland.cursor.no_hardware_cursors = true; # wayland.windowManager.hyprland.cursor.no_hardware_cursors = true;

View file

@ -78,6 +78,12 @@ in
# auto discover fonts in `home.packages` # auto discover fonts in `home.packages`
fonts.fontconfig.enable = true; fonts.fontconfig.enable = true;
# notifications
services.dunst = {
enable = mkDefault true;
waylandDisplay = config.home.sessionVariables.WAYLAND_DISPLAY;
};
# install some applications # install some applications
home.packages = import ./packages.nix { inherit pkgs; }; # use programs.PACKAGE or services.SERVICE when possible home.packages = import ./packages.nix { inherit pkgs; }; # use programs.PACKAGE or services.SERVICE when possible
@ -88,19 +94,11 @@ in
fi fi
''; '';
# notifications services.udiskie = {
services = { enable = mkDefault true;
dunst = { tray = mkDefault "never";
enable = mkDefault true;
waylandDisplay = config.home.sessionVariables.WAYLAND_DISPLAY;
};
udiskie = {
enable = mkDefault true;
tray = mkDefault "never";
};
network-manager-applet.enable = mkDefault true;
}; };
services.network-manager-applet.enable = mkDefault true;
}; };
} }

View file

@ -3,30 +3,15 @@
let let
cfg = config.wayland.windowManager.hyprland; cfg = config.wayland.windowManager.hyprland;
nonCenterFloatingClasses = [ inherit (builtins) toString;
"Gimp"
"steam"
"KiCad"
];
nonCenterFloatingClassesRegex = concatStringsSep "|" nonCenterFloatingClasses;
inherit (builtins) concatStringsSep toString;
inherit (lib) mkDefault; inherit (lib) mkDefault;
in in
{ {
# Do not add binds here. Use `./binds/default.nix` instead. # Do not add binds here. Use `./binds/default.nix` instead.
"$mod" = cfg.modifier; "$mod" = cfg.modifier;
exec-once = [
"dbus-update-activation-environment --systemd WAYLAND_DISPLAY XDG_CURRENT_DESKTOP" # dbus package comes from NixOS module option `services.dbus.dbusPackage` [1]
"gnome-keyring-daemon --start --components=secrets" # gnome-keyring package comes from NixOS module `services.gnome.gnome-keyring` [1]
];
# 1: see Hyprland NixOS module
windowrule = [ windowrule = [
# "float, class:^(${nonCenterFloatingClassesRegex})$" "center, floating:1, not class:^(Gimp)$, not class:^(steam)$"
"center, floating:1, class:^(?!.*(${nonCenterFloatingClassesRegex})).*$"
"float, title:^(Open|Save) Files?$" "float, title:^(Open|Save) Files?$"
"noborder, onworkspace:w[t1]" "noborder, onworkspace:w[t1]"
"bordersize ${toString cfg.settings.general.border_size}, floating:1" "bordersize ${toString cfg.settings.general.border_size}, floating:1"
@ -39,12 +24,6 @@ in
"noblur, class:^(xwaylandvideobridge)$" "noblur, class:^(xwaylandvideobridge)$"
]; ];
gesture = [
"3, horizontal, workspace"
"3, up, dispatcher, exec, bemenu-run" # TODO: move to hyprland.applications
"4, swipe, move"
];
# Layouts # Layouts
general.layout = mkDefault "master"; general.layout = mkDefault "master";
master = { master = {

View file

@ -20,10 +20,8 @@ in
enable = mkDefault true; enable = mkDefault true;
createDirectories = mkDefault true; createDirectories = mkDefault true;
}; };
portal = { portal.enable = mkDefault true;
enable = mkDefault true; portal.extraPortals = [ portal ];
extraPortals = [ portal ]; portal.configPackages = [ portal ];
configPackages = [ portal ];
};
}; };
} }

View file

@ -19,13 +19,13 @@ let
in in
{ {
urls = [ { template = engine.url; } ]; urls = [ { template = engine.url; } ];
inherit (engine) icon; icon = engine.icon;
updateInterval = if (isUrl engine.icon) then every_day else null; updateInterval = if (isUrl engine.icon) then every_day else null;
definedAliases = optional (engine ? alias) engine.alias; definedAliases = optional (engine ? alias) engine.alias;
}; };
transformedEngines = mapAttrs' (name: engine: { transformedEngines = mapAttrs' (name: engine: {
inherit name; name = name;
value = transformEngine engine; value = transformEngine engine;
}) engines; }) engines;
@ -79,7 +79,7 @@ in
profiles.default.search.engines = mapAttrs (_: name: transformedEngines.${name}) ( profiles.default.search.engines = mapAttrs (_: name: transformedEngines.${name}) (
listToAttrs ( listToAttrs (
map (name: { map (name: {
inherit name; name = name;
value = name; value = name;
}) cfg.searchEngines }) cfg.searchEngines
) )

View file

@ -1,71 +1,79 @@
[ [
# cursor navigation
{ {
options.desc = "scroll down, recenter"; # scroll down, recenter
key = "<C-d>"; key = "<C-d>";
action = "<C-d>zz"; action = "<C-d>zz";
mode = "n"; mode = "n";
} }
{ {
options.desc = "scroll up, recenter"; # scroll up, recenter
key = "<C-u>"; key = "<C-u>";
action = "<C-u>zz"; action = "<C-u>zz";
mode = "n"; mode = "n";
} }
# searching
{ {
options.desc = "center cursor after search next"; # center cursor after search next
key = "n"; key = "n";
action = "nzzzv"; action = "nzzzv";
mode = "n"; mode = "n";
} }
{ {
options.desc = "center cursor after search previous"; # center cursor after search previous
key = "N"; key = "N";
action = "Nzzzv"; action = "Nzzzv";
mode = "n"; mode = "n";
} }
{ {
options.desc = "ex command"; # ex command
key = "<leader>pv"; key = "<leader>pv";
action = "<cmd>Ex<CR>"; action = "<cmd>Ex<CR>";
mode = "n"; mode = "n";
} }
# search and replace
{ {
options.desc = "search and replace word under cursor"; # search and replace word under cursor
key = "<leader>s"; key = "<leader>s";
action = ":%s/<C-r><C-w>/<C-r><C-w>/gI<Left><Left><Left>"; action = ":%s/<C-r><C-w>/<C-r><C-w>/gI<Left><Left><Left>";
mode = "n"; mode = "n";
} }
# search and replace selected text
{ {
key = "<leader>s"; key = "<leader>s";
action = "y:%s/<C-r>0/<C-r>0/gI<Left><Left><Left>"; action = "y:%s/<C-r>0/<C-r>0/gI<Left><Left><Left>";
mode = "v"; mode = "v";
} }
# clipboard operations
{ {
options.desc = "copy to system clipboard in visual mode"; # copy to system clipboard in visual mode
key = "<C-c>"; key = "<C-c>";
action = ''"+y ''; action = ''"+y '';
mode = "v"; mode = "v";
} }
{ {
options.desc = "paste from system clipboard in visual mode"; # paste from system clipboard in visual mode
key = "<C-v>"; key = "<C-v>";
action = ''"+p ''; action = ''"+p '';
mode = "v"; mode = "v";
} }
{ {
options.desc = "yank to system clipboard"; # yank to system clipboard
key = "<leader>Y"; key = "<leader>Y";
action = "+Y"; action = "+Y";
mode = "n"; mode = "n";
} }
{ {
options.desc = "replace selected text with clipboard content"; # replace selected text with clipboard content
key = "<leader>p"; key = "<leader>p";
action = "_dP"; action = "_dP";
mode = "x"; mode = "x";
} }
{ {
options.desc = "delete without copying to clipboard"; # delete without copying to clipboard
key = "<leader>d"; key = "<leader>d";
action = "_d"; action = "_d";
mode = [ mode = [
@ -73,184 +81,267 @@
"v" "v"
]; ];
} }
# line operations
{ {
options.desc = "move lines down in visual mode"; # move lines down in visual mode
key = "J"; key = "J";
action = ":m '>+1<CR>gv=gv"; action = ":m '>+1<CR>gv=gv";
mode = "v"; mode = "v";
} }
{ {
options.desc = "move lines up in visual mode"; # move lines up in visual mode
key = "K"; key = "K";
action = ":m '<-2<CR>gv=gv"; action = ":m '<-2<CR>gv=gv";
mode = "v"; mode = "v";
} }
{ {
options.desc = "join lines"; # join lines
key = "J"; key = "J";
action = "mzJ`z"; action = "mzJ`z";
mode = "n"; mode = "n";
} }
# quickfix
{ {
options.desc = "Run make command"; # Run make command
key = "<leader>m"; key = "<leader>m";
action = "<cmd>:make<CR>"; action = "<cmd>:make<CR>";
mode = "n"; mode = "n";
} }
{ {
options.desc = "previous quickfix item"; # previous quickfix item
key = "<C-A-J>"; key = "<C-A-J>";
action = "<cmd>cprev<CR>zz"; action = "<cmd>cprev<CR>zz";
mode = "n"; mode = "n";
} }
{ {
options.desc = "next quickfix item"; # next quickfix item
key = "<C-A-K>"; key = "<C-A-K>";
action = "<cmd>cnext<CR>zz"; action = "<cmd>cnext<CR>zz";
mode = "n"; mode = "n";
} }
# location list navigation
{ {
options.desc = "previous location list item"; # previous location list item
key = "<leader>j"; key = "<leader>j";
action = "<cmd>lprev<CR>zz"; action = "<cmd>lprev<CR>zz";
mode = "n"; mode = "n";
} }
{ {
options.desc = "next location list item"; # next location list item
key = "<leader>k"; key = "<leader>k";
action = "<cmd>lnext<CR>zz"; action = "<cmd>lnext<CR>zz";
mode = "n"; mode = "n";
} }
# disabling keys
{ {
options.desc = "disable the 'Q' key"; # disable the 'Q' key
key = "Q"; key = "Q";
action = "<nop>"; action = "<nop>";
mode = "n"; mode = "n";
} }
# text selection
{ {
options.desc = "select whole buffer"; # select whole buffer
key = "<C-a>"; key = "<C-a>";
action = "ggVG"; action = "ggVG";
mode = "n"; mode = "n";
} }
# window operations
{ {
options.desc = "focus next window"; # focus next window
key = "<C-j>"; key = "<C-j>";
action = ":wincmd W<CR>"; action = ":wincmd W<CR>";
options = {
noremap = true;
silent = true;
};
mode = "n"; mode = "n";
} }
{ {
options.desc = "focus next window"; # focus previous window
key = "<Tab>";
action = ":wincmd W<CR>";
mode = "n";
}
{
options.desc = "focus previous window";
key = "<C-k>"; key = "<C-k>";
action = ":wincmd w<CR>"; action = ":wincmd w<CR>";
options = {
noremap = true;
silent = true;
};
mode = "n"; mode = "n";
} }
# window size adjustments
{ {
options.desc = "focus previous window"; # increase window width
key = "<S-Tab>";
action = ":wincmd w<CR>";
mode = "n";
}
{
options.desc = "increase window width";
key = "<C-l>"; key = "<C-l>";
action = ":vertical resize +5<CR>"; action = ":vertical resize +5<CR>";
options = {
noremap = true;
silent = true;
};
mode = "n"; mode = "n";
} }
{ {
options.desc = "decrease window width"; # decrease window width
key = "<C-h>"; key = "<C-h>";
action = ":vertical resize -5<CR>"; action = ":vertical resize -5<CR>";
options = {
noremap = true;
silent = true;
};
mode = "n"; mode = "n";
} }
# window closing and opening
{ {
options.desc = "close current window"; # close current window
key = "<leader-S>c"; key = "<leader-S>c";
action = ":q<CR>"; action = ":q<CR>";
options = {
noremap = true;
silent = true;
};
mode = "n"; mode = "n";
} }
{ {
options.desc = "new vertical split at $HOME"; # new vertical split at $HOME
key = "<leader>n"; key = "<leader>n";
action = ":vsp $HOME<CR>"; action = ":vsp $HOME<CR>";
options = {
noremap = true;
silent = true;
};
mode = "n"; mode = "n";
} }
# window split orientation toggling
{ {
options.desc = "toggle split orientation"; # toggle split orientation
key = "<leader>t"; key = "<leader>t";
action = ":wincmd T<CR>"; action = ":wincmd T<CR>";
options = {
noremap = true;
silent = true;
};
mode = "n"; mode = "n";
} }
# spell checking
{ {
options.desc = "toggle spell checking"; # toggle spell checking
key = "<leader>ss"; key = "<leader>ss";
action = ":setlocal spell!<CR>"; action = ":setlocal spell!<CR>";
options = {
noremap = true;
silent = true;
};
mode = "n"; mode = "n";
} }
{ {
options.desc = "switch to english spell checking"; # switch to english spell checking
key = "<leader>se"; key = "<leader>se";
action = ":setlocal spelllang=en_us<CR>"; action = ":setlocal spelllang=en_us<CR>";
options = {
noremap = true;
silent = true;
};
mode = "n"; mode = "n";
} }
{ {
options.desc = "switch to german spell checking"; # switch to german spell checking
key = "<leader>sg"; key = "<leader>sg";
action = ":setlocal spelllang=de_20<CR>"; action = ":setlocal spelllang=de_20<CR>";
options = {
noremap = true;
silent = true;
};
mode = "n"; mode = "n";
} }
{ {
options.desc = "move to next misspelling"; # move to next misspelling
key = "]s"; key = "]s";
action = "]szz"; action = "]szz";
options = {
noremap = true;
silent = true;
};
mode = "n"; mode = "n";
} }
{ {
options.desc = "move to previous misspelling"; # move to previous misspelling
key = "[s"; key = "[s";
action = "[szz"; action = "[szz";
options = {
noremap = true;
silent = true;
};
mode = "n"; mode = "n";
} }
{ {
options.desc = "correction suggestions for a misspelled word"; # correction suggestions for a misspelled word
key = "z="; key = "z=";
action = "z="; action = "z=";
options = {
noremap = true;
silent = true;
};
mode = "n"; mode = "n";
} }
{ {
options.desc = "adding words to the dictionary"; # adding words to the dictionary
key = "zg"; key = "zg";
action = "zg"; action = "zg";
options = {
noremap = true;
silent = true;
};
mode = "n"; mode = "n";
} }
# buffer navigation
{ {
options.desc = "next buffer"; # next buffer
key = "<C-S-J>"; key = "<C-S-J>";
action = ":bnext<CR>"; action = ":bnext<CR>";
options = {
noremap = true;
silent = true;
};
mode = "n"; mode = "n";
} }
{ {
options.desc = "previous buffer"; # previous buffer
key = "<C-S-K>"; key = "<C-S-K>";
action = ":bprevious<CR>"; action = ":bprevious<CR>";
options = {
noremap = true;
silent = true;
};
mode = "n"; mode = "n";
} }
{ {
options.desc = "close current buffer"; # close current buffer
key = "<leader>bd"; key = "<leader>bd";
action = ":bdelete<CR>"; action = ":bdelete<CR>";
options = {
noremap = true;
silent = true;
};
mode = "n"; mode = "n";
} }
{ {
options.desc = "apply code action"; # apply code action
key = "<leader>ca"; key = "<leader>ca";
action = ":lua vim.lsp.buf.code_action()<CR>"; action = ":lua vim.lsp.buf.code_action()<CR>";
options = {
noremap = true;
silent = true;
};
mode = "n"; mode = "n";
} }
] ]

View file

@ -3,17 +3,14 @@
{ {
imports = [ imports = [
./cmp.nix ./cmp.nix
./diffview.nix
./lsp.nix ./lsp.nix
./gitsigns.nix
./lualine.nix ./lualine.nix
./telescope.nix ./telescope.nix
./treesitter.nix # ./treesitter.nix # HOTFIX: does not build
./trouble.nix ./trouble.nix
]; ];
config.programs.nixvim.plugins = { config.programs.nixvim.plugins = {
which-key.enable = lib.mkDefault true;
markdown-preview.enable = lib.mkDefault true; markdown-preview.enable = lib.mkDefault true;
# warning: Nixvim: `plugins.web-devicons` was enabled automatically because the following plugins are enabled. This behaviour is deprecated. Please explicitly define `plugins.web-devicons.enable` # warning: Nixvim: `plugins.web-devicons` was enabled automatically because the following plugins are enabled. This behaviour is deprecated. Please explicitly define `plugins.web-devicons.enable`
web-devicons.enable = true; web-devicons.enable = true;

View file

@ -1,59 +0,0 @@
{ config, lib, ... }:
let
cfg = config.programs.nixvim;
plugin = cfg.plugins.diffview;
inherit (lib) mkDefault mkIf;
in
{
config = {
programs.nixvim = {
plugins.diffview = {
enable = mkDefault true;
};
# highlight = mkIf plugin.enable {
# DiffAdd = {
# bg = "#2d4a2d";
# fg = "NONE";
# };
# DiffDelete = {
# bg = "#4a2d2d";
# fg = "NONE";
# };
# DiffChange = {
# bg = "#2d3a4a";
# fg = "NONE";
# };
# DiffText = {
# bg = "#1a5a1a";
# fg = "NONE";
# };
# };
keymaps = mkIf plugin.enable [
{
mode = "n";
key = "<leader>gd";
action.__raw = ''
function()
local lib = require("diffview.lib")
local view = lib.get_current_view()
if view then
vim.cmd("DiffviewClose")
else
vim.cmd("DiffviewOpen")
end
end
'';
options = {
noremap = true;
silent = true;
desc = "toggle git diff";
};
}
];
};
};
}

View file

@ -1,104 +0,0 @@
{ config, lib, ... }:
let
cfg = config.programs.nixvim;
plugin = cfg.plugins.gitsigns;
inherit (lib) mkDefault mkIf;
in
{
config = {
programs.nixvim = {
plugins.gitsigns = {
enable = mkDefault true;
settings = {
current_line_blame = mkDefault false;
current_line_blame_opts = {
virt_text = mkDefault true;
virt_text_pos = mkDefault "eol";
};
signcolumn = mkDefault true;
signs = {
add.text = mkDefault "+";
change.text = mkDefault "";
changedelete.text = mkDefault "~";
delete.text = mkDefault "-";
topdelete.text = mkDefault "-";
untracked.text = mkDefault "?";
};
watch_gitdir.follow_files = mkDefault true;
};
};
keymaps = mkIf plugin.enable [
{
mode = "n";
key = "<leader>hs";
action.__raw = "function() require('gitsigns').stage_hunk() end";
options = {
noremap = true;
silent = true;
desc = "stage hunk";
};
}
{
mode = "v";
key = "<leader>hs";
action.__raw = ''
function()
require('gitsigns').stage_hunk({vim.fn.line('.'), vim.fn.line('v')})
end
'';
options = {
noremap = true;
silent = true;
desc = "stage selected hunks";
};
}
{
mode = "n";
key = "<leader>hu";
action.__raw = "function() require('gitsigns').undo_stage_hunk() end";
options = {
noremap = true;
silent = true;
desc = "undo stage hunk";
};
}
{
mode = "n";
key = "<leader>hS";
action.__raw = "function() require('gitsigns').stage_buffer() end";
options = {
noremap = true;
silent = true;
desc = "stage buffer";
};
}
{
mode = "n";
key = "<leader>hr";
action.__raw = "function() require('gitsigns').reset_hunk() end";
options = {
noremap = true;
silent = true;
desc = "reset hunk";
};
}
{
mode = "v";
key = "<leader>hr";
action.__raw = ''
function()
require('gitsigns').reset_hunk({vim.fn.line('.'), vim.fn.line('v')})
end
'';
options = {
noremap = true;
silent = true;
desc = "reset selected hunks";
};
}
];
};
};
}

View file

@ -16,31 +16,6 @@ in
programs.nixvim = { programs.nixvim = {
plugins.telescope = { plugins.telescope = {
enable = mkDefault true; enable = mkDefault true;
settings = {
defaults = {
vimgrep_arguments = [
"rg"
"--color=never"
"--no-heading"
"--with-filename"
"--line-number"
"--column"
"--smart-case"
"--hidden"
];
file_ignore_patterns = [
"^.git/"
"^.direnv/"
"^.cache/"
"^node_modules/"
];
};
pickers = {
find_files = {
hidden = true;
};
};
};
extensions = { extensions = {
file-browser.enable = mkDefault true; file-browser.enable = mkDefault true;
fzf-native.enable = mkDefault true; fzf-native.enable = mkDefault true;

View file

@ -16,19 +16,17 @@ in
{ {
config = { config = {
programs.nixvim = { programs.nixvim = {
plugins = { plugins.treesitter = {
treesitter = { enable = mkDefault true;
enable = mkDefault true; nixvimInjections = mkDefault true;
nixvimInjections = mkDefault true; settings = {
settings = { folding.enable = mkDefault true;
folding.enable = mkDefault true; highlight.enable = mkDefault true;
highlight.enable = mkDefault true; indent.enable = mkDefault true;
indent.enable = mkDefault true;
};
}; };
treesitter-context = mkIf plugin.enable { enable = mkDefault true; };
treesitter-textobjects = mkIf plugin.enable { enable = mkDefault true; };
}; };
plugins.treesitter-context = mkIf plugin.enable { enable = mkDefault true; };
plugins.treesitter-textobjects = mkIf plugin.enable { enable = mkDefault true; };
}; };
# Fix for: ERROR `cc` executable not found. # Fix for: ERROR `cc` executable not found.

View file

@ -2,7 +2,7 @@
let let
spellDir = config.xdg.dataHome + "/nvim/site/spell"; spellDir = config.xdg.dataHome + "/nvim/site/spell";
baseUrl = "https://vim.ftp.fu-berlin.de/runtime/spell"; baseUrl = "http://ftp.de.vim.org/runtime/spell";
in in
{ {
home.file = { home.file = {
@ -12,7 +12,7 @@ in
url = baseUrl + "/de.utf-8.spl"; url = baseUrl + "/de.utf-8.spl";
sha256 = "sha256-c8cQfqM5hWzb6SHeuSpFk5xN5uucByYdobndGfaDo9E="; sha256 = "sha256-c8cQfqM5hWzb6SHeuSpFk5xN5uucByYdobndGfaDo9E=";
}; };
target = spellDir + "/de.utf-8.spl"; target = spellDir + "/de.utf8.spl";
}; };
de-sug = { de-sug = {
enable = true; enable = true;
@ -20,7 +20,7 @@ in
url = baseUrl + "/de.utf-8.sug"; url = baseUrl + "/de.utf-8.sug";
sha256 = "sha256-E9Ds+Shj2J72DNSopesqWhOg6Pm6jRxqvkerqFcUqUg="; sha256 = "sha256-E9Ds+Shj2J72DNSopesqWhOg6Pm6jRxqvkerqFcUqUg=";
}; };
target = spellDir + "/de.utf-8.sug"; target = spellDir + "/de.utf8.sug";
}; };
}; };
} }

View file

@ -9,7 +9,7 @@
let let
cfg = config.programs.passwordManager; cfg = config.programs.passwordManager;
passmenuScript = pkgs.writeShellScriptBin "passmenu-bemenu" (builtins.readFile ./passmenu); # TODO: override original passmenu script coming from pass itself passmenuScript = pkgs.writeShellScriptBin "passmenu-bemenu" (builtins.readFile ./passmenu); # TODO: override original passmenu script coming from pass itself
inherit (pkgs) passff-host; passff-host = pkgs.passff-host;
inherit (lib) inherit (lib)
mkDefault mkDefault

View file

@ -17,7 +17,6 @@ let
"moonfly" "moonfly"
"nord" "nord"
"oxocarbon" "oxocarbon"
"generate-from-image"
]; ];
# schemes names in `pkgs.base16-schemes` that need a suffix # schemes names in `pkgs.base16-schemes` that need a suffix
needsSuffix = [ needsSuffix = [
@ -29,7 +28,13 @@ let
"moonfly" "moonfly"
"oxocarbon" "oxocarbon"
]; ];
schemeName = if builtins.elem cfg.scheme needsSuffix then "${cfg.scheme}-${cfg.polarity}" else cfg.scheme; schemeName =
if builtins.elem cfg.scheme needsSuffix then "${cfg.scheme}-${cfg.polarity}" else cfg.scheme;
scheme =
if builtins.elem cfg.scheme customSchemes then
./schemes/${schemeName}.yaml
else
"${pkgs.base16-schemes}/share/themes/${schemeName}.yaml";
inherit (lib) inherit (lib)
mkDefault mkDefault
@ -51,8 +56,6 @@ in
description = '' description = ''
Base16 color scheme name. Available options are: Base16 color scheme name. Available options are:
${toString validSchemes} ${toString validSchemes}
"generate-from-image" generates a color scheme from `stylix.image`
''; '';
}; };
}; };
@ -67,12 +70,7 @@ in
stylix = { stylix = {
autoEnable = mkDefault true; autoEnable = mkDefault true;
base16Scheme = mkIf (cfg.scheme != "generate-from-image") ( base16Scheme = scheme;
if builtins.elem cfg.scheme customSchemes then
./schemes/${schemeName}.yaml
else
"${pkgs.base16-schemes}/share/themes/${schemeName}.yaml"
);
fonts = { fonts = {
monospace = mkDefault { monospace = mkDefault {
package = pkgs.hack-font; package = pkgs.hack-font;

View file

@ -1,4 +1,3 @@
---
system: "base16" system: "base16"
name: "Moonfly" name: "Moonfly"
description: "A dark theme inspired by the Moonfly color scheme." description: "A dark theme inspired by the Moonfly color scheme."

View file

@ -1,4 +1,3 @@
---
system: "base16" system: "base16"
name: "Oxocarbon" name: "Oxocarbon"
description: "A dark theme inspired by the Oxocarbon Dark color scheme." description: "A dark theme inspired by the Oxocarbon Dark color scheme."

View file

@ -4,13 +4,11 @@ let
cfg = config.stylix; cfg = config.stylix;
target = cfg.targets.nixvim; target = cfg.targets.nixvim;
inherit (lib) mkIf optionalAttrs; inherit (lib) mkIf;
in in
{ {
config = mkIf cfg.enable { config = mkIf cfg.enable {
stylix.targets.nixvim.enable = false; stylix.targets.nixvim.enable = false;
programs.nixvim.colorschemes = optionalAttrs (cfg.scheme != "generate-from-image") { programs.nixvim.colorschemes."${cfg.scheme}".enable = !target.enable;
"${cfg.scheme}".enable = !target.enable;
};
}; };
} }

View file

@ -71,11 +71,11 @@ in
#workspaces { #workspaces {
color: ${colors.base05}; color: ${colors.base05};
background: ${colors.base00};
} }
#workspaces button { #workspaces button {
padding: ${halfgaps}px; padding: ${halfgaps}px;
color: ${colors.base05};
} }
#workspaces button.active { #workspaces button.active {

View file

@ -56,7 +56,6 @@ let
}; };
# Add your custom modules here # Add your custom modules here
"custom/notifications" = import ./modules/notifications.nix { inherit lib pkgs; };
"custom/newsboat" = import ./modules/newsboat.nix { inherit lib pkgs; }; "custom/newsboat" = import ./modules/newsboat.nix { inherit lib pkgs; };
"pulseaudio#input" = import ./modules/pulseaudio/input.nix { inherit lib pkgs; }; "pulseaudio#input" = import ./modules/pulseaudio/input.nix { inherit lib pkgs; };
"pulseaudio#output" = import ./modules/pulseaudio/output.nix { inherit lib pkgs; }; "pulseaudio#output" = import ./modules/pulseaudio/output.nix { inherit lib pkgs; };
@ -96,13 +95,11 @@ in
"disk" "disk"
"pulseaudio#input" "pulseaudio#input"
"pulseaudio#output" "pulseaudio#output"
"custom/notifications"
"tray" "tray"
]; ];
inherit inherit
"custom/newsboat" "custom/newsboat"
"custom/notifications"
"hyprland/language" "hyprland/language"
"hyprland/workspaces" "hyprland/workspaces"
"pulseaudio#input" "pulseaudio#input"
@ -132,7 +129,5 @@ in
}; };
}; };
}; };
services.batsignal.enable = builtins.elem "battery" cfg.settings.mainBar.modules-right;
}; };
} }

View file

@ -10,13 +10,13 @@ let
let let
newsboat = "${pkgs.newsboat}/bin/newsboat"; newsboat = "${pkgs.newsboat}/bin/newsboat";
in in
pkgs.writeShellScriptBin "newsboat-print-unread" '' (pkgs.writeShellScriptBin "newsboat-print-unread" ''
UNREAD=$(${newsboat} -x print-unread | awk '{print $1}') UNREAD=$(${newsboat} -x print-unread | awk '{print $1}')
if [[ $UNREAD -gt 0 ]]; then if [[ $UNREAD -gt 0 ]]; then
printf " %i" "$UNREAD" printf " %i" "$UNREAD"
fi fi
''; '');
inherit (lib) mkDefault; inherit (lib) mkDefault;
in in

View file

@ -1,27 +0,0 @@
{ pkgs, lib, ... }:
let
inherit (lib) mkDefault;
dunst = "${pkgs.dunst}/bin/dunstctl";
in
{
format = mkDefault "{icon}";
format-icons = {
unmuted = mkDefault "";
muted = mkDefault "";
};
return-type = mkDefault "json";
exec = mkDefault (
pkgs.writeShellScript "notifications" ''
is_paused=$(${dunst} is-paused)
if [ "$is_paused" = "true" ]; then
echo '{"alt": "muted", "class": "muted"}'
else
echo '{"alt": "unmuted", "class": "unmuted"}'
fi
''
);
on-click = mkDefault "dunst-toggle";
interval = mkDefault "once";
signal = mkDefault 12;
}

View file

@ -1,4 +1,3 @@
#!/usr/bin/env bash
TIMER_FILE="/tmp/timer" # file to store the current time TIMER_FILE="/tmp/timer" # file to store the current time
SIGNAL=11 # signal number to send to status bar SIGNAL=11 # signal number to send to status bar
STATUS_BAR="waybar" # Support for more status bars? STATUS_BAR="waybar" # Support for more status bars?
@ -17,7 +16,7 @@ start_timer() {
notify-send "Timer Started" "Your countdown timer has been started." notify-send "Timer Started" "Your countdown timer has been started."
trap "exit" INT TERM trap "exit" INT TERM
trap 'rm -f -- "$TIMER_FILE"' EXIT trap "rm -f -- '$TIMER_FILE'" EXIT
while [ $total_seconds -gt 0 ]; do while [ $total_seconds -gt 0 ]; do
hours=$(( total_seconds / 3600 )) hours=$(( total_seconds / 3600 ))
@ -63,7 +62,7 @@ if [ "$1" = "start" ]; then
MINUTES=${MINUTES:-0} MINUTES=${MINUTES:-0}
SECONDS=${SECONDS:-0} SECONDS=${SECONDS:-0}
start_timer "$HOURS" "$MINUTES" "$SECONDS" start_timer $HOURS $MINUTES $SECONDS
elif [ "$1" = "stop" ]; then elif [ "$1" = "stop" ]; then
notify-send "Timer Stopped" "Your countdown timer has been stopped." notify-send "Timer Stopped" "Your countdown timer has been stopped."

View file

@ -6,19 +6,19 @@
environment.systemPackages = with pkgs; [ environment.systemPackages = with pkgs; [
lact lact
nvtopPackages.amd nvtopPackages.amd
rocmPackages.clr.icd
rocmPackages.hipcc rocmPackages.hipcc
rocmPackages.miopen rocmPackages.miopen
rocmPackages.rocm-runtime rocmPackages.rocm-runtime
rocmPackages.rocm-smi rocmPackages.rocm-smi
rocmPackages.rocminfo rocmPackages.rocminfo
]; ];
# environment.variables.ROC_ENABLE_PRE_VEGA = "1"; # for Polaris # environment.variables.ROC_ENABLE_PRE_VEGA = "1"; # for Polaris
hardware.amdgpu.opencl.enable = true; hardware.amdgpu.opencl.enable = true;
hardware.graphics.extraPackages = with pkgs; [ rocmPackages.clr.icd ];
systemd.packages = with pkgs; [ lact ]; systemd.packages = with pkgs; [ lact ];
systemd.services.lactd.wantedBy = [ "multi-user.target" ]; systemd.services.lactd.wantedBy = [ "multi-user.target" ];
} }

View file

@ -4,16 +4,10 @@ let
inherit (lib) mkDefault; inherit (lib) mkDefault;
in in
{ {
hardware = { hardware.bluetooth.enable = mkDefault true;
bluetooth = { hardware.bluetooth.powerOnBoot = mkDefault false;
enable = mkDefault true; hardware.bluetooth.settings.General.Enable = mkDefault "Source,Sink,Media,Socket";
powerOnBoot = mkDefault false; hardware.bluetooth.settings.General.Experimental = mkDefault true;
settings.General = {
Enable = mkDefault "Source,Sink,Media,Socket";
Experimental = mkDefault true;
};
};
};
environment.systemPackages = with pkgs; [ environment.systemPackages = with pkgs; [
blueman blueman

View file

@ -84,6 +84,8 @@ in
) { } cfg.remotes; ) { } cfg.remotes;
# Ensure that all cifs-mount services are started with the graphical session # Ensure that all cifs-mount services are started with the graphical session
systemd.user.targets.graphical-session.wants = map (remote: "cifs-mount-${remote.shareName}.service") cfg.remotes; systemd.user.targets.graphical-session.wants = map (
remote: "cifs-mount-${remote.shareName}.service"
) cfg.remotes;
}; };
} }

View file

@ -1,20 +0,0 @@
{ lib, pkgs, ... }:
{
boot = {
# fix CVE-2026-31431
kernelPackages = lib.mkIf (lib.versionOlder pkgs.linux.version "6.18.22") (lib.mkDefault pkgs.linuxPackages_6_18);
# fix CVE-2026-43500
extraModprobeConfig = ''
install esp4 ${pkgs.coreutils}/bin/false
install esp6 ${pkgs.coreutils}/bin/false
install rxrpc ${pkgs.coreutils}/bin/false
'';
blacklistedKernelModules = [
"esp4"
"esp6"
"rxrpc"
];
};
}

View file

@ -1,6 +1,5 @@
{ {
imports = [ imports = [
./boot.nix
./environment.nix ./environment.nix
./htop.nix ./htop.nix
./nationalization.nix ./nationalization.nix

View file

@ -9,59 +9,55 @@ let
inherit (lib) mkDefault optionals; inherit (lib) mkDefault optionals;
in in
{ {
environment = { environment.systemPackages =
systemPackages = with pkgs;
with pkgs; [
[ cryptsetup
cryptsetup curl
curl dig
dig dnsutils
dnsutils fzf
fzf gptfdisk
gptfdisk iproute2
iproute2 jq
jq lm_sensors
lm_sensors lsof
lsof netcat-openbsd
netcat-openbsd nettools
nettools nixos-container
nixos-container nmap
nmap nurl
nurl p7zip
p7zip pciutils
pciutils psmisc
psmisc rclone
rclone rsync
rsync tcpdump
tcpdump tmux
tmux tree
tree unzip
unzip usbutils
usbutils wget
wget xxd
xxd zip
zip
(callPackage ../../../apps/rebuild { }) (callPackage ../../../apps/rebuild { })
] ]
++ optionals (pkgs.stdenv.hostPlatform == pkgs.stdenv.buildPlatform) [ ++ optionals (pkgs.stdenv.hostPlatform == pkgs.stdenv.buildPlatform) [
pkgs.kitty.terminfo pkgs.kitty.terminfo
]; ];
shellAliases = { environment.shellAliases = {
l = "ls -lh"; l = "ls -lh";
ll = "ls -lAh"; ll = "ls -lAh";
ports = "ss -tulpn"; ports = "ss -tulpn";
publicip = "curl ifconfig.me/all"; publicip = "curl ifconfig.me/all";
sudo = "sudo "; # make aliases work with `sudo` sudo = "sudo "; # make aliases work with `sudo`
};
# saves one instance of nixpkgs.
ldso32 = null;
}; };
boot = { # saves one instance of nixpkgs.
tmp.cleanOnBoot = mkDefault true; environment.ldso32 = null;
initrd.systemd.enable = mkDefault (!config.boot.swraid.enable && !config.boot.isContainer);
}; boot.tmp.cleanOnBoot = mkDefault true;
boot.initrd.systemd.enable = mkDefault (!config.boot.swraid.enable && !config.boot.isContainer);
} }

View file

@ -2,13 +2,16 @@
# avoid TOFU MITM # avoid TOFU MITM
programs.ssh.knownHosts = { programs.ssh.knownHosts = {
"github.com".hostNames = [ "github.com" ]; "github.com".hostNames = [ "github.com" ];
"github.com".publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl"; "github.com".publicKey =
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl";
"gitlab.com".hostNames = [ "gitlab.com" ]; "gitlab.com".hostNames = [ "gitlab.com" ];
"gitlab.com".publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAfuCHKVTjquxvt6CM6tdG4SLp1Btn/nOeHHE5UOzRdf"; "gitlab.com".publicKey =
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAfuCHKVTjquxvt6CM6tdG4SLp1Btn/nOeHHE5UOzRdf";
"git.sr.ht".hostNames = [ "git.sr.ht" ]; "git.sr.ht".hostNames = [ "git.sr.ht" ];
"git.sr.ht".publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMZvRd4EtM7R+IHVMWmDkVU3VLQTSwQDSAvW0t2Tkj60"; "git.sr.ht".publicKey =
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMZvRd4EtM7R+IHVMWmDkVU3VLQTSwQDSAvW0t2Tkj60";
}; };
# TODO: add synix # TODO: add synix
} }

View file

@ -42,8 +42,12 @@ in
static-auth-secret-file = mkIf cfg.sops config.sops.secrets."coturn/static-auth-secret".path; static-auth-secret-file = mkIf cfg.sops config.sops.secrets."coturn/static-auth-secret".path;
realm = mkDefault "turn.${config.networking.domain}"; realm = mkDefault "turn.${config.networking.domain}";
cert = mkIf (!cfg.no-tls && cfg.sops) "${config.security.acme.certs.${cfg.realm}.directory}/full.pem"; cert =
pkey = mkIf (!cfg.no-tls && cfg.sops) "${config.security.acme.certs.${cfg.realm}.directory}/key.pem"; mkIf (!cfg.no-tls && cfg.sops)
"${config.security.acme.certs.${cfg.realm}.directory}/full.pem";
pkey =
mkIf (!cfg.no-tls && cfg.sops)
"${config.security.acme.certs.${cfg.realm}.directory}/key.pem";
extraConfig = '' extraConfig = ''
# ban private IP ranges # ban private IP ranges

View file

@ -14,7 +14,6 @@
i2pd = import ./i2pd; i2pd = import ./i2pd;
jellyfin = import ./jellyfin; jellyfin = import ./jellyfin;
jirafeau = import ./jirafeau; jirafeau = import ./jirafeau;
librechat-oci = import ./librechat-oci;
mailserver = import ./mailserver; mailserver = import ./mailserver;
matrix-synapse = import ./matrix-synapse; matrix-synapse = import ./matrix-synapse;
maubot = import ./maubot; maubot = import ./maubot;
@ -22,7 +21,6 @@
miniflux = import ./miniflux; miniflux = import ./miniflux;
nginx = import ./nginx; nginx = import ./nginx;
normalUsers = import ./normalUsers; normalUsers = import ./normalUsers;
nostr-relay = import ./nostr-relay;
nvidia = import ./nvidia; nvidia = import ./nvidia;
ollama = import ./ollama; ollama = import ./ollama;
open-webui-oci = import ./open-webui-oci; open-webui-oci = import ./open-webui-oci;
@ -35,4 +33,5 @@
virtualisation = import ./virtualisation; virtualisation = import ./virtualisation;
webPage = import ./webPage; webPage = import ./webPage;
windows-oci = import ./windows-oci; windows-oci = import ./windows-oci;
zfs = import ./zfs;
} }

View file

@ -33,13 +33,11 @@ in
fonts.fontconfig.enable = mkDefault false; fonts.fontconfig.enable = mkDefault false;
xdg = { xdg.autostart.enable = mkDefault false;
autostart.enable = mkDefault false; xdg.icons.enable = mkDefault false;
icons.enable = mkDefault false; xdg.menus.enable = mkDefault false;
menus.enable = mkDefault false; xdg.mime.enable = mkDefault false;
mime.enable = mkDefault false; xdg.sounds.enable = mkDefault false;
sounds.enable = mkDefault false;
};
programs.git.package = mkDefault pkgs.gitMinimal; programs.git.package = mkDefault pkgs.gitMinimal;
@ -50,25 +48,22 @@ in
viAlias = mkDefault true; viAlias = mkDefault true;
}; };
systemd = { # emergency mode is useless on headless machines
# emergency mode is useless on headless machines systemd.enableEmergencyMode = false;
enableEmergencyMode = false;
sleep.extraConfig = ''
AllowSuspend=no
AllowHibernation=no
'';
# force reboots
settings.Manager = {
RuntimeWatchdogSec = mkDefault "15s";
RebootWatchdogSec = mkDefault "30s";
KExecWatchdogSec = mkDefault "1m";
};
};
boot.initrd.systemd.suppressedUnits = mkIf config.systemd.enableEmergencyMode [ boot.initrd.systemd.suppressedUnits = mkIf config.systemd.enableEmergencyMode [
"emergency.service" "emergency.service"
"emergency.target" "emergency.target"
]; ];
systemd.sleep.extraConfig = ''
AllowSuspend=no
AllowHibernation=no
'';
# force reboots
systemd.settings.Manager = {
RuntimeWatchdogSec = mkDefault "15s";
RebootWatchdogSec = mkDefault "30s";
KExecWatchdogSec = mkDefault "1m";
};
} }

View file

@ -4,9 +4,7 @@ let
inherit (lib) mkDefault; inherit (lib) mkDefault;
in in
{ {
services = { services.qemuGuest.enable = mkDefault true;
qemuGuest.enable = mkDefault true; services.spice-vdagentd.enable = mkDefault true;
spice-vdagentd.enable = mkDefault true; services.spice-webdavd.enable = mkDefault true;
spice-webdavd.enable = mkDefault true;
};
} }

View file

@ -2,9 +2,9 @@
let let
cfg = config.services.ftp-webserver; cfg = config.services.ftp-webserver;
inherit (config.networking) domain; domain = config.networking.domain;
fqdn = if (cfg.subdomain != "") then "${cfg.subdomain}.${domain}" else domain; fqdn = if (cfg.subdomain != "") then "${cfg.subdomain}.${domain}" else domain;
inherit (config.services) nginx; nginx = config.services.nginx;
inherit (lib) inherit (lib)
mkEnableOption mkEnableOption
@ -35,7 +35,7 @@ in
config = mkIf cfg.enable { config = mkIf cfg.enable {
services.nginx.virtualHosts."${fqdn}" = { services.nginx.virtualHosts."${fqdn}" = {
inherit (cfg) root; root = cfg.root;
locations."/" = { locations."/" = {
extraConfig = '' extraConfig = ''
autoindex on; autoindex on;
@ -43,7 +43,7 @@ in
autoindex_localtime on; autoindex_localtime on;
''; '';
}; };
inherit (cfg) forceSSL; forceSSL = cfg.forceSSL;
enableACME = cfg.forceSSL; enableACME = cfg.forceSSL;
sslCertificate = mkIf cfg.forceSSL "${config.security.acme.certs."${fqdn}".directory}/cert.pem"; sslCertificate = mkIf cfg.forceSSL "${config.security.acme.certs."${fqdn}".directory}/cert.pem";
sslCertificateKey = mkIf cfg.forceSSL "${config.security.acme.certs."${fqdn}".directory}/key.pem"; sslCertificateKey = mkIf cfg.forceSSL "${config.security.acme.certs."${fqdn}".directory}/key.pem";

View file

@ -7,10 +7,10 @@
let let
cfg = config.services.headplane; cfg = config.services.headplane;
inherit (config.networking) domain; domain = config.networking.domain;
inherit (cfg.reverseProxy) subdomain; subdomain = cfg.reverseProxy.subdomain;
fqdn = if (cfg.reverseProxy.enable && subdomain != "") then "${subdomain}.${domain}" else domain; fqdn = if (cfg.reverseProxy.enable && subdomain != "") then "${subdomain}.${domain}" else domain;
inherit (config.services) headscale; headscale = config.services.headscale;
inherit (lib) inherit (lib)
mkDefault mkDefault
@ -45,17 +45,17 @@ in
url = "http://127.0.0.1:${toString headscale.port}"; url = "http://127.0.0.1:${toString headscale.port}";
public_url = headscale.settings.server_url; public_url = headscale.settings.server_url;
config_path = "/etc/headscale/config.yaml"; config_path = "/etc/headscale/config.yaml";
api_key_path = config.sops.secrets."headplane/api_key".path;
}; };
integration.agent = { integration.agent = {
enabled = mkDefault true; enabled = mkDefault true;
pre_authkey_path = config.sops.secrets."headplane/agent_pre_authkey".path;
}; };
}; };
}; };
services.nginx.virtualHosts = mkIf cfg.reverseProxy.enable { services.nginx.virtualHosts = mkIf cfg.reverseProxy.enable {
"${fqdn}" = mkVirtualHost { "${fqdn}" = mkVirtualHost {
inherit (cfg.settings.server) port; port = cfg.settings.server.port;
ssl = cfg.reverseProxy.forceSSL; ssl = cfg.reverseProxy.forceSSL;
}; };
}; };
@ -63,14 +63,14 @@ in
sops.secrets = sops.secrets =
let let
owner = headscale.user; owner = headscale.user;
inherit (headscale) group; group = headscale.group;
mode = "0400"; mode = "0400";
in in
{ {
"headplane/cookie_secret" = { "headplane/cookie_secret" = {
inherit owner group mode; inherit owner group mode;
}; };
"headplane/api_key" = { "headplane/agent_pre_authkey" = {
inherit owner group mode; inherit owner group mode;
}; };
}; };

View file

@ -6,8 +6,8 @@
let let
cfg = config.services.headscale; cfg = config.services.headscale;
inherit (config.networking) domain; domain = config.networking.domain;
inherit (cfg.reverseProxy) subdomain; subdomain = cfg.reverseProxy.subdomain;
fqdn = if (cfg.reverseProxy.enable && subdomain != "") then "${subdomain}.${domain}" else domain; fqdn = if (cfg.reverseProxy.enable && subdomain != "") then "${subdomain}.${domain}" else domain;
acl = "headscale/acl.hujson"; acl = "headscale/acl.hujson";

View file

@ -8,9 +8,20 @@ in
programs.dconf.enable = true; # fixes nixvim hm module programs.dconf.enable = true; # fixes nixvim hm module
services = { xdg.portal = {
flatpak.enable = true; enable = true;
gnome.gnome-keyring.enable = true; extraPortals = with pkgs; [
udisks2.enable = mkDefault true; xdg-desktop-portal-gtk
];
}; };
services.gnome.gnome-keyring.enable = true;
security.pam.services = {
login = {
enableGnomeKeyring = true;
};
hyprlock = { };
};
services.udisks2.enable = mkDefault true;
} }

View file

@ -7,8 +7,8 @@
let let
cfg = config.services.jellyfin; cfg = config.services.jellyfin;
inherit (config.networking) domain; domain = config.networking.domain;
inherit (cfg.reverseProxy) subdomain; subdomain = cfg.reverseProxy.subdomain;
fqdn = if (cfg.reverseProxy.enable && subdomain != "") then "${subdomain}.${domain}" else domain; fqdn = if (cfg.reverseProxy.enable && subdomain != "") then "${subdomain}.${domain}" else domain;
inherit (lib) inherit (lib)
@ -48,7 +48,9 @@ in
]; ];
systemd.tmpfiles.rules = systemd.tmpfiles.rules =
(map (library: "d ${cfg.dataDir}/libraries/${library} 0770 ${cfg.user} ${cfg.group} -") cfg.libraries) (map (
library: "d ${cfg.dataDir}/libraries/${library} 0770 ${cfg.user} ${cfg.group} -"
) cfg.libraries)
++ [ ++ [
"z ${cfg.dataDir} 0770 ${cfg.user} ${cfg.group} -" "z ${cfg.dataDir} 0770 ${cfg.user} ${cfg.group} -"
"Z ${cfg.dataDir}/libraries 0770 ${cfg.user} ${cfg.group} -" "Z ${cfg.dataDir}/libraries 0770 ${cfg.user} ${cfg.group} -"

View file

@ -2,8 +2,8 @@
let let
cfg = config.services.jirafeau; cfg = config.services.jirafeau;
inherit (config.networking) domain; domain = config.networking.domain;
inherit (cfg.reverseProxy) subdomain; subdomain = cfg.reverseProxy.subdomain;
fqdn = if (cfg.reverseProxy.enable && subdomain != "") then "${subdomain}.${domain}" else domain; fqdn = if (cfg.reverseProxy.enable && subdomain != "") then "${subdomain}.${domain}" else domain;
inherit (lib) inherit (lib)

View file

@ -1,535 +0,0 @@
{
config,
lib,
pkgs,
...
}:
let
cfg = config.services.librechat-oci;
defaultImages = {
librechat = pkgs.dockerTools.pullImage {
imageName = "ghcr.io/danny-avila/librechat";
imageDigest = "sha256:a46254938507971e0d4f7ed3f9d116bd9b118f4810b5b75eb716baf575645068";
hash = "sha256-zevUN6vrs3hymwCGFmk/YXlUzYjN37H+EO5aLxYchyc=";
finalImageName = "ghcr.io/danny-avila/librechat";
finalImageTag = "v0.8.5";
};
meilisearch = pkgs.dockerTools.pullImage {
imageName = "getmeili/meilisearch";
imageDigest = "sha256:b839a48d008d4e67e57f78dcff5b21d5e8b8fa066bacd11f97824d6307abb0dd";
hash = "sha256-+hypvjFDSdnnWAO4tARTnjNd/6HlF6pMg1L6UUffdYM=";
finalImageName = "getmeili/meilisearch";
finalImageTag = "v1.44.0";
};
mongodb = pkgs.dockerTools.pullImage {
imageName = "mongo";
imageDigest = "sha256:098862b1339f031900ca66cf8fef799e616d6324fa41b9a263f2ec899552c1ef";
hash = "sha256-XuWnvcqAAkAGshdQtnngKOOJP2Bd33FXTbGHTRX3nUc=";
finalImageName = "mongo";
finalImageTag = "8.0.20";
};
vectordb = pkgs.dockerTools.pullImage {
imageName = "pgvector/pgvector";
imageDigest = "sha256:8809cfffff0082cf260c9ac752f1dd1afc77f6f0a55c4e6411321e78efc3d9a5";
hash = "sha256-rc6gQLMzv8UOZVLmWKGUESNIo+iPf5DR7T79AmbzWc4=";
finalImageName = "pgvector/pgvector";
finalImageTag = "0.8.0-pg15-trixie";
};
ragApi = pkgs.dockerTools.pullImage {
imageName = "registry.librechat.ai/danny-avila/librechat-rag-api-dev-lite";
imageDigest = "sha256:6dfb6832661ff9c26fa329c823ce266059e33567670a763e9ecb9b566b8daa68";
hash = "sha256-k8pkEgbqT4NU2+2ZjdRFlfFvMUk/1p+pkysbELh95pM=";
finalImageName = "registry.librechat.ai/danny-avila/librechat-rag-api-dev-lite";
finalImageTag = "latest";
};
};
defaultEnv = {
# Server
HOST = "0.0.0.0";
PORT = toString cfg.port;
NO_INDEX = "true";
TRUST_PROXY = "1";
# Logging
CONSOLE_JSON = "false";
DEBUG_LOGGING = "true";
DEBUG_CONSOLE = "false";
AGENT_DEBUG_LOGGING = "false";
DEBUG_OPENAI = "false";
# Node
NODE_MAX_OLD_SPACE_SIZE = "6144";
# Search
SEARCH = "true";
MEILI_NO_ANALYTICS = "true";
# Moderation
OPENAI_MODERATION = "false";
BAN_VIOLATIONS = "true";
BAN_DURATION = "1000 * 60 * 60 * 2";
BAN_INTERVAL = "20";
LOGIN_VIOLATION_SCORE = "1";
REGISTRATION_VIOLATION_SCORE = "1";
CONCURRENT_VIOLATION_SCORE = "1";
MESSAGE_VIOLATION_SCORE = "1";
NON_BROWSER_VIOLATION_SCORE = "20";
TTS_VIOLATION_SCORE = "0";
STT_VIOLATION_SCORE = "0";
FORK_VIOLATION_SCORE = "0";
IMPORT_VIOLATION_SCORE = "0";
FILE_UPLOAD_VIOLATION_SCORE = "0";
LOGIN_MAX = "7";
LOGIN_WINDOW = "5";
REGISTER_MAX = "5";
REGISTER_WINDOW = "60";
LIMIT_CONCURRENT_MESSAGES = "true";
CONCURRENT_MESSAGE_MAX = "2";
LIMIT_MESSAGE_IP = "true";
MESSAGE_IP_MAX = "40";
MESSAGE_IP_WINDOW = "1";
LIMIT_MESSAGE_USER = "false";
MESSAGE_USER_MAX = "40";
MESSAGE_USER_WINDOW = "1";
ILLEGAL_MODEL_REQ_SCORE = "5";
# Registration and login
ALLOW_EMAIL_LOGIN = "true";
ALLOW_REGISTRATION = "false";
ALLOW_SOCIAL_LOGIN = "false";
ALLOW_SOCIAL_REGISTRATION = "false";
ALLOW_PASSWORD_RESET = "false";
ALLOW_UNVERIFIED_EMAIL_LOGIN = "true";
SESSION_EXPIRY = "1000 * 60 * 15";
REFRESH_TOKEN_EXPIRY = "(1000 * 60 * 60 * 24) * 7";
# OpenID
OPENID_SCOPE = "openid profile email";
OPENID_CALLBACK_URL = "/oauth/openid/callback";
OPENID_AUTO_REDIRECT = "false";
OPENID_USE_PKCE = "false";
OPENID_ON_BEHALF_FLOW_USERINFO_SCOPE = "user.read";
# OAuth callback URLs
DISCORD_CALLBACK_URL = "/oauth/discord/callback";
FACEBOOK_CALLBACK_URL = "/oauth/facebook/callback";
GITHUB_CALLBACK_URL = "/oauth/github/callback";
GOOGLE_CALLBACK_URL = "/oauth/google/callback";
APPLE_CALLBACK_URL = "/oauth/apple/callback";
SAML_CALLBACK_URL = "/oauth/saml/callback";
# Entra ID
USE_ENTRA_ID_FOR_PEOPLE_SEARCH = "false";
ENTRA_ID_INCLUDE_OWNERS_AS_MEMBERS = "false";
OPENID_GRAPH_SCOPES = "User.Read,People.Read,GroupMember.Read.All";
# Shared links
ALLOW_SHARED_LINKS = "true";
ALLOW_SHARED_LINKS_PUBLIC = "false";
# UI
APP_TITLE = "LibreChat";
HELP_AND_FAQ_URL = "https://librechat.ai";
# Flux
FLUX_API_BASE_URL = "https://api.us1.bfl.ai";
# Email
EMAIL_PORT = "25";
EMAIL_FROM = "noreply@librechat.ai";
# Azure Blob Storage
AZURE_STORAGE_PUBLIC_ACCESS = "false";
AZURE_CONTAINER_NAME = "files";
};
mkImageOption =
name: description:
mkOption {
type = types.package;
default = defaultImages.${name};
inherit description;
};
inherit (lib)
literalExpression
mkEnableOption
mkIf
mkOption
mkOverride
optional
types
;
in
{
options.services.librechat-oci = {
enable = mkEnableOption "LibreChat container with Podman.";
images = {
librechat = mkImageOption "librechat" "The LibreChat Docker image (`pkgs.dockerTools.pullImage`).";
meilisearch = mkImageOption "meilisearch" "The Meilisearch Docker image (`pkgs.dockerTools.pullImage`).";
mongodb = mkImageOption "mongodb" "The MongoDB Docker image (`pkgs.dockerTools.pullImage`).";
vectordb = mkImageOption "vectordb" "The pgvector Docker image (`pkgs.dockerTools.pullImage`).";
ragApi = mkImageOption "ragApi" "The LibreChat RAG API Docker image (`pkgs.dockerTools.pullImage`).";
};
configFile = mkOption {
type = types.nullOr types.path;
default = null;
example = literalExpression "./librechat.yaml";
description = "Path to the `librechat.yaml` configuration file.";
};
externalUrl = mkOption {
type = types.nullOr types.str;
default = null;
example = literalExpression ''"https://chat.example.com"'';
description = "Public URL to configure for LibreChat (sets DOMAIN_CLIENT and DOMAIN_SERVER).";
};
port = mkOption {
type = types.port;
default = 3080;
description = "Which port the LibreChat server listens on.";
};
meiliPort = mkOption {
type = types.port;
default = 7700;
description = "Which port Meilisearch listens on.";
};
ragPort = mkOption {
type = types.port;
default = 8000;
description = "Which port the RAG API listens on.";
};
environment = mkOption {
default = { };
type = types.attrsOf types.str;
description = ''
Extra environment variables for LibreChat.
These are merged on top of the defaults and can override them.
For secrets use <option>environmentFile</option> instead.
See <https://docs.librechat.ai/docs/configuration/dotenv>.
'';
};
environmentFile = mkOption {
type = types.nullOr types.path;
default = null;
example = literalExpression "config.sops.templates.librechat-env.path";
description = ''
Environment file passed to the LibreChat, Meilisearch, and RAG API
containers. Use this for secrets such as JWT_SECRET, CREDS_KEY,
MEILI_MASTER_KEY, and API keys.
'';
};
};
config = mkIf cfg.enable {
virtualisation = {
podman = {
enable = true;
autoPrune.enable = true;
dockerCompat = true;
};
oci-containers = {
backend = "podman";
containers = {
librechat-mongodb = {
image = with cfg.images.mongodb; "${imageName}:${imageTag}";
imageFile = cfg.images.mongodb;
cmd = [
"mongod"
"--noauth"
];
volumes = [
"librechat_mongodb_data:/data/db:rw"
];
log-driver = "journald";
extraOptions = [
"--network-alias=mongodb"
"--network=librechat_default"
];
};
librechat-meilisearch = {
image = with cfg.images.meilisearch; "${imageName}:${imageTag}";
imageFile = cfg.images.meilisearch;
environment = {
MEILI_NO_ANALYTICS = "true";
MEILI_HOST = "http://meilisearch:${toString cfg.meiliPort}";
};
environmentFiles = optional (cfg.environmentFile != null) cfg.environmentFile;
volumes = [
"librechat_meili_data:/meili_data:rw"
];
log-driver = "journald";
extraOptions = [
"--network-alias=meilisearch"
"--network=librechat_default"
];
};
librechat-vectordb = {
image = with cfg.images.vectordb; "${imageName}:${imageTag}";
imageFile = cfg.images.vectordb;
environment = {
POSTGRES_DB = "mydatabase";
POSTGRES_USER = "myuser";
POSTGRES_PASSWORD = "mypassword";
};
volumes = [
"librechat_pgdata:/var/lib/postgresql/data:rw"
];
log-driver = "journald";
extraOptions = [
"--network-alias=vectordb"
"--network=librechat_default"
];
};
librechat-rag-api = {
image = with cfg.images.ragApi; "${imageName}:${imageTag}";
imageFile = cfg.images.ragApi;
environment = {
DB_HOST = "vectordb";
RAG_PORT = toString cfg.ragPort;
};
environmentFiles = optional (cfg.environmentFile != null) cfg.environmentFile;
dependsOn = [ "librechat-vectordb" ];
log-driver = "journald";
extraOptions = [
"--network-alias=rag_api"
"--network=librechat_default"
];
};
librechat = {
image = with cfg.images.librechat; "${imageName}:${imageTag}";
imageFile = cfg.images.librechat;
environment =
defaultEnv
// {
MONGO_URI = "mongodb://mongodb:27017/LibreChat";
MEILI_HOST = "http://meilisearch:${toString cfg.meiliPort}";
RAG_PORT = toString cfg.ragPort;
RAG_API_URL = "http://rag_api:${toString cfg.ragPort}";
DOMAIN_CLIENT = if cfg.externalUrl != null then cfg.externalUrl else "http://localhost:${toString cfg.port}";
DOMAIN_SERVER = if cfg.externalUrl != null then cfg.externalUrl else "http://localhost:${toString cfg.port}";
}
// cfg.environment;
environmentFiles = optional (cfg.environmentFile != null) cfg.environmentFile;
volumes = [
"librechat_images:/app/client/public/images:rw"
"librechat_uploads:/app/uploads:rw"
"librechat_logs:/app/logs:rw"
]
++ optional (cfg.configFile != null) "${cfg.configFile}:/app/librechat.yaml:ro";
ports = [
"0.0.0.0:${toString cfg.port}:${toString cfg.port}/tcp"
];
dependsOn = [
"librechat-mongodb"
"librechat-rag-api"
];
log-driver = "journald";
extraOptions = [
"--network-alias=api"
"--network=librechat_default"
];
};
};
};
};
networking.firewall.interfaces =
let
matchAll = if !config.networking.nftables.enable then "podman+" else "podman*";
in
{
"${matchAll}".allowedUDPPorts = [ 53 ];
};
systemd = {
services = {
podman-librechat-mongodb = {
serviceConfig.Restart = mkOverride 90 "always";
after = [
"podman-network-librechat_default.service"
"podman-volume-librechat_mongodb_data.service"
];
requires = [
"podman-network-librechat_default.service"
"podman-volume-librechat_mongodb_data.service"
];
partOf = [ "podman-compose-librechat-root.target" ];
wantedBy = [ "podman-compose-librechat-root.target" ];
};
podman-librechat-meilisearch = {
serviceConfig.Restart = mkOverride 90 "always";
after = [
"podman-network-librechat_default.service"
"podman-volume-librechat_meili_data.service"
];
requires = [
"podman-network-librechat_default.service"
"podman-volume-librechat_meili_data.service"
];
partOf = [ "podman-compose-librechat-root.target" ];
wantedBy = [ "podman-compose-librechat-root.target" ];
};
podman-librechat-vectordb = {
serviceConfig.Restart = mkOverride 90 "always";
after = [
"podman-network-librechat_default.service"
"podman-volume-librechat_pgdata.service"
];
requires = [
"podman-network-librechat_default.service"
"podman-volume-librechat_pgdata.service"
];
partOf = [ "podman-compose-librechat-root.target" ];
wantedBy = [ "podman-compose-librechat-root.target" ];
};
podman-librechat-rag-api = {
serviceConfig.Restart = mkOverride 90 "always";
after = [
"podman-network-librechat_default.service"
"podman-librechat-vectordb.service"
];
requires = [
"podman-network-librechat_default.service"
"podman-librechat-vectordb.service"
];
partOf = [ "podman-compose-librechat-root.target" ];
wantedBy = [ "podman-compose-librechat-root.target" ];
};
podman-librechat = {
serviceConfig.Restart = mkOverride 90 "always";
after = [
"podman-network-librechat_default.service"
"podman-volume-librechat_images.service"
"podman-volume-librechat_uploads.service"
"podman-volume-librechat_logs.service"
"podman-librechat-mongodb.service"
"podman-librechat-meilisearch.service"
"podman-librechat-rag-api.service"
];
requires = [
"podman-network-librechat_default.service"
"podman-volume-librechat_images.service"
"podman-volume-librechat_uploads.service"
"podman-volume-librechat_logs.service"
"podman-librechat-mongodb.service"
"podman-librechat-meilisearch.service"
"podman-librechat-rag-api.service"
];
partOf = [ "podman-compose-librechat-root.target" ];
wantedBy = [ "podman-compose-librechat-root.target" ];
};
podman-network-librechat_default = {
path = [ pkgs.podman ];
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
ExecStop = "podman network rm -f librechat_default";
};
script = ''
podman network inspect librechat_default || podman network create librechat_default
'';
partOf = [ "podman-compose-librechat-root.target" ];
wantedBy = [ "podman-compose-librechat-root.target" ];
};
podman-volume-librechat_mongodb_data = {
path = [ pkgs.podman ];
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
};
script = "podman volume inspect librechat_mongodb_data || podman volume create librechat_mongodb_data";
partOf = [ "podman-compose-librechat-root.target" ];
wantedBy = [ "podman-compose-librechat-root.target" ];
};
podman-volume-librechat_meili_data = {
path = [ pkgs.podman ];
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
};
script = "podman volume inspect librechat_meili_data || podman volume create librechat_meili_data";
partOf = [ "podman-compose-librechat-root.target" ];
wantedBy = [ "podman-compose-librechat-root.target" ];
};
podman-volume-librechat_pgdata = {
path = [ pkgs.podman ];
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
};
script = "podman volume inspect librechat_pgdata || podman volume create librechat_pgdata";
partOf = [ "podman-compose-librechat-root.target" ];
wantedBy = [ "podman-compose-librechat-root.target" ];
};
podman-volume-librechat_images = {
path = [ pkgs.podman ];
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
};
script = "podman volume inspect librechat_images || podman volume create librechat_images";
partOf = [ "podman-compose-librechat-root.target" ];
wantedBy = [ "podman-compose-librechat-root.target" ];
};
podman-volume-librechat_uploads = {
path = [ pkgs.podman ];
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
};
script = "podman volume inspect librechat_uploads || podman volume create librechat_uploads";
partOf = [ "podman-compose-librechat-root.target" ];
wantedBy = [ "podman-compose-librechat-root.target" ];
};
podman-volume-librechat_logs = {
path = [ pkgs.podman ];
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
};
script = "podman volume inspect librechat_logs || podman volume create librechat_logs";
partOf = [ "podman-compose-librechat-root.target" ];
wantedBy = [ "podman-compose-librechat-root.target" ];
};
};
targets.podman-compose-librechat-root = {
unitConfig.Description = "Root target generated by compose2nix.";
wantedBy = [ "multi-user.target" ];
};
};
};
}

View file

@ -8,7 +8,7 @@
let let
cfg = config.mailserver; cfg = config.mailserver;
inherit (config.networking) domain; domain = config.networking.domain;
fqdn = "${cfg.subdomain}.${domain}"; fqdn = "${cfg.subdomain}.${domain}";
inherit (lib) inherit (lib)
@ -74,7 +74,7 @@ in
nameValuePair "${user}@${domain}" { nameValuePair "${user}@${domain}" {
name = "${user}@${domain}"; name = "${user}@${domain}";
aliases = map (alias: "${alias}@${domain}") (accConf.aliases or [ ]); aliases = map (alias: "${alias}@${domain}") (accConf.aliases or [ ]);
inherit (accConf) sendOnly; sendOnly = accConf.sendOnly;
quota = mkDefault "5G"; quota = mkDefault "5G";
hashedPasswordFile = config.sops.secrets."mailserver/accounts/${user}".path; hashedPasswordFile = config.sops.secrets."mailserver/accounts/${user}".path;
} }

View file

@ -23,7 +23,7 @@ let
services."mautrix-${name}" = { services."mautrix-${name}" = {
enable = true; enable = true;
inherit (cfg.bridges.${name}) package; package = cfg.bridges.${name}.package;
environmentFile = mkIf cfg.sops config.sops.templates."mautrix-${name}/env-file".path; environmentFile = mkIf cfg.sops config.sops.templates."mautrix-${name}/env-file".path;
settings = { settings = {
bridge = { bridge = {
@ -71,29 +71,33 @@ let
mode = "0400"; mode = "0400";
in in
{ {
secrets = { secrets."mautrix-${name}/encryption-pickle-key" = {
"mautrix-${name}/encryption-pickle-key" = { inherit owner group mode;
inherit owner group mode; };
}; secrets."mautrix-${name}/provisioning-shared-secret" = {
"mautrix-${name}/provisioning-shared-secret" = { inherit owner group mode;
inherit owner group mode; };
}; secrets."mautrix-${name}/public-media-signing-key" = {
"mautrix-${name}/public-media-signing-key" = { inherit owner group mode;
inherit owner group mode; };
}; secrets."mautrix-${name}/direct-media-server-key" = {
"mautrix-${name}/direct-media-server-key" = { inherit owner group mode;
inherit owner group mode;
};
}; };
templates."mautrix-${name}/env-file" = { templates."mautrix-${name}/env-file" = {
inherit owner group mode; inherit owner group mode;
content = '' content = ''
MAUTRIX_${toUpper name}_ENCRYPTION_PICKLE_KEY=${config.sops.placeholder."mautrix-${name}/encryption-pickle-key"} MAUTRIX_${toUpper name}_ENCRYPTION_PICKLE_KEY=${
config.sops.placeholder."mautrix-${name}/encryption-pickle-key"
}
MAUTRIX_${toUpper name}_PROVISIONING_SHARED_SECRET=${ MAUTRIX_${toUpper name}_PROVISIONING_SHARED_SECRET=${
config.sops.placeholder."mautrix-${name}/provisioning-shared-secret" config.sops.placeholder."mautrix-${name}/provisioning-shared-secret"
} }
MAUTRIX_${toUpper name}_PUBLIC_MEDIA_SIGNING_KEY=${config.sops.placeholder."mautrix-${name}/public-media-signing-key"} MAUTRIX_${toUpper name}_PUBLIC_MEDIA_SIGNING_KEY=${
MAUTRIX_${toUpper name}_DIRECT_MEDIA_SERVER_KEY=${config.sops.placeholder."mautrix-${name}/direct-media-server-key"} config.sops.placeholder."mautrix-${name}/public-media-signing-key"
}
MAUTRIX_${toUpper name}_DIRECT_MEDIA_SERVER_KEY=${
config.sops.placeholder."mautrix-${name}/direct-media-server-key"
}
''; '';
}; };
} }

View file

@ -84,74 +84,77 @@ in
}; };
config = mkIf cfg.enable { config = mkIf cfg.enable {
services = { services.postgresql = {
postgresql = { enable = true;
enable = true; initialScript = pkgs.writeText "synapse-init.sql" ''
initialScript = pkgs.writeText "synapse-init.sql" '' CREATE ROLE "matrix-synapse" WITH LOGIN PASSWORD 'synapse';
CREATE ROLE "matrix-synapse" WITH LOGIN PASSWORD 'synapse'; CREATE DATABASE "matrix-synapse" WITH OWNER "matrix-synapse"
CREATE DATABASE "matrix-synapse" WITH OWNER "matrix-synapse" TEMPLATE template0
TEMPLATE template0 LC_COLLATE = 'C'
LC_COLLATE = 'C' LC_CTYPE = 'C';
LC_CTYPE = 'C'; '';
''; };
};
matrix-synapse = mkMerge [ services.matrix-synapse = mkMerge [
{ {
settings = { settings = {
registration_shared_secret_path = mkIf cfg.sops config.sops.secrets."matrix/registration-shared-secret".path; registration_shared_secret_path =
server_name = config.networking.domain; mkIf cfg.sops
public_baseurl = baseUrl; config.sops.secrets."matrix/registration-shared-secret".path;
listeners = [ server_name = config.networking.domain;
{ public_baseurl = baseUrl;
inherit (cfg) port; listeners = [
bind_addresses = [ "127.0.0.1" ]; {
resources = [ inherit (cfg) port;
{ bind_addresses = [ "127.0.0.1" ];
compress = true; resources = [
names = [ "client" ]; {
} compress = true;
{ names = [ "client" ];
compress = false; }
names = [ "federation" ]; {
} compress = false;
]; names = [ "federation" ];
tls = false; }
type = "http"; ];
x_forwarded = true; tls = false;
} type = "http";
]; x_forwarded = true;
}; }
} ];
(mkIf cfg.coturn.enable {
settings = {
turn_uris = with cfg.coturn; [
"turn:${realm}:${toString listening-port}?transport=udp"
"turn:${realm}:${toString listening-port}?transport=tcp"
"turn:${realm}:${toString tls-listening-port}?transport=udp"
"turn:${realm}:${toString tls-listening-port}?transport=tcp"
"turn:${realm}:${toString alt-listening-port}?transport=udp"
"turn:${realm}:${toString alt-listening-port}?transport=tcp"
"turn:${realm}:${toString alt-tls-listening-port}?transport=udp"
"turn:${realm}:${toString alt-tls-listening-port}?transport=tcp"
];
extraConfigFiles = mkIf cfg.sops [ config.sops.templates."coturn/static-auth-secret.env".path ];
turn_user_lifetime = "1h";
};
})
];
nginx.virtualHosts."${cfg.settings.server_name}" = {
enableACME = true;
forceSSL = true;
locations = {
"= /.well-known/matrix/server".extraConfig = mkWellKnown serverConfig;
"= /.well-known/matrix/client".extraConfig = mkWellKnown clientConfig;
"/_matrix".proxyPass = "http://127.0.0.1:${toString cfg.port}";
"/_synapse".proxyPass = "http://127.0.0.1:${toString cfg.port}";
}; };
}; }
(mkIf cfg.coturn.enable {
settings = {
turn_uris = with cfg.coturn; [
"turn:${realm}:${toString listening-port}?transport=udp"
"turn:${realm}:${toString listening-port}?transport=tcp"
"turn:${realm}:${toString tls-listening-port}?transport=udp"
"turn:${realm}:${toString tls-listening-port}?transport=tcp"
"turn:${realm}:${toString alt-listening-port}?transport=udp"
"turn:${realm}:${toString alt-listening-port}?transport=tcp"
"turn:${realm}:${toString alt-tls-listening-port}?transport=udp"
"turn:${realm}:${toString alt-tls-listening-port}?transport=tcp"
];
extraConfigFiles = mkIf cfg.sops [ config.sops.templates."coturn/static-auth-secret.env".path ];
turn_user_lifetime = "1h";
};
})
];
environment.shellAliases = mkIf cfg.sops {
register_new_matrix_user = "${cfg.package}/bin/register_new_matrix_user -k $(sudo cat ${cfg.settings.registration_shared_secret_path})";
};
services.nginx.virtualHosts."${cfg.settings.server_name}" = {
enableACME = true;
forceSSL = true;
locations."= /.well-known/matrix/server".extraConfig = mkWellKnown serverConfig;
locations."= /.well-known/matrix/client".extraConfig = mkWellKnown clientConfig;
locations."/_matrix".proxyPass = "http://127.0.0.1:${toString cfg.port}";
locations."/_synapse".proxyPass = "http://127.0.0.1:${toString cfg.port}";
}; };
sops = mkIf cfg.sops { sops = mkIf cfg.sops {

View file

@ -6,53 +6,51 @@
let let
cfg = config.services.matrix-synapse; cfg = config.services.matrix-synapse;
inherit (config.networking) domain; domain = config.networking.domain;
inherit (lib) mkIf mkDefault; inherit (lib) mkIf mkDefault;
in in
{ {
config = mkIf cfg.enable { config = mkIf cfg.enable {
services = { services.livekit = {
livekit = { enable = true;
enable = true; settings.port = mkDefault 7880;
settings.port = mkDefault 7880; settings.room.auto_create = mkDefault false;
settings.room.auto_create = mkDefault false; openFirewall = mkDefault true;
openFirewall = mkDefault true; keyFile = mkIf cfg.sops config.sops.templates."livekit/key".path;
keyFile = mkIf cfg.sops config.sops.templates."livekit/key".path; };
};
lk-jwt-service = { services.lk-jwt-service = {
enable = true; enable = true;
port = mkDefault 8080; port = mkDefault 8080;
livekitUrl = "wss://${domain}/livekit/sfu"; livekitUrl = "wss://${domain}/livekit/sfu";
keyFile = mkIf cfg.sops config.sops.templates."livekit/key".path; keyFile = mkIf cfg.sops config.sops.templates."livekit/key".path;
};
nginx.virtualHosts = {
"${domain}".locations = {
"^~ /livekit/jwt/" = {
priority = 400;
proxyPass = "http://127.0.0.1:${toString config.services.lk-jwt-service.port}/";
};
"^~ /livekit/sfu/" = {
priority = 400;
proxyPass = "http://127.0.0.1:${toString config.services.livekit.settings.port}/";
proxyWebsockets = true;
extraConfig = ''
proxy_send_timeout 120;
proxy_read_timeout 120;
proxy_buffering off;
proxy_set_header Accept-Encoding gzip;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
'';
};
};
};
}; };
systemd.services.lk-jwt-service.environment.LIVEKIT_FULL_ACCESS_HOMESERVERS = domain; systemd.services.lk-jwt-service.environment.LIVEKIT_FULL_ACCESS_HOMESERVERS = domain;
services.nginx.virtualHosts = {
"${domain}".locations = {
"^~ /livekit/jwt/" = {
priority = 400;
proxyPass = "http://127.0.0.1:${toString config.services.lk-jwt-service.port}/";
};
"^~ /livekit/sfu/" = {
priority = 400;
proxyPass = "http://127.0.0.1:${toString config.services.livekit.settings.port}/";
proxyWebsockets = true;
extraConfig = ''
proxy_send_timeout 120;
proxy_read_timeout 120;
proxy_buffering off;
proxy_set_header Accept-Encoding gzip;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
'';
};
};
};
sops = mkIf cfg.sops { sops = mkIf cfg.sops {
secrets."livekit/key" = { }; secrets."livekit/key" = { };
templates."livekit/key".content = '' templates."livekit/key".content = ''

View file

@ -76,7 +76,7 @@ in
sops = mkIf cfg.sops ( sops = mkIf cfg.sops (
let let
owner = user.name; owner = user.name;
inherit (user) group; group = user.group;
mode = "0400"; mode = "0400";
in in
{ {
@ -98,7 +98,9 @@ in
'' ''
admins: admins:
'' ''
+ concatLines (map (admin: " ${admin}: ${config.sops.placeholder."maubot/admins/${admin}"}") cfg.admins) + concatLines (
map (admin: " ${admin}: ${config.sops.placeholder."maubot/admins/${admin}"}") cfg.admins
)
); );
}; };
} }

View file

@ -28,12 +28,6 @@ in
default = null; default = null;
}; };
port = mkOption {
type = types.port;
default = 8000;
description = "Port on which the mcpo service should listen.";
};
user = mkOption { user = mkOption {
type = types.str; type = types.str;
description = "The user the mcpo service will run as."; description = "The user the mcpo service will run as.";
@ -96,7 +90,7 @@ in
users.users."${cfg.user}" = { users.users."${cfg.user}" = {
isSystemUser = true; isSystemUser = true;
inherit (cfg) group; group = cfg.group;
}; };
users.groups."${cfg.group}" = { }; users.groups."${cfg.group}" = { };
@ -106,7 +100,7 @@ in
after = [ "network.target" ]; after = [ "network.target" ];
wantedBy = [ "multi-user.target" ]; wantedBy = [ "multi-user.target" ];
serviceConfig = { serviceConfig = {
ExecStart = "${getExe cfg.package} --port ${toString cfg.port} --config ${configFile}"; ExecStart = "${getExe cfg.package} --config ${configFile}";
Restart = "on-failure"; Restart = "on-failure";
User = cfg.user; User = cfg.user;
Group = cfg.group; Group = cfg.group;

View file

@ -6,8 +6,8 @@
let let
cfg = config.services.miniflux; cfg = config.services.miniflux;
inherit (config.networking) domain; domain = config.networking.domain;
inherit (cfg.reverseProxy) subdomain; subdomain = cfg.reverseProxy.subdomain;
fqdn = if (cfg.reverseProxy.enable && subdomain != "") then "${subdomain}.${domain}" else domain; fqdn = if (cfg.reverseProxy.enable && subdomain != "") then "${subdomain}.${domain}" else domain;
port = 8085; port = 8085;

View file

@ -50,7 +50,8 @@ in
"1.1.1.1" "1.1.1.1"
"2606:4700:4700::1111" "2606:4700:4700::1111"
]; ];
resolvers = if config.networking.nameservers == [ ] then cloudflare else config.networking.nameservers; resolvers =
if config.networking.nameservers == [ ] then cloudflare else config.networking.nameservers;
in in
map escapeIPv6 resolvers; map escapeIPv6 resolvers;
@ -59,7 +60,7 @@ in
virtualHosts = { virtualHosts = {
"${config.networking.domain}" = mkDefault { "${config.networking.domain}" = mkDefault {
enableACME = cfg.forceSSL; enableACME = cfg.forceSSL;
inherit (cfg) forceSSL; forceSSL = cfg.forceSSL;
}; };
}; };
}; };

View file

@ -21,7 +21,7 @@ in
types.submodule { types.submodule {
options = { options = {
extraGroups = mkOption { extraGroups = mkOption {
type = types.listOf types.str; type = (types.listOf types.str);
default = [ ]; default = [ ];
description = "Extra groups for the user"; description = "Extra groups for the user";
example = [ "wheel" ]; example = [ "wheel" ];
@ -37,7 +37,7 @@ in
description = "Initial password for the user"; description = "Initial password for the user";
}; };
sshKeyFiles = mkOption { sshKeyFiles = mkOption {
type = types.listOf types.path; type = (types.listOf types.path);
default = [ ]; default = [ ];
description = "SSH key files for the user"; description = "SSH key files for the user";
example = [ "/path/to/id_rsa.pub" ]; example = [ "/path/to/id_rsa.pub" ];

View file

@ -1,56 +0,0 @@
{ config, lib, ... }:
let
cfg = config.services.nostr-relay;
inherit (config.networking) domain;
inherit (cfg.reverseProxy) subdomain;
fqdn = if (cfg.reverseProxy.enable && subdomain != "") then "${subdomain}.${domain}" else domain;
inherit (lib)
mkDefault
mkIf
;
inherit (lib.utils)
mkReverseProxyOption
;
in
{
options.services.nostr-relay = {
reverseProxy = mkReverseProxyOption "Nostr Relay" "nostr";
};
config = mkIf cfg.enable {
services.nostr-rs-relay = {
settings = {
network = {
port = mkDefault 12849;
host = mkDefault (if cfg.reverseProxy.enable then "127.0.0.1" else "0.0.0.0");
};
limits = {
max_event_size = mkDefault 65536;
max_subscriptions = mkDefault 20;
max_filters = mkDefault 10;
max_subid_length = mkDefault 128;
max_event_tags = mkDefault 2000;
max_content_length = mkDefault 32768;
};
federation = {
enabled = mkDefault true;
max_message_size = mkDefault 1048576;
};
database = {
max_query_time_ms = mkDefault 5000;
};
};
};
services.nginx.virtualHosts = mkIf cfg.reverseProxy.enable {
"${fqdn}" = {
enableACME = cfg.reverseProxy.forceSSL;
inherit (cfg.reverseProxy) forceSSL;
locations."/".proxyPass = "http://127.0.0.1:${toString config.services.nostr-rs-relay.settings.network.port}";
};
};
};
}

View file

@ -9,26 +9,20 @@ let
inherit (lib) mkDefault; inherit (lib) mkDefault;
in in
{ {
boot = { boot.blacklistedKernelModules = [ "nouveau" ];
blacklistedKernelModules = [ "nouveau" ]; boot.extraModulePackages = [ config.hardware.nvidia.package ];
extraModulePackages = [ config.hardware.nvidia.package ]; boot.initrd.kernelModules = [ "nvidia" ];
initrd.kernelModules = [ "nvidia" ];
};
environment.systemPackages = with pkgs; [ environment.systemPackages = with pkgs; [
nvtopPackages.nvidia nvtopPackages.nvidia
]; ];
hardware = { hardware.enableRedistributableFirmware = true;
enableRedistributableFirmware = true; hardware.graphics.enable = true;
graphics.enable = true; hardware.nvidia.modesetting.enable = true;
nvidia = { hardware.nvidia.nvidiaSettings = true;
modesetting.enable = true; hardware.nvidia.open = false;
nvidiaSettings = true; hardware.nvidia.package = mkDefault config.boot.kernelPackages.nvidiaPackages.latest;
open = false;
package = mkDefault config.boot.kernelPackages.nvidiaPackages.latest;
};
};
nixpkgs.config.cudaSupport = true; nixpkgs.config.cudaSupport = true;

View file

@ -2,8 +2,8 @@
let let
cfg = config.services.ollama; cfg = config.services.ollama;
inherit (config.networking) domain; domain = config.networking.domain;
inherit (cfg.reverseProxy) subdomain; subdomain = cfg.reverseProxy.subdomain;
fqdn = if (cfg.reverseProxy.enable && subdomain != "") then "${subdomain}.${domain}" else domain; fqdn = if (cfg.reverseProxy.enable && subdomain != "") then "${subdomain}.${domain}" else domain;
inherit (lib) inherit (lib)
@ -31,7 +31,7 @@ in
services.nginx.virtualHosts = mkIf cfg.reverseProxy.enable { services.nginx.virtualHosts = mkIf cfg.reverseProxy.enable {
"${fqdn}" = mkVirtualHost { "${fqdn}" = mkVirtualHost {
inherit (cfg) port; port = cfg.port;
ssl = cfg.reverseProxy.forceSSL; ssl = cfg.reverseProxy.forceSSL;
recommendedProxySettings = mkForce false; recommendedProxySettings = mkForce false;
extraConfig = '' extraConfig = ''

View file

@ -10,10 +10,10 @@ let
image = pkgs.dockerTools.pullImage { image = pkgs.dockerTools.pullImage {
imageName = "ghcr.io/open-webui/open-webui"; imageName = "ghcr.io/open-webui/open-webui";
imageDigest = "sha256:a7e4796ae894d1e2a0c1824860ade472f35c507608a01c3581377b5c19b0ed49"; imageDigest = "sha256:2deb90b0423473d8f97febced2e62b8fd898aa3eb61877bb3aa336370214c258";
hash = "sha256-uhPlVXSxY6rGbYGvlPVV3zurmbI96mAHEuKKy9FFaD4="; hash = "sha256-2cdKfvZGUyPUm7TFXcf5OcG9ey4BvOZPUOVim1S2C+s=";
finalImageName = "ghcr.io/open-webui/open-webui"; finalImageName = "ghcr.io/open-webui/open-webui";
finalImageTag = "0.9.2"; finalImageTag = "0.8.5";
}; };
defaultEnv = { defaultEnv = {
@ -83,43 +83,10 @@ in
}; };
config = mkIf cfg.enable { config = mkIf cfg.enable {
virtualisation = { virtualisation.podman = {
podman = { enable = true;
enable = true; autoPrune.enable = true;
autoPrune.enable = true; dockerCompat = true;
dockerCompat = true;
};
oci-containers = {
backend = "podman";
containers."open-webui" = {
image = with cfg.image; imageName + ":" + imageTag;
imageFile = cfg.image;
environment =
defaultEnv
// cfg.environment
// {
PORT = "${toString cfg.port}";
CORS_ALLOW_ORIGIN = concatStringsSep ";" (
[
"http://localhost:${toString cfg.port}"
"http://127.0.0.1:${toString cfg.port}"
"http://0.0.0.0:${toString cfg.port}"
]
++ optional (cfg.externalUrl != null) cfg.externalUrl
);
};
environmentFiles = optional (cfg.environmentFile != null) cfg.environmentFile;
volumes = [
"open-webui_open-webui:/app/backend/data:rw"
];
log-driver = "journald";
extraOptions = [
"--network=host"
];
};
};
}; };
networking.firewall.interfaces = networking.firewall.interfaces =
@ -130,44 +97,70 @@ in
"${matchAll}".allowedUDPPorts = [ 53 ]; "${matchAll}".allowedUDPPorts = [ 53 ];
}; };
systemd = { virtualisation.oci-containers.backend = "podman";
services."podman-open-webui" = {
serviceConfig = {
Restart = mkOverride 90 "always";
};
after = [
"podman-volume-open-webui_open-webui.service"
];
requires = [
"podman-volume-open-webui_open-webui.service"
];
partOf = [
"podman-compose-open-webui-root.target"
];
wantedBy = [
"podman-compose-open-webui-root.target"
];
};
services."podman-volume-open-webui_open-webui" = { virtualisation.oci-containers.containers."open-webui" = {
path = [ pkgs.podman ]; image = with cfg.image; imageName + ":" + imageTag;
serviceConfig = { imageFile = cfg.image;
Type = "oneshot"; environment =
RemainAfterExit = true; defaultEnv
// cfg.environment
// {
PORT = "${toString cfg.port}";
CORS_ALLOW_ORIGIN = concatStringsSep ";" (
[
"http://localhost:${toString cfg.port}"
"http://127.0.0.1:${toString cfg.port}"
"http://0.0.0.0:${toString cfg.port}"
]
++ optional (cfg.externalUrl != null) cfg.externalUrl
);
}; };
script = '' environmentFiles = optional (cfg.environmentFile != null) cfg.environmentFile;
podman volume inspect open-webui_open-webui || podman volume create open-webui_open-webui volumes = [
''; "open-webui_open-webui:/app/backend/data:rw"
partOf = [ "podman-compose-open-webui-root.target" ]; ];
wantedBy = [ "podman-compose-open-webui-root.target" ]; log-driver = "journald";
extraOptions = [
"--network=host"
];
};
systemd.services."podman-open-webui" = {
serviceConfig = {
Restart = mkOverride 90 "always";
}; };
after = [
"podman-volume-open-webui_open-webui.service"
];
requires = [
"podman-volume-open-webui_open-webui.service"
];
partOf = [
"podman-compose-open-webui-root.target"
];
wantedBy = [
"podman-compose-open-webui-root.target"
];
};
targets."podman-compose-open-webui-root" = { systemd.services."podman-volume-open-webui_open-webui" = {
unitConfig = { path = [ pkgs.podman ];
Description = "Root target generated by compose2nix."; serviceConfig = {
}; Type = "oneshot";
wantedBy = [ "multi-user.target" ]; RemainAfterExit = true;
}; };
script = ''
podman volume inspect open-webui_open-webui || podman volume create open-webui_open-webui
'';
partOf = [ "podman-compose-open-webui-root.target" ];
wantedBy = [ "podman-compose-open-webui-root.target" ];
};
systemd.targets."podman-compose-open-webui-root" = {
unitConfig = {
Description = "Root target generated by compose2nix.";
};
wantedBy = [ "multi-user.target" ];
}; };
}; };
} }

View file

@ -7,8 +7,8 @@
let let
cfg = config.services.print-server; cfg = config.services.print-server;
inherit (config.networking) domain; domain = config.networking.domain;
inherit (cfg.reverseProxy) subdomain; subdomain = cfg.reverseProxy.subdomain;
fqdn = if (cfg.reverseProxy.enable && subdomain != "") then "${subdomain}.${domain}" else domain; fqdn = if (cfg.reverseProxy.enable && subdomain != "") then "${subdomain}.${domain}" else domain;
port = 631; port = 631;
@ -36,49 +36,47 @@ in
}; };
config = mkIf cfg.enable { config = mkIf cfg.enable {
services = { services.printing = {
printing = { enable = true;
enable = true; listenAddresses = [ "*:${builtins.toString port}" ];
listenAddresses = [ "*:${builtins.toString port}" ]; webInterface = true;
webInterface = true; tempDir = "/tmp/cups";
tempDir = "/tmp/cups"; allowFrom = [ "all" ];
allowFrom = [ "all" ]; snmpConf = ''
snmpConf = '' Address @LOCAL
Address @LOCAL '';
''; clientConf = "";
clientConf = ""; openFirewall = cfg.openFirewall;
inherit (cfg) openFirewall; drivers = with pkgs; [
drivers = with pkgs; [ brlaser
brlaser brgenml1lpr
brgenml1lpr brgenml1cupswrapper # Brother
brgenml1cupswrapper # Brother postscript-lexmark # Lexmark
postscript-lexmark # Lexmark hplip
hplip hplipWithPlugin # HP
hplipWithPlugin # HP splix
splix samsung-unified-linux-driver # Samsung
samsung-unified-linux-driver # Samsung gutenprint
gutenprint gutenprintBin # different vendors
gutenprintBin # different vendors ];
]; defaultShared = true;
defaultShared = true; browsing = true;
browsing = true; browsedConf = ''
browsedConf = '' BrowsePoll ${fqdn}
BrowsePoll ${fqdn} '';
''; };
};
# autodiscovery of network printers # autodiscovery of network printers
avahi = { services.avahi = {
enable = true; enable = true;
nssmdns4 = true; nssmdns4 = true;
inherit (cfg) openFirewall; openFirewall = cfg.openFirewall;
}; };
nginx.virtualHosts = mkIf cfg.reverseProxy.enable { services.nginx.virtualHosts = mkIf cfg.reverseProxy.enable {
${fqdn} = mkVirtualHost { ${fqdn} = mkVirtualHost {
inherit port; inherit port;
ssl = cfg.reverseProxy.forceSSL; ssl = cfg.reverseProxy.forceSSL;
};
}; };
}; };
}; };

View file

@ -7,8 +7,8 @@
let let
cfg = config.services.radicale; cfg = config.services.radicale;
inherit (config.networking) domain; domain = config.networking.domain;
inherit (cfg.reverseProxy) subdomain; subdomain = cfg.reverseProxy.subdomain;
fqdn = if (cfg.reverseProxy.enable && subdomain != "") then "${subdomain}.${domain}" else domain; fqdn = if (cfg.reverseProxy.enable && subdomain != "") then "${subdomain}.${domain}" else domain;
port = 5232; port = 5232;
@ -63,7 +63,7 @@ in
services.nginx.virtualHosts = mkIf cfg.reverseProxy.enable { services.nginx.virtualHosts = mkIf cfg.reverseProxy.enable {
"${fqdn}" = { "${fqdn}" = {
inherit (cfg.reverseProxy) forceSSL; forceSSL = cfg.reverseProxy.forceSSL;
enableACME = cfg.reverseProxy.forceSSL; enableACME = cfg.reverseProxy.forceSSL;
locations = { locations = {
"/" = { "/" = {

View file

@ -2,8 +2,8 @@
let let
cfg = config.services.rss-bridge; cfg = config.services.rss-bridge;
inherit (config.networking) domain; domain = config.networking.domain;
inherit (cfg.reverseProxy) subdomain; subdomain = cfg.reverseProxy.subdomain;
fqdn = if (cfg.reverseProxy.enable && subdomain != "") then "${subdomain}.${domain}" else domain; fqdn = if (cfg.reverseProxy.enable && subdomain != "") then "${subdomain}.${domain}" else domain;
inherit (lib) inherit (lib)
@ -12,6 +12,7 @@ let
inherit (lib.utils) inherit (lib.utils)
mkReverseProxyOption mkReverseProxyOption
mkVirtualHost
; ;
in in
{ {
@ -31,9 +32,8 @@ in
systemd.tmpfiles.rules = [ "d ${cfg.dataDir} 0755 ${cfg.user} ${cfg.group} -" ]; systemd.tmpfiles.rules = [ "d ${cfg.dataDir} 0755 ${cfg.user} ${cfg.group} -" ];
services.nginx.virtualHosts = mkIf cfg.reverseProxy.enable { services.nginx.virtualHosts = mkIf cfg.reverseProxy.enable {
"${fqdn}" = { "${cfg.virtualHost}" = mkVirtualHost {
enableACME = cfg.reverseProxy.forceSSL; ssl = cfg.reverseProxy.forceSSL;
inherit (cfg.reverseProxy) forceSSL;
}; };
}; };
}; };

View file

@ -1,17 +1,9 @@
{ { config, lib, ... }:
config,
lib,
pkgs,
...
}:
let let
cfg = config.services.tailscale; cfg = config.services.tailscale;
inherit (lib) inherit (lib)
concatStrings
filterAttrs
mapAttrsToList
mkIf mkIf
mkOption mkOption
optional optional
@ -20,122 +12,39 @@ let
in in
{ {
options.services.tailscale = { options.services.tailscale = {
tailnets = mkOption { loginServer = mkOption {
default = { }; type = types.str;
type = types.attrsOf ( description = "The Tailscale login server to use.";
types.submodule { };
options = { enableSSH = mkOption {
loginServer = mkOption { type = types.bool;
type = types.str; default = false;
description = "Login server for this tailnet."; description = "Enable Tailscale SSH functionality.";
}; };
authKeyFile = mkOption { acceptDNS = mkOption {
type = types.nullOr types.str; type = types.bool;
default = null; default = true;
description = "Path to auth key secret."; description = "Enable Tailscale's MagicDNS and custom DNS configuration.";
};
enableSSH = mkOption {
type = types.bool;
default = false;
};
acceptDNS = mkOption {
type = types.bool;
default = true;
};
default = mkOption {
type = types.bool;
default = false;
description = "Connect to this tailnet on boot.";
};
};
}
);
}; };
}; };
config = config = mkIf cfg.enable {
let services.tailscale = {
defaultTailnets = filterAttrs (_: t: t.default) cfg.tailnets; authKeyFile = config.sops.secrets."tailscale/auth-key".path;
defaultTailnet = if defaultTailnets == { } then null else builtins.head (builtins.attrValues defaultTailnets); extraSetFlags = optional cfg.enableSSH "--ssh" ++ optional cfg.acceptDNS "--accept-dns";
extraUpFlags = [
entries = mapAttrsToList (name: tcfg: '' "--login-server=${cfg.loginServer}"
TAILNETS["${name}"]="${tcfg.loginServer}|${if tcfg.enableSSH then "true" else "false"}|${ ]
if tcfg.acceptDNS then "true" else "false" ++ optional cfg.enableSSH "--ssh"
}|${if tcfg.authKeyFile != null then tcfg.authKeyFile else ""}" ++ optional cfg.acceptDNS "--accept-dns";
'') cfg.tailnets;
tailnetSwitchCli = pkgs.writeShellScriptBin "tailnet-switch" ''
set -euo pipefail
TAILSCALE="${cfg.package}/bin/tailscale"
declare -A TAILNETS
${concatStrings entries}
CHOICE="''${1:-}"
if [[ -z "$CHOICE" ]]; then
if [[ -t 0 ]]; then
CHOICE=$(printf '%s\n' "''${!TAILNETS[@]}" | sort | ${pkgs.fzf}/bin/fzf --prompt="Switch tailnet: ")
else
echo "Usage: tailnet-switch <tailnet-name>" >&2
printf 'Available tailnets: %s\n' "''${!TAILNETS[@]}" >&2
exit 1
fi
fi
if [[ -z "''${TAILNETS[$CHOICE]+x}" ]]; then
echo "Unknown tailnet: $CHOICE" >&2
printf 'Available tailnets: %s\n' "''${!TAILNETS[@]}" >&2
exit 1
fi
IFS='|' read -r LOGIN_SERVER ENABLE_SSH ACCEPT_DNS AUTH_KEY_FILE <<< "''${TAILNETS[$CHOICE]}"
echo "Switching to: $CHOICE ($LOGIN_SERVER)"
"$TAILSCALE" down --accept-risk=lose-ssh 2>/dev/null || true
"$TAILSCALE" logout 2>/dev/null || true
UP_FLAGS=("--login-server=$LOGIN_SERVER")
[[ "$ENABLE_SSH" == "true" ]] && UP_FLAGS+=("--ssh")
[[ "$ACCEPT_DNS" == "true" ]] && UP_FLAGS+=("--accept-dns")
[[ -n "$AUTH_KEY_FILE" ]] && UP_FLAGS+=("--auth-key=$(cat "$AUTH_KEY_FILE")")
"$TAILSCALE" up "''${UP_FLAGS[@]}"
echo "Switched to tailnet: $CHOICE"
'';
in
mkIf cfg.enable {
assertions = [
{
assertion = (builtins.length (builtins.attrValues (filterAttrs (_: t: t.default) cfg.tailnets))) <= 1;
message = "services.tailscale.tailnets: Only one tailnet can be set as default.";
}
{
assertion = cfg.tailnets != { };
message = "services.tailscale.tailnets: At least one tailnet must be defined.";
}
];
services.tailscale = mkIf (defaultTailnet != null) (
with defaultTailnet;
{
inherit authKeyFile;
extraSetFlags = optional enableSSH "--ssh" ++ optional acceptDNS "--accept-dns";
extraUpFlags = [
"--login-server=${loginServer}"
]
++ optional enableSSH "--ssh"
++ optional acceptDNS "--accept-dns";
}
);
environment.systemPackages = [ tailnetSwitchCli ];
environment.shellAliases.ts = "${cfg.package}/bin/tailscale";
networking.firewall.trustedInterfaces = [ cfg.interfaceName ];
}; };
environment.shellAliases = {
ts = "${cfg.package}/bin/tailscale";
};
networking.firewall.trustedInterfaces = [ cfg.interfaceName ];
sops.secrets."tailscale/auth-key" = { };
};
} }

View file

@ -58,18 +58,16 @@ in
enable = mkDefault true; enable = mkDefault true;
onBoot = mkDefault "ignore"; onBoot = mkDefault "ignore";
onShutdown = mkDefault "shutdown"; onShutdown = mkDefault "shutdown";
qemu = { qemu.runAsRoot = mkDefault false;
runAsRoot = mkDefault false; qemu.verbatimConfig = ''
verbatimConfig = '' clear_emulation_capabilities = ${boolToZeroOne cfg.libvirtd.clearEmulationCapabilities}
clear_emulation_capabilities = ${boolToZeroOne cfg.libvirtd.clearEmulationCapabilities} ''
'' + optionalString (cfg.libvirtd.deviceACL != [ ]) ''
+ optionalString (cfg.libvirtd.deviceACL != [ ]) '' cgroup_device_acl = [
cgroup_device_acl = [ ${aclString}
${aclString} ]
] '';
''; qemu.swtpm.enable = mkDefault true; # TPM 2.0
swtpm.enable = mkDefault true; # TPM 2.0
};
}; };
spiceUSBRedirection.enable = mkDefault true; spiceUSBRedirection.enable = mkDefault true;
}; };

View file

@ -1,4 +1,3 @@
#!/usr/bin/env bash
shopt -s nullglob shopt -s nullglob
for d in /sys/kernel/iommu_groups/*/devices/*; do for d in /sys/kernel/iommu_groups/*/devices/*; do
n=${d#*/iommu_groups/*}; n=${n%%/*} n=${d#*/iommu_groups/*}; n=${n%%/*}

View file

@ -78,32 +78,34 @@ let
}; };
}; };
deviceType = types.submodule ( deviceType = (
{ config, options, ... }: types.submodule (
{ { config, options, ... }:
options = { {
resolution = mkOption { options = {
type = types.nullOr resolutionType; resolution = mkOption {
default = null; type = types.nullOr resolutionType;
description = "Automatically calculate the minimum device size for a specific resolution. Overrides `size` if set."; default = null;
description = "Automatically calculate the minimum device size for a specific resolution. Overrides `size` if set.";
};
size = mkOption {
type = types.number;
description = "Size for the kvmfr device in megabytes.";
};
permissions = mkOption {
type = permissionsType;
default = { };
description = "Permissions of the kvmfr device.";
};
}; };
size = mkOption { config = {
type = types.number; size = mkIf (config.resolution != null) (sizeFromResolution config.resolution);
description = "Size for the kvmfr device in megabytes.";
}; };
}
permissions = mkOption { )
type = permissionsType;
default = { };
description = "Permissions of the kvmfr device.";
};
};
config = {
size = mkIf (config.resolution != null) (sizeFromResolution config.resolution);
};
}
); );
inherit (lib) inherit (lib)
@ -142,7 +144,7 @@ in
''; '';
"modprobe.d/kvmfr.conf".text = '' "modprobe.d/kvmfr.conf".text = ''
options kvmfr static_size_mb=${concatStringsSep "," (map toString deviceSizes)} options kvmfr static_size_mb=${concatStringsSep "," (map (size: toString size) deviceSizes)}
''; '';
"apparmor.d/local/abstractions/libvirt-qemu" = mkIf config.security.apparmor.enable { "apparmor.d/local/abstractions/libvirt-qemu" = mkIf config.security.apparmor.enable {

View file

@ -2,7 +2,7 @@
let let
cfg = config.services.webPage; cfg = config.services.webPage;
inherit (config.networking) domain; domain = config.networking.domain;
fqdn = if (cfg.subdomain != "") then "${cfg.subdomain}.${domain}" else domain; fqdn = if (cfg.subdomain != "") then "${cfg.subdomain}.${domain}" else domain;
nginxUser = config.services.nginx.user; nginxUser = config.services.nginx.user;
@ -41,7 +41,7 @@ in
config = mkIf cfg.enable { config = mkIf cfg.enable {
services.nginx.virtualHosts."${fqdn}" = { services.nginx.virtualHosts."${fqdn}" = {
enableACME = cfg.forceSSL; enableACME = cfg.forceSSL;
inherit (cfg) forceSSL; forceSSL = cfg.forceSSL;
root = cfg.webRoot; root = cfg.webRoot;
locations."/".index = "index.html"; locations."/".index = "index.html";
sslCertificate = mkIf cfg.forceSSL "${config.security.acme.certs."${fqdn}".directory}/cert.pem"; sslCertificate = mkIf cfg.forceSSL "${config.security.acme.certs."${fqdn}".directory}/cert.pem";

View file

@ -81,97 +81,90 @@ in
}; };
config = mkIf cfg.enable { config = mkIf cfg.enable {
systemd = { systemd.tmpfiles.rules = [ "d ${cfg.volume} 0755 root podman -" ];
tmpfiles.rules = [ "d ${cfg.volume} 0755 root podman -" ];
services = { virtualisation.podman = {
"podman-windows" = { enable = true;
serviceConfig = { autoPrune.enable = true;
Restart = mkOverride 90 "always"; dockerCompat = true;
}; defaultNetwork.settings = {
after = [ dns_enabled = true;
"podman-network-windows_default.service"
];
requires = [
"podman-network-windows_default.service"
];
partOf = [
"podman-compose-windows-root.target"
];
wantedBy = [
"podman-compose-windows-root.target"
];
};
"podman-network-windows_default" = {
path = [ pkgs.podman ];
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
ExecStop = "podman network rm -f windows_default";
};
script = ''
podman network inspect windows_default || podman network create windows_default
'';
partOf = [ "podman-compose-windows-root.target" ];
wantedBy = [ "podman-compose-windows-root.target" ];
};
};
targets."podman-compose-windows-root" = {
unitConfig = {
Description = "Root target generated by compose2nix.";
};
wantedBy = [ "multi-user.target" ];
};
};
virtualisation = {
podman = {
enable = true;
autoPrune.enable = true;
dockerCompat = true;
defaultNetwork.settings = {
dns_enabled = true;
};
};
oci-containers = {
backend = "podman";
containers."windows" = {
image = "dockurr/windows";
environment = with cfg.settings; {
"VERSION" = version;
"RAM_SIZE" = ramSize;
"CPU_CORES" = cpuCores;
"DISK_SIZE" = diskSize;
"USERNAME" = username;
"PASSWORD" = password;
"REGION" = region;
"KEYBOARD" = keyboard;
};
volumes = [
"${cfg.volume}:/storage:rw"
]
++ optional (cfg.sharedVolume != null) "${cfg.sharedVolume}:/shared:rw";
ports = [
"8006:8006/tcp"
"3389:3389/tcp"
"3389:3389/udp"
];
log-driver = "journald";
extraOptions = [
"--cap-add=NET_ADMIN"
"--device=/dev/kvm:/dev/kvm:rwm"
"--device=/dev/net/tun:/dev/net/tun:rwm"
"--network-alias=windows"
"--network=windows_default"
];
};
}; };
}; };
# https://github.com/NixOS/nixpkgs/issues/226365 # https://github.com/NixOS/nixpkgs/issues/226365
networking.firewall.interfaces."podman+".allowedUDPPorts = [ 53 ]; networking.firewall.interfaces."podman+".allowedUDPPorts = [ 53 ];
virtualisation.oci-containers.backend = "podman";
virtualisation.oci-containers.containers."windows" = {
image = "dockurr/windows";
environment = with cfg.settings; {
"VERSION" = version;
"RAM_SIZE" = ramSize;
"CPU_CORES" = cpuCores;
"DISK_SIZE" = diskSize;
"USERNAME" = username;
"PASSWORD" = password;
"LANGUAGE" = language;
"REGION" = region;
"KEYBOARD" = keyboard;
};
volumes = [
"${cfg.volume}:/storage:rw"
]
++ optional (cfg.sharedVolume != null) "${cfg.sharedVolume}:/shared:rw";
ports = [
"8006:8006/tcp"
"3389:3389/tcp"
"3389:3389/udp"
];
log-driver = "journald";
extraOptions = [
"--cap-add=NET_ADMIN"
"--device=/dev/kvm:/dev/kvm:rwm"
"--device=/dev/net/tun:/dev/net/tun:rwm"
"--network-alias=windows"
"--network=windows_default"
];
};
systemd.services."podman-windows" = {
serviceConfig = {
Restart = mkOverride 90 "always";
};
after = [
"podman-network-windows_default.service"
];
requires = [
"podman-network-windows_default.service"
];
partOf = [
"podman-compose-windows-root.target"
];
wantedBy = [
"podman-compose-windows-root.target"
];
};
systemd.services."podman-network-windows_default" = {
path = [ pkgs.podman ];
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
ExecStop = "podman network rm -f windows_default";
};
script = ''
podman network inspect windows_default || podman network create windows_default
'';
partOf = [ "podman-compose-windows-root.target" ];
wantedBy = [ "podman-compose-windows-root.target" ];
};
systemd.targets."podman-compose-windows-root" = {
unitConfig = {
Description = "Root target generated by compose2nix.";
};
wantedBy = [ "multi-user.target" ];
};
}; };
} }

View file

@ -0,0 +1,55 @@
{
config,
pkgs,
lib,
...
}:
# Set `networking.hostId` to:
# $ head -c 8 /etc/machine-id
# Mark datasets to snapshot:
# $ sudo zfs set com.sun:auto-snapshot:daily=true dpool/data/backup
# Generate SSH key for replication (empty passphrase):
# $ sudo -i ssh-keygen -t rsa -b 4096 -f /root/.ssh/zfs-replication
let
cfg = config.services.zfs;
inherit (lib) mkDefault mkForce;
in
{
boot.supportedFilesystems = [ "zfs" ];
boot.loader.systemd-boot.enable = mkForce false;
boot.loader.grub.enable = mkForce true;
boot.loader.grub.zfsSupport = mkForce true;
services.zfs.trim = {
enable = mkDefault true;
interval = mkDefault "weekly";
};
services.zfs.scrub = {
enable = mkDefault true;
interval = mkDefault "monthly";
};
services.zfs.autoSnapshot = {
enable = mkDefault true;
flags = mkDefault "-k -p --utc";
frequent = mkDefault 0;
hourly = mkDefault 24;
daily = mkDefault 7;
weekly = mkDefault 4;
monthly = mkDefault 0;
};
services.zfs.autoReplication = {
username = mkDefault "root";
identityFilePath = mkDefault "/root/.ssh/zfs-replication";
followDelete = mkDefault true;
};
environment.systemPackages = with pkgs; [ lz4 ];
}

151
modules/nixos/zfs/disks.sh Normal file
View file

@ -0,0 +1,151 @@
#!/usr/bin/env bash
declare -a SSDs=(
# '/dev/disk/by-id/abc123'
)
declare -a HDDs=(
# '/dev/disk/by-id/def456'
)
declare -a DATA_DATASETS=(
# 'dataset'
)
MNT='/mnt'
SWAP_GB=32
# Helper function to wait for devices
wait_for_device() {
local device=$1
echo "Waiting for device: $device ..."
while [[ ! -e $device ]]; do
sleep 1
done
echo "Device $device is ready."
}
# Function to install a package if it's not already installed
install_if_missing() {
local cmd="$1"
local package="$2"
if ! command -v "$cmd" &> /dev/null; then
echo "$cmd not found, installing $package..."
nix-env -iA "nixos.$package"
fi
}
install_if_missing "sgdisk" "gptfdisk"
install_if_missing "partprobe" "parted"
# Ensure swap parts are off
swapoff --all
udevadm settle
### SSDs ###
echo "Setting up SSDs..."
# Wait for SSD devices to be ready
for ssd in "${SSDs[@]}"; do
wait_for_device "$ssd"
done
# Wipe and partition SSDs
for i in "${!SSDs[@]}"; do
ssd="${SSDs[$i]}"
ssd_num=$((i + 1))
echo "Processing SSD $ssd_num: $ssd"
# Wipe filesystems
wipefs -a "$ssd"
# Clear part tables
sgdisk --zap-all "$ssd"
# Partition disk
sgdisk -n1:1M:+1G -t1:EF00 -c1:BOOT$ssd_num "$ssd"
sgdisk -n2:0:+"$SWAP_GB"G -t2:8200 -c2:SWAP$ssd_num "$ssd"
sgdisk -n3:0:0 -t3:BF00 -c3:ROOT$ssd_num "$ssd"
partprobe -s "$ssd"
udevadm settle
wait_for_device "${ssd}-part3"
done
# Create root pool
echo "Creating root pool..."
zpool create -f -o ashift=12 -o autotrim=on -R "$MNT" -O acltype=posixacl -O canmount=off -O dnodesize=auto -O normalization=formD -O relatime=on -O xattr=sa -O mountpoint=none rpool mirror "${SSDs[0]}"-part3 "${SSDs[1]}"-part3
# Create and mount root system container
zfs create -o canmount=noauto -o mountpoint=legacy rpool/root
mount -o X-mount.mkdir -t zfs rpool/root "$MNT"
# Create root datasets
declare -a ROOT_DATASETS=('home' 'nix' 'tmp' 'var')
for dataset in "${ROOT_DATASETS[@]}"; do
zfs create -o mountpoint=legacy "rpool/$dataset"
mount -o X-mount.mkdir -t zfs "rpool/$dataset" "$MNT/$dataset"
done
# Format boot and swap partitions
for i in "${!SSDs[@]}"; do
ssd="${SSDs[$i]}"
ssd_num=$((i + 1))
mkfs.vfat -F 32 -n BOOT$ssd_num "${ssd}"-part1
mkswap -L SWAP$ssd_num "${ssd}"-part2
swapon -L SWAP$ssd_num
done
# Mount first boot partition
mount -t vfat -o fmask=0077,dmask=0077,iocharset=iso8859-1,X-mount.mkdir -L BOOT1 "$MNT"/boot
### HDDs ###
echo "Setting up HDDs..."
# Wait for HDD devices to be ready
for hdd in "${HDDs[@]}"; do
wait_for_device "$hdd"
done
# Wipe and partition HDDs
for i in "${!HDDs[@]}"; do
hdd="${HDDs[$i]}"
hdd_num=$((i + 1))
echo "Processing HDD $hdd_num: $hdd"
wipefs -a "$hdd"
sgdisk --zap-all "$hdd"
sgdisk -n1:0:0 -t1:BF00 -c1:DATA$hdd_num "$hdd"
done
udevadm settle
# Wait for all HDD partitions to appear
for hdd in "${HDDs[@]}"; do
wait_for_device "${hdd}-part1"
done
# Create data pool
echo "Creating data pool..."
mkdir -p "$MNT"/data
hdd_partitions=()
for hdd in "${HDDs[@]}"; do
hdd_partitions+=("${hdd}-part1")
done
zpool create -f -o ashift=12 -o autotrim=on -R "$MNT" -O acltype=posixacl -O xattr=sa -O dnodesize=auto -O compression=lz4 -O normalization=formD -O relatime=on -O mountpoint=none dpool raidz "${hdd_partitions[@]}"
# Create and mount data root container
zfs create -o canmount=noauto -o mountpoint=legacy dpool/data
mount -o X-mount.mkdir -t zfs dpool/data "$MNT"/data
# Create and mount data datasets
for dataset in "${DATA_DATASETS[@]}"; do
zfs create -o mountpoint=legacy "dpool/data/$dataset"
mount -o X-mount.mkdir -t zfs "dpool/data/$dataset" "$MNT/data/$dataset"
done
echo "Setup complete."

View file

@ -14,78 +14,72 @@ let
; ;
in in
{ {
nix = { nix.package = mkDefault pkgs.nix;
package = mkDefault pkgs.nix;
# for `nix run synix#foo`, `nix build synix#bar`, etc # for `nix run synix#foo`, `nix build synix#bar`, etc
registry = { nix.registry = {
synix = { synix = {
from = { from = {
id = "synix"; id = "synix";
type = "indirect"; type = "indirect";
}; };
to = { to = {
owner = "sid"; owner = "sid";
repo = "synix"; repo = "synix";
host = "git.sid.ovh"; host = "git.sid.ovh";
type = "gitea"; type = "gitea";
};
}; };
}; };
};
settings = { # fallback quickly if substituters are not available.
warn-dirty = mkDefault false; nix.settings.connect-timeout = mkDefault 5;
nix.settings.fallback = true;
# fallback quickly if substituters are not available. nix.settings.experimental-features = [
connect-timeout = mkDefault 5; "nix-command"
fallback = true; "flakes"
]
++ optional (
config.nix.package != null && versionOlder (versions.majorMinor config.nix.package.version) "2.22"
) "repl-flake";
experimental-features = [ nix.settings.log-lines = mkDefault 25;
"nix-command"
"flakes"
]
++ optional (
config.nix.package != null && versionOlder (versions.majorMinor config.nix.package.version) "2.22"
) "repl-flake";
log-lines = mkDefault 25; # avoid disk full issues
nix.settings.max-free = mkDefault (3000 * 1024 * 1024);
nix.settings.min-free = mkDefault (512 * 1024 * 1024);
# avoid disk full issues # avoid copying unnecessary stuff over SSH
max-free = mkDefault (3000 * 1024 * 1024); nix.settings.builders-use-substitutes = true;
min-free = mkDefault (512 * 1024 * 1024);
# avoid copying unnecessary stuff over SSH # workaround for https://github.com/NixOS/nix/issues/9574
builders-use-substitutes = true; nix.settings.nix-path = config.nix.nixPath;
# workaround for https://github.com/NixOS/nix/issues/9574 nix.settings.download-buffer-size = 524288000; # 500 MiB
nix-path = config.nix.nixPath;
download-buffer-size = 524288000; # 500 MiB # add all wheel users to the trusted-users group
nix.settings.trusted-users = [
"@wheel"
];
# add all wheel users to the trusted-users group # binary caches
trusted-users = [ nix.settings.substituters = [
"@wheel" "https://cache.nixos.org"
]; "https://nix-community.cachix.org"
"https://cache.garnix.io"
"https://numtide.cachix.org"
];
nix.settings.trusted-public-keys = [
"cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY="
"nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs="
"cache.garnix.io:CTFPyKSLcx5RMJKfLo5EEPUObbA78b0YQ2DTCJXqr9g="
"numtide.cachix.org-1:2ps1kLBUWjxIneOy1Ik6cQjb41X0iXVXeHigGmycPPE="
];
# binary caches nix.gc = {
substituters = [ automatic = true;
"https://cache.nixos.org" dates = "weekly";
"https://nix-community.cachix.org" options = "--delete-older-than 30d";
"https://cache.garnix.io"
"https://numtide.cachix.org"
];
trusted-public-keys = [
"cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY="
"nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs="
"cache.garnix.io:CTFPyKSLcx5RMJKfLo5EEPUObbA78b0YQ2DTCJXqr9g="
"numtide.cachix.org-1:2ps1kLBUWjxIneOy1Ik6cQjb41X0iXVXeHigGmycPPE="
];
};
gc = {
automatic = true;
dates = "weekly";
options = "--delete-older-than 30d";
};
}; };
} }

View file

@ -9,17 +9,11 @@
modifications = final: prev: { modifications = final: prev: {
# https://github.com/NixOS/nixpkgs/issues/335003#issuecomment-2755803376 # https://github.com/NixOS/nixpkgs/issues/335003#issuecomment-2755803376
kicad = prev.kicad.override { kicad = (
stable = true; prev.kicad.override {
}; stable = true;
}
python312 = prev.python312.override { );
packageOverrides = pyFinal: pyPrev: {
arxiv = pyPrev.arxiv.overridePythonAttrs (old: {
pythonRemoveDeps = (old.pythonRemoveDeps or [ ]) ++ [ "requests" ];
});
};
};
# bemenu is not a valid selector in v1.5.1 # bemenu is not a valid selector in v1.5.1
rofi-rbw-wayland = prev.rofi-rbw-wayland.overrideAttrs (oldAttrs: { rofi-rbw-wayland = prev.rofi-rbw-wayland.overrideAttrs (oldAttrs: {

View file

@ -1,28 +1,25 @@
{ {
lib, lib,
python312, python3,
fetchPypi, fetchPypi,
}: }:
let python3.pkgs.buildPythonApplication rec {
python = python312;
in
python.pkgs.buildPythonApplication rec {
pname = "arxiv-mcp-server"; pname = "arxiv-mcp-server";
version = "0.5.0"; version = "0.3.1";
pyproject = true; pyproject = true;
src = fetchPypi { src = fetchPypi {
pname = "arxiv_mcp_server"; pname = "arxiv_mcp_server";
inherit version; inherit version;
hash = "sha256-vxrKyq+uOgVYtWrvikcIidLra6n0GFlFvfKztrc7GH8="; hash = "sha256-yGNetU7el6ZXsavD8uvO17OZtaPuYgzkxiVEk402GUs=";
}; };
build-system = [ build-system = [
python.pkgs.hatchling python3.pkgs.hatchling
]; ];
dependencies = with python.pkgs; [ dependencies = with python3.pkgs; [
aiofiles aiofiles
aiohttp aiohttp
anyio anyio
@ -39,7 +36,7 @@ python.pkgs.buildPythonApplication rec {
uvicorn uvicorn
]; ];
optional-dependencies = with python.pkgs; { optional-dependencies = with python3.pkgs; {
test = [ test = [
aioresponses aioresponses
pytest pytest
@ -51,7 +48,6 @@ python.pkgs.buildPythonApplication rec {
pythonRemoveDeps = [ pythonRemoveDeps = [
"black" "black"
"mcp"
]; ];
pythonImportsCheck = [ pythonImportsCheck = [

View file

@ -11,19 +11,17 @@
rustPlatform.buildRustPackage rec { rustPlatform.buildRustPackage rec {
pname = "baibot"; pname = "baibot";
version = "1.19.3"; version = "1.14.3";
src = fetchFromGitHub { src = fetchFromGitHub {
owner = "etkecc"; owner = "etkecc";
repo = "baibot"; repo = "baibot";
rev = "v${version}"; rev = "v${version}";
hash = "sha256-Fr1CvFocb/VAYQGykXXZ6CCfvC31bKB/tr1aoA4oIME="; hash = "sha256-bFUijsvwUQhISjWVoVvoDXNSDPaWZTunqUfxfgaxclM=";
}; };
cargoHash = "sha256-CNEkge585bzUUPMHCSJ1CAH5wx3Wttq9I3A3oqfBzis="; useFetchCargoVendor = true;
cargoHash = "sha256-/7KSCVWuxTk7gKOYxE/uQ5T0BnYlDYWOvdFXEiU9mB0=";
cargoBuildFlags = "--ignore-rust-version";
cargoTestFlags = "--ignore-rust-version";
nativeBuildInputs = [ nativeBuildInputs = [
pkg-config pkg-config

View file

@ -6,13 +6,13 @@
python3.pkgs.buildPythonApplication rec { python3.pkgs.buildPythonApplication rec {
pname = "blender-mcp"; pname = "blender-mcp";
version = "1.5.6"; version = "1.5.5";
pyproject = true; pyproject = true;
src = fetchPypi { src = fetchPypi {
pname = "blender_mcp"; pname = "blender_mcp";
inherit version; inherit version;
hash = "sha256-9aBGQcMC1ustj3H1WgRFCha6fk+XSASIzcZr6Rrgif4="; hash = "sha256-n8bYnrDVLe5zPvg8rbGgh5U5AeIm/DA6aPgmxS68d7g=";
}; };
build-system = [ build-system = [

View file

@ -11,20 +11,15 @@
cppman = pkgs.callPackage ./cppman { }; cppman = pkgs.callPackage ./cppman { };
fetcher-mcp = pkgs.callPackage ./fetcher-mcp { }; fetcher-mcp = pkgs.callPackage ./fetcher-mcp { };
freecad-mcp = pkgs.callPackage ./freecad-mcp { }; freecad-mcp = pkgs.callPackage ./freecad-mcp { };
jirafeau = pkgs.callPackage ./jirafeau { };
jirafeau-cli = pkgs.callPackage ./jirafeau-cli { };
kicad-mcp = pkgs.callPackage ./kicad-mcp { }; kicad-mcp = pkgs.callPackage ./kicad-mcp { };
mcpo = pkgs.callPackage ./mcpo { }; mcpo = pkgs.callPackage ./mcpo { };
nerdlog = pkgs.callPackage ./nerdlog { };
pass2bw = pkgs.callPackage ./pass2bw { }; pass2bw = pkgs.callPackage ./pass2bw { };
pyman = pkgs.callPackage ./pyman { }; pyman = pkgs.callPackage ./pyman { };
quicknote = pkgs.callPackage ./quicknote { }; quicknote = pkgs.callPackage ./quicknote { };
synapse_change_display_name = pkgs.callPackage ./synapse_change_display_name { }; synapse_change_display_name = pkgs.callPackage ./synapse_change_display_name { };
synix-docs = pkgs.callPackage ./synix-docs { }; synix-docs = pkgs.callPackage ./synix-docs { };
tinyfugue = pkgs.callPackage ./tinyfugue { };
trelis-gitingest-mcp = pkgs.callPackage ./trelis-gitingest-mcp { }; trelis-gitingest-mcp = pkgs.callPackage ./trelis-gitingest-mcp { };
tunerstudio = pkgs.callPackage ./tunerstudio { }; tunerstudio = pkgs.callPackage ./tunerstudio { };
# marker-pdf = pkgs.callPackage ./marker-pdf { }; # FIXME # marker-pdf = pkgs.callPackage ./marker-pdf { }; # FIXME
# openmv-ide = pkgs.callPackage ./openmv-ide { }; # FIXME
} }

View file

@ -12,9 +12,11 @@
let let
revision = "1161"; revision = "1161";
chromium-headless-shell = playwright-driver.passthru.components."chromium-headless-shell".overrideAttrs (old: { chromium-headless-shell =
inherit revision; playwright-driver.passthru.components."chromium-headless-shell".overrideAttrs
}); (old: {
inherit revision;
});
browsers-headless-only = linkFarm "playwright-browsers-headless-only" [ browsers-headless-only = linkFarm "playwright-browsers-headless-only" [
{ {
@ -23,9 +25,9 @@ let
} }
]; ];
in in
buildNpmPackage { buildNpmPackage rec {
pname = "fetcher-mcp"; pname = "fetcher-mcp";
version = "0.3.9"; version = "0.3.6";
src = fetchFromGitHub { src = fetchFromGitHub {
owner = "jae-jae"; owner = "jae-jae";
@ -60,6 +62,7 @@ buildNpmPackage {
description = "MCP server for fetch web page content using Playwright headless browser"; description = "MCP server for fetch web page content using Playwright headless browser";
homepage = "https://github.com/jae-jae/fetcher-mcp"; homepage = "https://github.com/jae-jae/fetcher-mcp";
license = lib.licenses.mit; license = lib.licenses.mit;
maintainers = with lib.maintainers; [ ];
mainProgram = "fetcher-mcp"; mainProgram = "fetcher-mcp";
platforms = lib.platforms.all; platforms = lib.platforms.all;
}; };

View file

@ -6,13 +6,13 @@
python3.pkgs.buildPythonApplication rec { python3.pkgs.buildPythonApplication rec {
pname = "freecad-mcp"; pname = "freecad-mcp";
version = "0.1.17"; version = "0.1.16";
pyproject = true; pyproject = true;
src = fetchPypi { src = fetchPypi {
pname = "freecad_mcp"; pname = "freecad_mcp";
inherit version; inherit version;
hash = "sha256-DugQWGZizIXO27cjzfyidNTyyahN1BTcDNTl4DHWqN0="; hash = "sha256-eFt7UgJw56C2zY8Vznrg8L0a5nvP0xtiW1oJuJ/fL3Y=";
}; };
build-system = [ build-system = [

View file

@ -1,20 +0,0 @@
{
writeShellApplication,
coreutils,
curl,
...
}:
let
name = "jirafeau-cli";
text = builtins.readFile ./${name}.sh;
in
writeShellApplication {
inherit name text;
meta.mainProgram = name;
runtimeInputs = [
coreutils
curl
];
}

View file

@ -1,152 +0,0 @@
#!/bin/bash
# This script has been auto-generated by Jirafeau but you can still edit options below.
# Config begin
proxy='' # Or set JIRAFEAU_PROXY.
url='' # Or set JIRAFEAU_URL.
time='month' # Or set JIRAFEAU_TIME.
one_time='' # Or set JIRAFEAU_ONE_TIME.
upload_password='' # Or set JIRAFEAU_UPLOAD_PASSWD
# Config end
if [ -n "${JIRAFEAU_PROXY:-}" ]; then
proxy="$JIRAFEAU_PROXY"
fi
if [ -n "${JIRAFEAU_URL:-}" ]; then
url="$JIRAFEAU_URL"
fi
if [ -z "$url" ]; then
echo "Please set url in script parameters or export JIRAFEAU_URL"
fi
if [ -n "${JIRAFEAU_TIME:-}" ]; then
time="$JIRAFEAU_TIME"
fi
if [ -n "${JIRAFEAU_ONE_TIME:-}" ]; then
one_time='1'
fi
if [ -n "${JIRAFEAU_UPLOAD_PASSWD:-}" ]; then
upload_password="$JIRAFEAU_UPLOAD_PASSWD"
fi
if [ -z "${2:-}" ]; then
echo "Jirafeau Bash Script 4.7.1"
echo "--------------------------"
echo "Usage:"
echo " $0 OPTIONS"
echo
echo "Options:"
echo " $0 send FILE [PASSWORD]"
echo " $0 get URL [PASSWORD]"
echo " $0 delete URL"
echo
echo "Global variables to export:"
echo " JIRAFEAU_PROXY: Domain and port of proxy server, eg. »proxyserver.example.com:3128«"
echo " JIRAFEAU_URL : URI to Jirafeau installation with trailing slash, eg. »https://example.com/jirafeau/«"
echo " JIRAFEAU_TIME : expiration time, eg. »minute«, »hour«, »day«, »week«, fortnight, »month«, »quarter«, »year« or »none«"
echo " JIRAFEAU_ONE_TIME : self-destroy after first download, eg. »1« to enable or »« (empty) to disable"
echo " JIRAFEAU_UPLOAD_PASSWD : upload password"
exit 0
fi
proxy_args=()
if [ -n "$proxy" ]; then
proxy_args=(-x "$proxy")
fi
options=()
if [ -n "$one_time" ]; then
options+=(-F one_time_download=1)
fi
if [ -n "$upload_password" ]; then
options+=(-F "upload_password=$upload_password")
fi
password=''
if [ -n "${3:-}" ]; then
password="$3"
options+=(-F "key=$password")
fi
apipage='script.php'
downloadpage='f.php'
if [ "$1" == "send" ]; then
if [ ! -f "$2" ]; then
echo "File \"$2\" does not exist."
exit
fi
# Get result
res=$(curl -X POST --http1.0 "${proxy_args[@]}" "${options[@]}" \
-F "time=$time" \
-F "file=@$2" \
"${url}${apipage}")
if [[ "$res" == Error* ]]; then
echo "Error while uploading."
echo "$res"
exit
fi
# Not using head or tail to minimise command dependencies
code=$(cnt=0; echo "$res" | while read -r l; do
if [[ "$cnt" == "0" ]]; then
echo "$l"
fi
cnt=$(( cnt + 1 ))
done)
del_code=$(cnt=0; echo "$res" | while read -r l; do
if [[ "$cnt" == "1" ]]; then
echo "$l"
fi
cnt=$(( cnt + 1 ))
done)
key_code=$(cnt=0; echo "$res" | while read -r l; do
if [[ "$cnt" == "2" ]]; then
echo "$l"
fi
cnt=$(( cnt + 1 ))
done)
echo
echo "Download page:"
if [[ $key_code ]]; then
echo " ${url}${downloadpage}?h=$code&k=$key_code"
else
echo " ${url}${downloadpage}?h=$code"
fi
echo "Direct download:"
if [[ $key_code ]]; then
echo " ${url}${downloadpage}?h=$code&k=$key_code&d=1"
else
echo " ${url}${downloadpage}?h=$code&d=1"
fi
echo "Delete link:"
echo " ${url}${downloadpage}?h=$code&d=$del_code"
echo
echo "Download via API:"
if [[ $key_code ]]; then
echo " ${0} get ${url}${apipage}?h=$code&k=$key_code [PASSWORD]"
else
echo " ${0} get ${url}${apipage}?h=$code [PASSWORD]"
fi
echo "Delete via API:"
echo " ${0} delete \"${url}${downloadpage}?h=$code&d=$del_code\""
elif [ "$1" == "get" ]; then
if [ -z "$password" ]; then
curl "${proxy_args[@]}" -OJ "$2"
else
curl "${proxy_args[@]}" -OJ -X POST -F "key=$password" "$2"
fi
elif [ "$1" == "delete" ]; then
curl "${proxy_args[@]}" "$2" --data-raw "do_delete=1%2F" | grep "div class" | sed -e "s/<[^>]\+>//g"
fi

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