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
66 changed files with 549 additions and 2130 deletions

View file

@ -27,7 +27,7 @@ Add this repo to your flake inputs:
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

View file

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

View file

@ -1,20 +1,15 @@
# NixOS and standalone Home Manager rebuild script
# Defaults
FLAKE_PATH="${REBUILD_FLAKE_PATH:-${FLAKE_PATH:-$HOME/.config/nixos}}" # Default flake path
USER="${REBUILD_USER:-$(whoami)}" # Default username
HOST="${REBUILD_HOST:-$(hostname)}" # Default hostname
BUILD_HOST="${REBUILD_BUILD_HOST:-}" # Default build host
TARGET_HOST="${REBUILD_TARGET_HOST:-}" # Default target host
UPDATE="${REBUILD_UPDATE:-0}" # Default to not update
UPDATE_INPUTS="${REBUILD_UPDATE_INPUTS:-}" # Default list of inputs
ROLLBACK="${REBUILD_ROLLBACK:-0}" # Default to not rollback
SHOW_TRACE="${REBUILD_SHOW_TRACE:-0}" # Default to not show trace
# 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; }
FLAKE_PATH="$HOME/.config/nixos" # Default flake path
HOME_USER="$(whoami)" # Default username. Used to identify the Home Manager configuration
NIXOS_HOST="$(hostname)" # Default hostname. Used to identify the NixOS and Home Manager configuration
BUILD_HOST="" # Default build host. Empty means localhost
TARGET_HOST="" # Default target host. Empty means localhost
UPDATE=0 # Default to not update flake repositories
UPDATE_INPUTS="" # Default list of inputs to update. Empty means all
ROLLBACK=0 # Default to not rollback
SHOW_TRACE=0 # Default to not show detailed error messages
# Function to display the help message
Help() {
@ -28,7 +23,7 @@ Help() {
echo " help Show this help message"
echo
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 " -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"
@ -39,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
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
Rebuild_nixos() {
local FLAKE="$FLAKE_PATH#$HOST"
local FLAKE="$FLAKE_PATH#$NIXOS_HOST"
# Construct rebuild command
local CMD=("nixos-rebuild" "switch" "--sudo")
[[ -n "$TARGET_HOST" || -n "$BUILD_HOST" ]] && CMD+=("--ask-sudo-password")
CMD+=("--flake" "$FLAKE")
[ "$ROLLBACK" = 1 ] && CMD+=("--rollback")
[ "$SHOW_TRACE" = 1 ] && CMD+=("--show-trace")
[ -n "$BUILD_HOST" ] && CMD+=("--build-host" "$BUILD_HOST")
if [ "$HOST" != "$(hostname)" ] && [ -z "$TARGET_HOST" ]; then
TARGET_HOST="$HOST"
_status "Using '$TARGET_HOST' as target host."
if [ "$NIXOS_HOST" != "$(hostname)" ] && [ -z "$TARGET_HOST" ]; then
TARGET_HOST="$NIXOS_HOST"
echo "Using '$TARGET_HOST' as target host."
fi
[ -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
local BUILD_CMD=("nixos-rebuild" "build" "--no-build-output" "--flake" "$FLAKE")
[ "$SHOW_TRACE" = 1 ] && BUILD_CMD+=("--show-trace")
[ -n "$BUILD_HOST" ] && BUILD_CMD+=("--build-host" "$BUILD_HOST")
# Rebuild NixOS configuration
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"
echo "Rebuilding NixOS configuration '$FLAKE'..."
else
_status "Rolling back to last NixOS generation"
echo "Rolling back to last NixOS generation..."
fi
sudo -v
_status "Executing command: ${CMD[*]}"
echo "Executing command: ${CMD[*]}"
"${CMD[@]}" || error "NixOS rebuild failed"
success "NixOS rebuild completed successfully."
echo "NixOS rebuild completed successfully."
}
# Function to rebuild Home Manager configuration
Rebuild_home() {
local FLAKE="$FLAKE_PATH#$USER@$HOST"
local FLAKE="$FLAKE_PATH#$HOME_USER@$NIXOS_HOST"
if [ -n "$BUILD_HOST" ] || [ -n "$TARGET_HOST" ]; then
error "Remote building is not supported for Home Manager."
@ -99,27 +91,21 @@ Rebuild_home() {
[ "$SHOW_TRACE" = 1 ] && CMD+=("--show-trace")
fi
# Build config first so we can diff it
# Rebuild Home Manager configuration
if [ "$ROLLBACK" = 0 ]; then
local BUILD_CMD=("home-manager" "build" "--no-out-link" "--flake" "$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"
echo "Rebuilding Home Manager configuration '$FLAKE'..."
else
_status "Rolling back to last Home Manager generation"
echo "Rolling back to last Home Manager generation..."
fi
_status "Executing command: ${CMD[*]}"
echo "Executing command: ${CMD[*]}"
"${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() {
_status "Updating flake inputs..."
echo "Updating flake inputs..."
# Construct update command as an array
local CMD=("nix" "flake" "update" "--flake" "$FLAKE_PATH")
@ -131,18 +117,17 @@ Update() {
done
fi
_status "Executing command: ${CMD[*]}"
echo "Executing command: ${CMD[*]}"
"${CMD[@]}" || error "Failed to update flake repositories"
success "Flake repositories updated successfully."
echo "Flake repositories updated successfully."
}
# Parse command-line options
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
exit 1
fi
COMMAND=$1
shift
@ -156,7 +141,7 @@ while [ $# -gt 0 ]; do
case "${1:-}" in
-H|--host)
if [ -n "${2:-}" ]; then
HOST="$2"
NIXOS_HOST="$2"
shift 2
else
error "-H|--host option requires an argument"
@ -164,7 +149,7 @@ while [ $# -gt 0 ]; do
;;
-u|--user)
if [ -n "${2:-}" ]; then
USER="$2"
HOME_USER="$2"
shift 2
else
error "-u|--user option requires an argument"
@ -213,7 +198,9 @@ while [ $# -gt 0 ]; do
fi
;;
*)
error "Unknown option '$1'"
echo "Error: Unknown option '$1'"
Help
exit 1
;;
esac
done
@ -240,10 +227,10 @@ fi
[ "$UPDATE" = 1 ] && Update
case "$COMMAND" in
nixos|os)
nixos)
Rebuild_nixos
;;
home|hm)
home)
Rebuild_home
;;
all)
@ -251,6 +238,9 @@ case "$COMMAND" in
Rebuild_home
;;
*)
error "Unknown command '$COMMAND'"
echo "Error: Unknown command '$COMMAND'"
echo "Printing help page:"
Help
exit 1
;;
esac

View file

@ -19,7 +19,7 @@ Provide the following entries to your `secrets.yaml`:
```yaml
headplane:
cookie_secret: abc123
api_key: abc123
agent_pre_authkey: abc123
```
Generate your cookie secret with:
@ -28,7 +28,7 @@ Generate your cookie secret with:
nix-shell -p openssl --run "openssl rand -hex 16"
```
Generate your agent `api_key` with:
Generate your agent pre-authkey with:
```bash
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": {
"locked": {
"lastModified": 1776384880,
"narHash": "sha256-28Cg9HI/IwFHUm5fZyMEGCQ94L3Il6K4OfHeRf0b+Zw=",
"lastModified": 1770913512,
"narHash": "sha256-jRC1qRoRCrMjDalVfUMHFlKSkkA2q0RZWTDW0LsquoA=",
"owner": "DeterminateSystems",
"repo": "flake-schemas",
"rev": "b852751d29c6e09919f25c1e10754646bdc181c9",
"rev": "3c464b0e09cb44c6e073c41dd1d834980c3e0b24",
"type": "github"
},
"original": {
@ -258,11 +258,11 @@
]
},
"locked": {
"lastModified": 1776796298,
"narHash": "sha256-PcRvlWayisPSjd0UcRQbhG8Oqw78AcPE6x872cPRHN8=",
"lastModified": 1772024342,
"narHash": "sha256-+eXlIc4/7dE6EcPs9a2DaSY3fTA9AE526hGqkNID3Wg=",
"owner": "cachix",
"repo": "git-hooks.nix",
"rev": "3cfd774b0a530725a077e17354fbdb87ea1c4aad",
"rev": "6e34e97ed9788b17796ee43ccdbaf871a5c2b476",
"type": "github"
},
"original": {
@ -349,11 +349,11 @@
]
},
"locked": {
"lastModified": 1777851538,
"narHash": "sha256-Gp8qwTEYNoy2yvmErVGlvLOQvrtEECCAKbonW7VJef8=",
"lastModified": 1772020340,
"narHash": "sha256-aqBl3GNpCadMoJ/hVkWTijM1Aeilc278MjM+LA3jK6g=",
"owner": "nix-community",
"repo": "home-manager",
"rev": "cc09c0f9b7eaa95c2d9827338a5eb03d32505ca5",
"rev": "36e38ca0d9afe4c55405fdf22179a5212243eecc",
"type": "github"
},
"original": {
@ -465,11 +465,11 @@
},
"nixpkgs_2": {
"locked": {
"lastModified": 1777673416,
"narHash": "sha256-5c2POKPOjU40Kh0MirOdScBLG0bu9TAuPYAtPRNZMBs=",
"lastModified": 1771903837,
"narHash": "sha256-sdaqdnsQCv3iifzxwB22tUwN/fSHoN7j2myFW5EIkGk=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "26ef669cffa904b6f6832ab57b77892a37c1a671",
"rev": "e764fc9a405871f1f6ca3d1394fb422e0a0c3951",
"type": "github"
},
"original": {
@ -511,11 +511,11 @@
]
},
"locked": {
"lastModified": 1777906128,
"narHash": "sha256-ei9g+4pCXhacUwnYuljs9v75hwfTqnfU+J6s6s8hfvc=",
"lastModified": 1772140017,
"narHash": "sha256-wFUc9tn5Ik11oL009BkVnj4NxsY63UbZjhOyEqCsIQE=",
"owner": "nix-community",
"repo": "NUR",
"rev": "c278c296b010e2a32838c8d4539a2d72c1686d4d",
"rev": "be0fea0af0e8cfadb3995dd7bb3a167bc012e935",
"type": "github"
},
"original": {
@ -605,11 +605,11 @@
"tinted-zed": "tinted-zed"
},
"locked": {
"lastModified": 1777581180,
"narHash": "sha256-JcDBTZkkz68WlZKYDoD+MZG8b3dnIJXqMvyuVx3Wkdg=",
"lastModified": 1771788390,
"narHash": "sha256-RzBpBwn93GWxLjacTte+ngwwg0L/BVOg4G/sSIeK3Rw=",
"owner": "nix-community",
"repo": "stylix",
"rev": "a2538cd28ae2140ffce9cee9108b8d569a9c4fed",
"rev": "ebb238f14d6f930068be4718472da3105fd5d3bf",
"type": "github"
},
"original": {

View file

@ -219,9 +219,17 @@
};
in
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 = {
@ -281,10 +289,6 @@
path = ./templates/dev/rs-hello;
description = "Rust hello world template.";
};
stm32-blink = {
path = ./templates/dev/stm32-blink;
description = "STM32G4 blink template with libopencm3.";
};
};
};
}

View file

@ -58,6 +58,7 @@ nav:
- radicale: modules/nixos/radicale.md
- rss-bridge: modules/nixos/rss-bridge.md
- sops: modules/nixos/sops.md
- tailscale: modules/nixos/tailscale.md
- virtualisation: modules/nixos/virtualisation.md
- webPage: modules/nixos/webpage.md
- Home Manager:

View file

@ -1,46 +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;
};
};
programs.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;
};
};
programs.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

@ -56,10 +56,8 @@ let
in
{
imports = [
./bitwarden
./bemenu
./dmenu-bluetooth
./dunst-toggle
./element-desktop
./feh
./kitty
@ -137,11 +135,6 @@ in
bind = [ "$mod SHIFT, m, exec, ${terminal} -T ${musicplayer} -e ${musicplayer}" ];
};
notifications = mkAppAttrs {
default = "dunst-toggle";
bind = [ "$mod, Backspace, exec, ${notifications}" ];
};
networksettings = mkAppAttrs {
default = "networkmanager_dmenu";
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

@ -3,30 +3,15 @@
let
cfg = config.wayland.windowManager.hyprland;
nonCenterFloatingClasses = [
"Gimp"
"steam"
"KiCad"
];
nonCenterFloatingClassesRegex = concatStringsSep "|" nonCenterFloatingClasses;
inherit (builtins) concatStringsSep toString;
inherit (builtins) toString;
inherit (lib) mkDefault;
in
{
# Do not add binds here. Use `./binds/default.nix` instead.
"$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 = [
# "float, class:^(${nonCenterFloatingClassesRegex})$"
"center, floating:1, class:^(?!.*(${nonCenterFloatingClassesRegex})).*$"
"center, floating:1, not class:^(Gimp)$, not class:^(steam)$"
"float, title:^(Open|Save) Files?$"
"noborder, onworkspace:w[t1]"
"bordersize ${toString cfg.settings.general.border_size}, floating:1"
@ -39,12 +24,6 @@ in
"noblur, class:^(xwaylandvideobridge)$"
];
gesture = [
"3, horizontal, workspace"
"3, up, dispatcher, exec, bemenu-run" # TODO: move to hyprland.applications
"4, swipe, move"
];
# Layouts
general.layout = mkDefault "master";
master = {

View file

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

View file

@ -3,17 +3,14 @@
{
imports = [
./cmp.nix
./diffview.nix
./lsp.nix
./gitsigns.nix
./lualine.nix
./telescope.nix
./treesitter.nix
# ./treesitter.nix # HOTFIX: does not build
./trouble.nix
];
config.programs.nixvim.plugins = {
which-key.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`
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 = {
plugins.telescope = {
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 = {
file-browser.enable = mkDefault true;
fzf-native.enable = mkDefault true;

View file

@ -2,7 +2,7 @@
let
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
{
home.file = {
@ -12,7 +12,7 @@ in
url = baseUrl + "/de.utf-8.spl";
sha256 = "sha256-c8cQfqM5hWzb6SHeuSpFk5xN5uucByYdobndGfaDo9E=";
};
target = spellDir + "/de.utf-8.spl";
target = spellDir + "/de.utf8.spl";
};
de-sug = {
enable = true;
@ -20,7 +20,7 @@ in
url = baseUrl + "/de.utf-8.sug";
sha256 = "sha256-E9Ds+Shj2J72DNSopesqWhOg6Pm6jRxqvkerqFcUqUg=";
};
target = spellDir + "/de.utf-8.sug";
target = spellDir + "/de.utf8.sug";
};
};
}

View file

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

View file

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

View file

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

View file

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

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

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

View file

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

View file

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

View file

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

View file

@ -45,10 +45,10 @@ in
url = "http://127.0.0.1:${toString headscale.port}";
public_url = headscale.settings.server_url;
config_path = "/etc/headscale/config.yaml";
api_key_path = config.sops.secrets."headplane/api_key".path;
};
integration.agent = {
enabled = mkDefault true;
pre_authkey_path = config.sops.secrets."headplane/agent_pre_authkey".path;
};
};
};
@ -70,7 +70,7 @@ in
"headplane/cookie_secret" = {
inherit owner group mode;
};
"headplane/api_key" = {
"headplane/agent_pre_authkey" = {
inherit owner group mode;
};
};

View file

@ -8,14 +8,11 @@ in
programs.dconf.enable = true; # fixes nixvim hm module
services.flatpak.enable = true;
xdg.portal = {
enable = true;
extraPortals = with pkgs; [
xdg-desktop-portal-gtk
];
config.common.default = "gtk";
};
services.gnome.gnome-keyring.enable = true;

View file

@ -1,527 +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};
description = 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;
};
networking.firewall.interfaces =
let
matchAll = if !config.networking.nftables.enable then "podman+" else "podman*";
in
{
"${matchAll}".allowedUDPPorts = [ 53 ];
};
virtualisation.oci-containers.backend = "podman";
virtualisation.oci-containers.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"
];
};
virtualisation.oci-containers.containers.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"
];
};
virtualisation.oci-containers.containers.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"
];
};
virtualisation.oci-containers.containers.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"
];
};
virtualisation.oci-containers.containers.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"
];
};
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" ];
};
systemd.services.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" ];
};
systemd.services.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" ];
};
systemd.services.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" ];
};
systemd.services.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" ];
};
systemd.services.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" ];
};
systemd.services.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" ];
};
systemd.services.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" ];
};
systemd.services.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" ];
};
systemd.services.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" ];
};
systemd.services.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" ];
};
systemd.services.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" ];
};
systemd.targets.podman-compose-librechat-root = {
unitConfig.Description = "Root target generated by compose2nix.";
wantedBy = [ "multi-user.target" ];
};
};
}

View file

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

View file

@ -1,57 +0,0 @@
{ config, lib, ... }:
let
cfg = config.services.nostr-relay;
domain = config.networking.domain;
subdomain = 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;
forceSSL = cfg.reverseProxy.forceSSL;
locations."/".proxyPass =
"http://127.0.0.1:${toString config.services.nostr-rs-relay.settings.network.port}";
};
};
};
}

View file

@ -10,10 +10,10 @@ let
image = pkgs.dockerTools.pullImage {
imageName = "ghcr.io/open-webui/open-webui";
imageDigest = "sha256:a7e4796ae894d1e2a0c1824860ade472f35c507608a01c3581377b5c19b0ed49";
hash = "sha256-uhPlVXSxY6rGbYGvlPVV3zurmbI96mAHEuKKy9FFaD4=";
imageDigest = "sha256:2deb90b0423473d8f97febced2e62b8fd898aa3eb61877bb3aa336370214c258";
hash = "sha256-2cdKfvZGUyPUm7TFXcf5OcG9ey4BvOZPUOVim1S2C+s=";
finalImageName = "ghcr.io/open-webui/open-webui";
finalImageTag = "0.9.2";
finalImageTag = "0.8.5";
};
defaultEnv = {

View file

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

View file

@ -1,17 +1,9 @@
{
config,
lib,
pkgs,
...
}:
{ config, lib, ... }:
let
cfg = config.services.tailscale;
inherit (lib)
concatStrings
filterAttrs
mapAttrsToList
mkIf
mkOption
optional
@ -20,124 +12,39 @@ let
in
{
options.services.tailscale = {
tailnets = mkOption {
default = { };
type = types.attrsOf (
types.submodule {
options = {
loginServer = mkOption {
type = types.str;
description = "Login server for this tailnet.";
};
authKeyFile = mkOption {
type = types.nullOr types.str;
default = null;
description = "Path to auth key secret.";
description = "The Tailscale login server to use.";
};
enableSSH = mkOption {
type = types.bool;
default = false;
description = "Enable Tailscale SSH functionality.";
};
acceptDNS = mkOption {
type = types.bool;
default = true;
};
default = mkOption {
type = types.bool;
default = false;
description = "Connect to this tailnet on boot.";
};
};
}
);
description = "Enable Tailscale's MagicDNS and custom DNS configuration.";
};
};
config =
let
defaultTailnets = filterAttrs (_: t: t.default) cfg.tailnets;
defaultTailnet =
if defaultTailnets == { } then null else builtins.head (builtins.attrValues defaultTailnets);
entries = mapAttrsToList (name: tcfg: ''
TAILNETS["${name}"]="${tcfg.loginServer}|${if tcfg.enableSSH then "true" else "false"}|${
if tcfg.acceptDNS then "true" else "false"
}|${if tcfg.authKeyFile != null then tcfg.authKeyFile else ""}"
'') 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";
config = mkIf cfg.enable {
services.tailscale = {
authKeyFile = config.sops.secrets."tailscale/auth-key".path;
extraSetFlags = optional cfg.enableSSH "--ssh" ++ optional cfg.acceptDNS "--accept-dns";
extraUpFlags = [
"--login-server=${loginServer}"
"--login-server=${cfg.loginServer}"
]
++ optional enableSSH "--ssh"
++ optional acceptDNS "--accept-dns";
}
);
++ optional cfg.enableSSH "--ssh"
++ optional cfg.acceptDNS "--accept-dns";
};
environment.systemPackages = [ tailnetSwitchCli ];
environment.shellAliases.ts = "${cfg.package}/bin/tailscale";
environment.shellAliases = {
ts = "${cfg.package}/bin/tailscale";
};
networking.firewall.trustedInterfaces = [ cfg.interfaceName ];
sops.secrets."tailscale/auth-key" = { };
};
}

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

@ -32,8 +32,6 @@ in
};
};
nix.settings.warn-dirty = mkDefault false;
# fallback quickly if substituters are not available.
nix.settings.connect-timeout = mkDefault 5;
nix.settings.fallback = true;

View file

@ -6,13 +6,13 @@
python3.pkgs.buildPythonApplication rec {
pname = "arxiv-mcp-server";
version = "0.4.12";
version = "0.3.1";
pyproject = true;
src = fetchPypi {
pname = "arxiv_mcp_server";
inherit version;
hash = "sha256-khY63SfUKgNjyACNESEQpY9q3uJSlLhuJBEaBKGmZy8=";
hash = "sha256-yGNetU7el6ZXsavD8uvO17OZtaPuYgzkxiVEk402GUs=";
};
build-system = [

View file

@ -11,17 +11,17 @@
rustPlatform.buildRustPackage rec {
pname = "baibot";
version = "1.18.0";
version = "1.14.3";
src = fetchFromGitHub {
owner = "etkecc";
repo = "baibot";
rev = "v${version}";
hash = "sha256-TwtN8tEml/z0Js78jjWzEYV58Dg8a5YKxdEU9hBDtNs=";
hash = "sha256-bFUijsvwUQhISjWVoVvoDXNSDPaWZTunqUfxfgaxclM=";
};
useFetchCargoVendor = true;
cargoHash = "sha256-2TJsFT+I7JG1DauEOvB8HluMDb1vW0qXD/mzzSlAfew=";
cargoHash = "sha256-/7KSCVWuxTk7gKOYxE/uQ5T0BnYlDYWOvdFXEiU9mB0=";
nativeBuildInputs = [
pkg-config

View file

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

View file

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

View file

@ -25,9 +25,9 @@ let
}
];
in
buildNpmPackage {
buildNpmPackage rec {
pname = "fetcher-mcp";
version = "0.3.9";
version = "0.3.6";
src = fetchFromGitHub {
owner = "jae-jae";

View file

@ -6,13 +6,13 @@
python3.pkgs.buildPythonApplication rec {
pname = "freecad-mcp";
version = "0.1.17";
version = "0.1.16";
pyproject = true;
src = fetchPypi {
pname = "freecad_mcp";
inherit version;
hash = "sha256-DugQWGZizIXO27cjzfyidNTyyahN1BTcDNTl4DHWqN0=";
hash = "sha256-eFt7UgJw56C2zY8Vznrg8L0a5nvP0xtiW1oJuJ/fL3Y=";
};
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

View file

@ -1,43 +0,0 @@
{
lib,
stdenv,
fetchFromGitLab,
writeText,
nixosTests,
}:
let
localConfig = writeText "config.local.php" ''
<?php
return require(getenv('JIRAFEAU_CONFIG'));
?>
'';
in
stdenv.mkDerivation rec {
pname = "jirafeau";
version = "4.7.1";
src = fetchFromGitLab {
owner = "jirafeau";
repo = "Jirafeau";
rev = version;
hash = "sha256-jXUO+tj6VFNjJkT0vkCCtG7yNWf3QeCx7izZPekAng8=";
};
installPhase = ''
mkdir $out
cp -r * $out/
cp ${localConfig} $out/lib/config.local.php
'';
passthru.tests = { inherit (nixosTests) jirafeau; };
meta = {
description = "Jirafeau: a simple way to upload a file";
homepage = "https://gitlab.com/jirafeau/Jirafeau";
changelog = "https://gitlab.com/jirafeau/Jirafeau/-/blob/${src.rev}/CHANGELOG.md";
license = lib.licenses.agpl3Plus;
mainProgram = "jirafeau";
platforms = lib.platforms.all;
};
}

View file

@ -35,11 +35,11 @@ buildNpmPackage rec {
src = fetchFromGitHub {
owner = "mixelpixx";
repo = "KiCAD-MCP-Server";
rev = "216d3f189d194cdd539b274264b8b7ec00cbb13e";
hash = "sha256-73yDzYlWMDnnytf/IcUuwQp/P7YYaxrNr+JsGrgMI40=";
rev = "b5a1483fc20dd1201a1d275522904aac914157e8";
hash = "sha256-63Ef34YjniMY+ZPpDrWZpPx8bUbNA0XE+7Jlgm8KJv8=";
};
npmDepsHash = "sha256-qR43NvSK28RLt5sL9BZQzSyFRszYMt2CC0wTs9jf/hU=";
npmDepsHash = "sha256-nz73qj8CK2LyFixoF14ET2wq407YyuJUw/4VTDc80cQ=";
buildScript = "build";

View file

@ -7,14 +7,14 @@
python3.pkgs.buildPythonApplication rec {
pname = "mcpo";
version = "0.0.20";
version = "0.0.19";
pyproject = true;
src = fetchFromGitHub {
owner = "open-webui";
repo = "mcpo";
rev = "v${version}";
hash = "sha256-tA1KdcfmNPuqPbwE66jRY85tsrOKjPImsxSoGsW5ZD4=";
hash = "sha256-ZfTVMrXXEsEKHmeG4850Hq3MEpQA/3WMpAVZS0zvp1I=";
};
build-system = [

View file

@ -1,83 +0,0 @@
{
lib,
buildGoModule,
fetchFromGitHub,
stdenv,
darwin,
xorg,
pkg-config,
wayland,
libGL,
libxkbcommon,
makeWrapper,
}:
buildGoModule rec {
pname = "nerdlog";
version = "1.10.0";
src = fetchFromGitHub {
owner = "dimonomid";
repo = "nerdlog";
rev = "v${version}";
hash = "sha256-XlzWNeyd+Ar4ArFcN1wkQ0aod6ckAiIb12odK7cf4+s=";
};
vendorHash = "sha256-hvv0dsE1yz85VLaBOE7RWbux8L8kVTihcA1HyyHRYAM=";
subPackages = [
"cmd/nerdlog"
"cmd/generate_syslog"
];
doCheck = false;
nativeBuildInputs = [
pkg-config
makeWrapper
];
buildInputs =
lib.optionals stdenv.isDarwin [
darwin.apple_sdk.frameworks.Cocoa
]
++ lib.optionals stdenv.isLinux [
xorg.libX11
xorg.libXext
xorg.libXfixes
wayland
libGL
libxkbcommon
];
postInstall = lib.optionalString stdenv.isLinux ''
wrapProgram $out/bin/nerdlog \
--prefix LD_LIBRARY_PATH : ${
lib.makeLibraryPath [
xorg.libX11
xorg.libXext
xorg.libXfixes
wayland
libGL
libxkbcommon
]
}
'';
ldflags = [
"-s"
"-w"
"-X=github.com/dimonomid/nerdlog/version.version=${version}"
"-X=github.com/dimonomid/nerdlog/version.commit=${src.rev}"
"-X=github.com/dimonomid/nerdlog/version.date=1970-01-01T00:00:00Z"
"-X=github.com/dimonomid/nerdlog/version.builtBy=goreleaser"
];
meta = {
description = "Nerdlog: fast, remote-first, multi-host TUI log viewer with timeline histogram and no central server";
homepage = "https://github.com/dimonomid/nerdlog";
changelog = "https://github.com/dimonomid/nerdlog/blob/${src.rev}/CHANGELOG.md";
license = lib.licenses.bsd2;
mainProgram = "nerdlog";
};
}

View file

@ -1,132 +0,0 @@
{
lib,
stdenv,
fetchurl,
at-spi2-atk,
autoPatchelfHook,
cairo,
cups,
dbus,
fontconfig,
freetype,
gdk-pixbuf,
glib,
gtk3,
libGL,
libdrm,
libxcb-cursor,
libxkbcommon,
makeWrapper,
pango,
wayland,
xorg,
zlib,
}:
stdenv.mkDerivation rec {
pname = "openmv-ide";
version = "4.8.9";
src = fetchurl {
url = "https://github.com/openmv/openmv-ide/releases/download/v${version}/openmv-ide-linux-x86_64-${version}.tar.gz";
hash = "sha256-D0Uunb6IIX8gkirZIISjjhvrrCsR5bgRBcXidtjN/n0=";
};
nativeBuildInputs = [
autoPatchelfHook
makeWrapper
];
buildInputs = [
at-spi2-atk
cairo
cups
dbus.lib
fontconfig
freetype
gdk-pixbuf
glib
gtk3
libGL
libdrm
libxcb-cursor
libxkbcommon
pango
stdenv.cc.cc.lib
wayland
xorg.libX11
xorg.libxcb
xorg.xcbutil
xorg.xcbutilimage
xorg.xcbutilkeysyms
xorg.xcbutilrenderutil
xorg.xcbutilwm
zlib
];
autoPatchelfIgnoreMissingDeps = [
"libQt6Designer.so.6"
"libQt6Labs*.so.6"
"libQt6OpenGLWidgets.so.6"
"libQt6Qml*.so.6"
"libQt6Quick*.so.6"
"libQt6ShaderTools.so.6"
"libgssapi_krb5.so.2"
"libicudata.so.56"
"libicui18n.so.56"
"libicuuc.so.56"
"libmysqlclient.so.21"
"libodbc.so.2"
"libpq.so.5"
];
dontAutoPatchelf = true;
dontBuild = true;
dontConfigure = true;
dontStrip = true;
installPhase = ''
runHook preInstall
mkdir -p $out/share/openmv-ide
cp -r bin lib share $out/share/openmv-ide/
chmod -R u+w $out/share/openmv-ide/
mkdir -p $out/share/applications
cp share/applications/io.openmv.openmvide.desktop $out/share/applications/
mkdir -p $out/share/icons
cp -r share/icons/hicolor $out/share/icons/
substituteInPlace $out/share/applications/io.openmv.openmvide.desktop \
--replace "Exec=openmvide" "Exec=$out/bin/openmvide"
mkdir -p $out/bin
makeWrapper $out/share/openmv-ide/bin/openmvide $out/bin/openmvide \
--prefix LD_LIBRARY_PATH : "$out/share/openmv-ide/lib:$out/share/openmv-ide/lib/qtcreator:$out/share/openmv-ide/lib/Qt/lib" \
--add-flags "-user-library-path \"\$LD_LIBRARY_PATH\""
runHook postInstall
'';
postFixup = ''
addAutoPatchelfSearchPath $out/share/openmv-ide/lib/Qt/lib
addAutoPatchelfSearchPath $out/share/openmv-ide/lib/qtcreator
# skip embedded third-party toolchains and tools under share/
autoPatchelf -- \
$out/share/openmv-ide/bin \
$out/share/openmv-ide/lib
'';
meta = {
description = "QtCreator based IDE for the OpenMV Cam";
homepage = "https://openmv.io/";
license = lib.licenses.gpl3Plus;
mainProgram = "openmvide";
platforms = [ "x86_64-linux" ];
sourceProvenance = [ lib.sourceTypes.binaryNativeCode ];
};
}

View file

@ -43,20 +43,13 @@ def process_csv(input_file, output_file):
if col not in new_row:
new_row[col] = default_val
folder = new_row.get('folder', '')
name = new_row.get('name', '')
if folder.startswith('www') and name:
if not name.startswith('http://') and not name.startswith('https://'):
new_row['login_uri'] = f'https://{name}'
else:
new_row['login_uri'] = name
if new_row['notes']:
new_row['notes'] = new_row['notes'].replace('\n', ' ').replace('\r', ' ')
new_row['notes'] = re.sub(r'\s+', ' ', new_row['notes']).strip()
notes = new_row['notes']
if notes:
# Look for pattern: "login: USERNAME"
match = re.search(r'login:\s*(\S+)', notes, re.IGNORECASE)
if match:
username = match.group(1)

View file

@ -2,8 +2,6 @@
stdenv,
lib,
makeWrapper,
pass,
pass2csv,
python3,
...
}:
@ -22,18 +20,13 @@ stdenv.mkDerivation rec {
installPhase = ''
mkdir -p $out/bin
cp ${src}/pass2bw.sh $out/bin/${pname}
sed -e "s|python ./convert_csvs.py|python $out/bin/convert_csvs.py|" \
${src}/${pname}.sh > $out/bin/${pname}
chmod +x $out/bin/${pname}
cp ${src}/convert_csvs.py $out/bin/
wrapProgram $out/bin/${pname} \
--prefix PATH : ${
lib.makeBinPath [
pass
pass2csv
python3
]
}
--prefix PATH : ${lib.makeBinPath [ python3 ]}
'';
}

View file

@ -4,5 +4,5 @@ if [ "$#" -ne 2 ]; then
fi
pass2csv "$1" /tmp/pass.csv
python3 "$(dirname "$0")/convert_csvs.py" /tmp/pass.csv "$2"
python ./convert_csvs.py /tmp/pass.csv "$2"
rm /tmp/pass.csv

View file

@ -1,61 +0,0 @@
{
lib,
stdenv,
fetchFromGitHub,
ncurses,
openssl,
pcre2,
zlib,
lua5_4,
python3,
icu,
enableLua ? true,
enablePython ? true,
enableWidechar ? true,
}:
stdenv.mkDerivation rec {
pname = "tinyfugue";
version = "5.2.2";
src = fetchFromGitHub {
owner = "ingwarsw";
repo = "tinyfugue";
rev = version;
hash = "sha256-nHgu0XJ2Nd0JYrs3o/NLZ9gq1D4ubdPyowQLyhOzDDA=";
};
buildInputs = [
ncurses
openssl
pcre2
zlib
]
++ lib.optional enableLua lua5_4
++ lib.optional enablePython python3
++ lib.optional enableWidechar icu;
env.NIX_CFLAGS_COMPILE = "-D_DEFAULT_SOURCE";
configureFlags = [
"--enable-termcap=ncurses"
]
++ lib.optional enableLua "--enable-lua"
++ lib.optional enablePython "--enable-python"
++ lib.optional (!enableWidechar) "--disable-widechar";
installPhase = ''
make install
'';
meta = {
description = "TinyFugue - Rebirth";
homepage = "https://github.com/ingwarsw/tinyfugue";
changelog = "https://github.com/ingwarsw/tinyfugue/blob/${src.rev}/CHANGES";
license = lib.licenses.gpl2Only;
mainProgram = "tf";
platforms = lib.platforms.all;
};
}

View file

@ -1 +0,0 @@
use flake

View file

@ -1,5 +0,0 @@
.cache/
.direnv/
build/
compile_commands.json
result

View file

@ -1,67 +0,0 @@
PREFIX ?= arm-none-eabi-
CC = $(PREFIX)gcc
OBJCOPY = $(PREFIX)objcopy
SIZE = $(PREFIX)size
PNAME := blink
BUILD := build
# libopencm3 is provided by the Nix shell
OPENCM3_DIR ?= ""
SRCS := src/main.c
OBJS := $(addprefix $(BUILD)/, $(SRCS:.c=.o))
# STM32G4
CPU_FLAGS := -mcpu=cortex-m4 -mthumb -mfloat-abi=hard -mfpu=fpv4-sp-d16
CFLAGS := $(CPU_FLAGS) \
-DSTM32G4 \
-Os -Wall -Wextra \
-ffunction-sections -fdata-sections \
-I$(OPENCM3_DIR)/include
LDFLAGS := $(CPU_FLAGS) \
-nostartfiles \
-Wl,--gc-sections \
-L$(OPENCM3_DIR)/lib \
-lopencm3_stm32g4
ELF := $(BUILD)/$(PNAME).elf
BIN := $(BUILD)/$(PNAME).bin
HEX := $(BUILD)/$(PNAME).hex
.PHONY: all clean flash size
all: $(BIN) $(HEX) size
$(BUILD)/%.o: %.c
@mkdir -p $(@D)
$(CC) $(CFLAGS) -c $< -o $@
LDSCRIPT := $(BUILD)/stm32g474xe.ld
$(LDSCRIPT):
@mkdir -p $(@D)
$(CC) -I$(OPENCM3_DIR)/include $(shell python3 $(OPENCM3_DIR)/scripts/genlink.py $(OPENCM3_DIR)/ld/devices.data stm32g474xe DEFS) -P -E $(OPENCM3_DIR)/ld/linker.ld.S -o $@
$(ELF): $(OBJS) $(LDSCRIPT)
$(CC) $(OBJS) $(LDFLAGS) -T$(LDSCRIPT) -o $@
$(BIN): $(ELF)
$(OBJCOPY) -O binary $< $@
$(HEX): $(ELF)
$(OBJCOPY) -O ihex $< $@
size: $(ELF)
$(SIZE) $<
flash: $(BIN)
openocd \
-f interface/stlink.cfg \
-f target/stm32g4x.cfg \
-c "program $(BIN) verify reset exit 0x08000000"
clean:
rm -rf $(BUILD)

View file

@ -1,28 +0,0 @@
# STM32G4 blink template
Blinks the LD2 LED (PA5) on a [Nucleo-G474RE](https://www.st.com/en/evaluation-tools/nucleo-g474re.html) using [libopencm3](https://github.com/libopencm3/libopencm3).
Set `BLINK_PORT` / `BLINK_PIN` for your board in [`src/main.c`](./src/main.c).
## Toolchain
The Nix dev shell provides:
- `arm-none-eabi-gcc` (via `gcc-arm-embedded`)
- `openocd` for flashing and debugging
- `stlink` utilities (`st-flash`, `st-info`)
- `libopencm3` built for `stm32/g4` (exposed as `$LIBOPENCM3_DIR`)
## Build
```bash
make
```
## Flash
```bash
make flash
```
This uses OpenOCD with the built-in ST-Link interface config.

View file

@ -1,6 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
make clean
mkdir -p build
bear -- make

View file

@ -1,161 +0,0 @@
{
description = "A blink template for STM32G4";
inputs = {
nixpkgs.url = "nixpkgs/nixpkgs-unstable";
utils.url = "github:numtide/flake-utils";
libopencm3-src = {
url = "github:libopencm3/libopencm3";
flake = false;
};
pre-commit-hooks = {
url = "github:cachix/pre-commit-hooks.nix";
inputs.nixpkgs.follows = "nixpkgs";
};
};
outputs =
{
self,
nixpkgs,
utils,
libopencm3-src,
pre-commit-hooks,
...
}:
{
overlays.default = final: prev: {
libopencm3-stm32 = final.stdenvNoCC.mkDerivation {
pname = "libopencm3-stm32";
version = "latest";
src = libopencm3-src;
postPatch = ''
patchShebangs scripts
'';
nativeBuildInputs = [
final.gcc-arm-embedded
final.python3
final.gnumake
];
enableParallelBuilding = true;
makeFlags = [
"PREFIX=arm-none-eabi-"
];
TARGETS = "stm32/f0 stm32/f1 stm32/f2 stm32/f3 stm32/f4 stm32/f7 stm32/l0 stm32/l1 stm32/l4 stm32/g0 stm32/g4 stm32/h7 stm32/u5";
installPhase = ''
runHook preInstall
mkdir -p $out
cp -r include lib mk scripts ld $out/
patchShebangs $out/scripts
runHook postInstall
'';
meta = with final.lib; {
description = "Open source ARM Cortex-M microcontroller library (STM32 targets)";
homepage = "http://libopencm3.org/";
license = licenses.lgpl3Plus;
platforms = platforms.all;
};
};
};
}
// utils.lib.eachDefaultSystem (
system:
let
pkgs = import nixpkgs {
inherit system;
overlays = [ self.overlays.default ];
};
pname = "blink";
version = "0.1.0";
in
{
packages = {
inherit (pkgs) libopencm3-stm32;
default = pkgs.stdenvNoCC.mkDerivation {
inherit pname version;
src = pkgs.lib.cleanSource ./.;
nativeBuildInputs = with pkgs; [
gcc-arm-embedded
gnumake
python3
];
makeFlags = [
"OPENCM3_DIR=${pkgs.libopencm3-stm32}"
"PREFIX=arm-none-eabi-"
];
installPhase = ''
runHook preInstall
mkdir -p $out/bin
cp build/${pname}.elf $out/bin/
cp build/${pname}.bin $out/bin/
cp build/${pname}.hex $out/bin/
runHook postInstall
'';
};
};
devShells.default =
let
# FIXME: 'gnu/stubs-32.h' file not found
clangdWrapper = pkgs.writeShellScriptBin "clangd" ''
exec ${pkgs.clang-tools}/bin/clangd \
--query-driver=${pkgs.gcc-arm-embedded}/bin/arm-none-eabi-* \
"$@"
'';
in
pkgs.mkShell {
name = "stm32-blink";
packages = self.packages.${system}.default.nativeBuildInputs ++ [
clangdWrapper
pkgs.bear
pkgs.openocd
pkgs.stlink
];
OPENCM3_DIR = pkgs.libopencm3-stm32;
PREFIX = "arm-none-eabi-";
};
formatter =
let
inherit (self.checks.${system}.pre-commit-check.config) package configFile;
script = ''
${pkgs.lib.getExe package} run --all-files --config ${configFile}
'';
in
pkgs.writeShellScriptBin "pre-commit-run" script;
checks = {
build-packages = pkgs.linkFarm "flake-packages-${system}" self.packages.${system};
pre-commit-check = pre-commit-hooks.lib.${system}.run {
src = ./.;
hooks = {
nixfmt.enable = true;
clang-format = {
enable = true;
types_or = pkgs.lib.mkForce [
"c"
"c++"
];
};
};
};
};
}
);
}

View file

@ -1,50 +0,0 @@
#include <libopencm3/cm3/systick.h>
#include <libopencm3/stm32/gpio.h>
#include <libopencm3/stm32/rcc.h>
/* Nucleo-G474RE: LD2 is on PA5 */
#define BLINK_PORT GPIOA
#define BLINK_PIN GPIO5
#define BLINK_PERIOD_MS 1000
static volatile uint32_t s_ticks = 0;
void sys_tick_handler(void) { s_ticks++; }
static void delay_ms(uint32_t ms) {
uint32_t until = s_ticks + ms;
while (s_ticks < until)
;
}
static void clock_setup(void) {
rcc_clock_setup_pll(&rcc_hsi_configs[RCC_CLOCK_3V3_170MHZ]);
}
static void systick_setup(void) {
/* 1 ms tick at 170 MHz core clock */
systick_set_reload(rcc_ahb_frequency / 1000 - 1);
systick_set_clocksource(STK_CSR_CLKSOURCE_AHB);
systick_counter_enable();
systick_interrupt_enable();
}
static void gpio_setup(void) {
rcc_periph_clock_enable(RCC_GPIOA);
gpio_mode_setup(BLINK_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, BLINK_PIN);
gpio_set_output_options(BLINK_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_LOW,
BLINK_PIN);
}
int main(void) {
clock_setup();
systick_setup();
gpio_setup();
while (1) {
gpio_toggle(BLINK_PORT, BLINK_PIN);
delay_ms(BLINK_PERIOD_MS);
}
return 0;
}

View file

@ -23,8 +23,6 @@
};
stylix.enable = true;
stylix.scheme = "generate-from-image";
stylix.image = ./wallpaper.png;
home.stateVersion = "25.11";
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 MiB

View file

@ -1,38 +0,0 @@
let
port = 3080;
in
{
name = "librechat-oci-test";
nodes.machine =
{ ... }:
{
imports = [
../../modules/nixos/librechat-oci
];
config = {
virtualisation.diskSize = 32768;
virtualisation.memorySize = 8192;
services.librechat-oci = {
enable = true;
inherit port;
};
networking.firewall.enable = false;
};
};
testScript = ''
start_all()
machine.wait_for_unit("default.target")
machine.wait_for_unit("podman-librechat-mongodb.service")
machine.wait_for_unit("podman-librechat.service")
machine.wait_for_open_port(${toString port})
machine.succeed("curl --fail --retry 10 --retry-delay 5 --retry-connrefused http://localhost:${toString port}/")
'';
}