diff --git a/.gitignore b/.gitignore index 63cc0cf..494da59 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ .stfolder secrets.nix -variables.nix +squirtle.satstack.dev/variables.nix archive/ glance/config.yaml result \ No newline at end of file diff --git a/litten.brenise.dev/README.md b/litten.brenise.dev/README.md deleted file mode 100644 index 6af2150..0000000 --- a/litten.brenise.dev/README.md +++ /dev/null @@ -1,60 +0,0 @@ -# Litten - -* n8n - [cli commands](https://docs.n8n.io/hosting/cli-commands/#credentials) - -## Caddy with namecheap-dns - -Some special handling is necessary for Caddy - -```sh -#!/usr/bin/env bash -# This script demonstrates how to construct a Go pseudo-version for a commit from the -# "caddy-dns/namecheap" repository using curl, jq, and date. -# -# The pseudoversion format is: -# 0.0.0-YYYYMMDDHHMMSS- -# -# Steps: -# -# 1. Query the GitHub API for the commit metadata. (The URL is: -# https://api.github.com/repos/caddy-dns/namecheap/commits/HEAD -# You can replace HEAD with a specific commit SHA if needed.) -# -# 2. Extract the commit date. The GitHub API returns the date in ISO 8601 format, e.g., -# "2025-02-15T18:47:56Z". -# -# 3. Convert that date to UTC timestamp in the format YYYYMMDDHHMMSS. -# -# 4. Extract the full commit SHA and take its first 12 characters. -# -# 5. Combine these values in the form: "0.0.0--", resulting in a pseudo-version, -# like: "0.0.0-20250215184756-635a4c34fd25" -# -# Example: -# Given: -# Commit date = "2025-02-15T18:47:56Z" -# Commit SHA = "635a4c34fd25d48df62f8e9d5485ac9026796a83" -# The pseudo-version will be: -# 0.0.0-20250215184756-635a4c34fd25 - -# Set the commit reference (or use a specific commit SHA instead of "HEAD") -COMMIT_REF="HEAD" - -# Fetch commit metadata from GitHub API -COMMIT_JSON=$(curl -s "https://api.github.com/repos/caddy-dns/namecheap/commits/$COMMIT_REF") - -# Extract the commit date (author date) -COMMIT_DATE=$(echo "$COMMIT_JSON" | jq -r '.commit.author.date') - -# Convert the date to UTC (should already be in UTC if it ends with 'Z') and format it as YYYYMMDDHHMMSS -TIMESTAMP=$(date -u -d "$COMMIT_DATE" +"%Y%m%d%H%M%S") - -# Extract the full commit hash and get the first 12 characters -FULL_HASH=$(echo "$COMMIT_JSON" | jq -r '.sha') -SHORT_HASH=${FULL_HASH:0:12} - -# Construct the pseudo-version string -PSEUDO_VERSION="0.0.0-${TIMESTAMP}-${SHORT_HASH}" - -echo "Pseudo-Version: $PSEUDO_VERSION" -``` diff --git a/litten.brenise.dev/configuration.nix b/litten.brenise.dev/configuration.nix index b257bad..4a10111 100644 --- a/litten.brenise.dev/configuration.nix +++ b/litten.brenise.dev/configuration.nix @@ -1,51 +1,31 @@ { config, pkgs, lib, ... }: let - host_name = "litten"; - host_fqdn = "${host_name}.brenise.dev"; - secrets = import ./secrets.nix; - - # Add nixpkgs-unstable channel with the following command: - # nix-channel --add https://nixos.org/channels/nixpkgs-unstable nixpkgs-unstable && nix-channel --update - # 09/15/24: Use nixpkgs-unstable for logseq, see https://github.com/NixOS/nixpkgs/pull/340427 - unstable = import { config = config.nixpkgs.config; }; - - # https://github.com/NixOS/nixpkgs/pull/358586 - caddyWithPlugins = unstable.caddy.withPlugins { - # commit: 7095083a353829fc83632c34e8988fd8eb72f43d (01/14/2024) - plugins = ["github.com/caddy-dns/namecheap@v0.0.0-20240114194457-7095083a3538"]; - hash = "sha256-Wu4j6XBNHX+ckCBmqTMC7ECLnOc3D3IJ04sJF14NHJo="; # WARNING: This will change every update - }; + importedVars = import ./variables.nix { inherit config pkgs lib; }; in +with importedVars; { -# nix.settings.experimental-features = [ "nix-command" "flakes" ]; -# nix.shellHook = '' -# if [ -z "$IN_NIX_SHELL" ]; then -# exec fish -# fi -# ''; - nixpkgs.config = { allowUnfreePredicate = pkg: builtins.elem (lib.getName pkg) [ - "claude-code" + # "claude-code" "obsidian" "packer" "reaper" - "n8n" + # "n8n" "vscode" "terraform" "zoom" - "charlatan3" - "steam" - "steam-original" - "steam-unwrapped" - "steam-run" - "tokenizer.json" # goose-cli + # "charlatan3" + # "steam" + # "steam-original" + # "steam-unwrapped" + # "steam-run" + # "tokenizer.json" # goose-cli ]; }; nixpkgs.overlays = [ (self: super: { - n8n = unstable.n8n; + # n8n = unstable.n8n; ansible = super.ansible.overrideAttrs (oldAttrs: { propagatedBuildInputs = oldAttrs.propagatedBuildInputs ++ [ super.python312Packages.jmespath ]; @@ -138,33 +118,34 @@ in ]; packages = with pkgs; [ # unstable.goose-cli + mariadb + notify-client gh - solaar - binutils brave chromium - coreutils # base64 element-desktop firefox + haruna gimp gnumake home-manager jellyfin-media-player - kate # kwrite - kdenlive - libsForQt5.kcalc + kdePackages.kate #kwrite + kdePackages.kdenlive + kdePackages.kcalc + # libsForQt5.kcalc # unstable.logseq # warning: https://github.com/logseq/logseq/issues/10851#issuecomment-2402925912 # unstable.ghostty moonlight-qt nmap obs-studio obsidian - pavucontrol - # qbittorrent + # pavucontrol + qbittorrent rclone - rtorrent + # rtorrent sq - synergy + # synergy tenacity thunderbird tor-browser @@ -203,14 +184,6 @@ in ]; }; - claude = { - isNormalUser = true; - packages = with pkgs; [ - unstable.claude-code - unstable.aider-chat - ]; - }; - glance = { isSystemUser = true; group = "glance"; @@ -226,8 +199,9 @@ in environment = { systemPackages = with pkgs; [ - # GPU tools arp-scan + binutils + coreutils cryptsetup curl difftastic @@ -281,15 +255,17 @@ in } ''; - plasma5.excludePackages = with pkgs.libsForQt5; [ + # vscode on Wayland + sessionVariables.NIXOS_OZONE_WL = "1"; + # TODO: https://github.com/nix-community/plasma-manager + plasma6.excludePackages = with pkgs.kdePackages; [ plasma-browser-integration ]; + etc."gitconfig".text = '' [init] defaultBranch = main ''; - # vscode on Wayland - #sessionVariables.NIXOS_OZONE_WL = "1"; }; programs = { @@ -333,7 +309,6 @@ in enableSSHSupport = true; }; kdeconnect.enable = true; - steam.enable = true; chromium = { enable = true; extraOpts = { @@ -358,10 +333,6 @@ in } ]; }; -# pki.certificateFiles = [ -# "${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt" -# # "/etc/ssl/certs/dotted-turbans.pem" -# ]; }; services = { @@ -376,10 +347,10 @@ in alsa.enable = true; # for tenacity # jack.enable = true; # this might be useful for reaper, but enabling it breaks stereo audio for some reason }; - avahi = { # for resolving start9 hostname - enable = true; - nssmdns4 = true; # Enable NSS support for mDNS - }; +# avahi = { # for resolving start9 hostname +# enable = true; +# nssmdns4 = true; # Enable NSS support for mDNS +# }; syncthing = { enable = true; @@ -408,7 +379,8 @@ in enable = true; package = caddyWithPlugins; - logFormat = "output discard"; +# logFormat = "output discard"; + logFormat = "level DEBUG"; extraConfig = let tlsConfig = '' tls { @@ -420,9 +392,9 @@ in } ''; in '' - ${host_fqdn} { # open-webui + ${host_fqdn} { ${tlsConfig} - reverse_proxy http://127.0.0.1:8080 + reverse_proxy http://127.0.0.1:8034 # open-webui # handle /files/* { # root /mnt/usb/ @@ -431,29 +403,29 @@ in } - ${host_fqdn}:4430 { # node_exporter + ${host_fqdn}:4430 { ${tlsConfig} - reverse_proxy http://127.0.0.1:8030 + reverse_proxy http://127.0.0.1:8030 # node_exporter } - ${host_fqdn}:4431 { # litellm + ${host_fqdn}:4432 { ${tlsConfig} - reverse_proxy http://127.0.0.1:8031 + reverse_proxy http://127.0.0.1:8032 # glance } - ${host_fqdn}:4432 { # glance + ${host_fqdn}:4434 { ${tlsConfig} - reverse_proxy http://127.0.0.1:8032 + reverse_proxy http://127.0.0.1:8034 # open-webui } - ${host_fqdn}:4433 { # n8n + ${host_fqdn}:4435 { ${tlsConfig} - reverse_proxy http://127.0.0.1:8033 + reverse_proxy http://127.0.0.1:8035 # mealie } - ${host_fqdn}:4435 { # mealie + ${host_fqdn}:4436 { ${tlsConfig} - reverse_proxy http://127.0.0.1:8035 + reverse_proxy http://127.0.0.1:8036 # litellm } :9999 { @@ -462,24 +434,107 @@ in ''; }; - displayManager = { - sddm.enable = true; - #defaultSession = "plasmawayland"; - }; - xserver = { + open-webui = { enable = true; - desktopManager.plasma5.enable = true; + port = 8034; }; + litellm = { + enable = true; + port = 8036; + environmentFile = litellmEnvFile; + settings = { + litellm_settings = { + set_verbose = true; + }; + model_list = [ + { + model_name = "grok-3"; + litellm_params = { + model = "xai/grok-3-latest"; + api_key = "os.environ/XAI_API_KEY"; # LiteLLM reads this env var + }; + } + { + model_name = "deepseek-r1-distill-llama-70b"; + litellm_params = { + model = "groq/deepseek-r1-distill-llama-70b"; + api_key = "os.environ/GROK_API_KEY"; # LiteLLM reads this env var + }; + } + { + model_name = "claude-3.5-sonnet"; + litellm_params = { + model = "anthropic/claude-3-5-sonnet-latest"; + api_key = "os.environ/ANTHROPIC_API_KEY"; # LiteLLM reads this env var + }; + } + { + model_name = "gemini-2.0-flash"; + litellm_params = { + model = "gemini/gemini-2.0-flash-exp"; + api_key = "os.environ/GOOGLE_API_KEY"; # LiteLLM reads this env var + safety_settings = [ + { category = "HARM_CATEGORY_HARASSMENT"; threshold = "BLOCK_NONE"; } + { category = "HARM_CATEGORY_HATE_SPEECH"; threshold = "BLOCK_NONE"; } + { category = "HARM_CATEGORY_SEXUALLY_EXPLICIT"; threshold = "BLOCK_NONE"; } + { category = "HARM_CATEGORY_DANGEROUS_CONTENT"; threshold = "BLOCK_NONE"; } + ]; + }; + } + ]; + }; + }; + + mealie = { enable = true; port = 8035; - listenAddress = "127.0.0.1"; - package = unstable.mealie; + # listenAddress = "127.0.0.1"; + listenAddress = "0.0.0.0"; +# package = unstable.mealie; }; - n8n.enable = true; + # n8n.enable = true; + strongswan = { + enable = false; + secrets = [ + "/etc/nixos/ipsec.secrets" + ]; + connections = { + "aws-vpn" = { + type = "tunnel"; + authby = "secret"; + left = "%defaultroute"; + leftsubnet = "0.0.0.0/0"; + right = "${secrets.aws-vpn.tunnel-address}"; + rightsubnet = "10.0.64.0/24"; # subnet-0f2b39fd2ed6a88d7 "private-lambda-us-west-2d" + ike = "aes256-sha2_256-modp2048!"; + esp = "aes256-sha2_256!"; + ikelifetime = "8h"; + keylife = "1h"; + keyingtries = "3"; + auto = "start"; + installpolicy = "yes"; # Install IPsec policies + routing = "yes"; # Install routes + dpdaction = "restart"; # Restart connection if peer is unreachable + dpddelay = "30s"; # Dead peer detection delay + closeaction = "restart"; # Restart connection if it drops + }; + }; + setup = { + charondebug = "esp 2, ike 2, cfg 2, net 2, enc 2"; + }; + }; + + desktopManager.plasma6.enable = true; + displayManager = { + sddm.enable = true; + sddm.wayland.enable = true; + #defaultSession = "plasmawayland"; + }; + xserver.enable = true; }; systemd = { @@ -496,18 +551,6 @@ in }; }; - services.open-webui = { - wantedBy = [ "multi-user.target" ]; - after = [ "network.target" ]; - serviceConfig = { - Type = "forking"; - User = "blee"; - WorkingDirectory = "/opt/open-webui"; - Environment = "NIX_PATH=nixpkgs=/nix/var/nix/profiles/per-user/root/channels/nixos:nixos-config=/etc/nixos/configuration.nix:/nix/var/nix/profiles/per-user/root/channels"; - }; - script = "${pkgs.nix}/bin/nix-shell"; - }; - services.ena-vpn = { description = "ENA Corp VPN"; # wantedBy = [ "multi-user.target" ]; # 2fa required, avoid startup @@ -525,12 +568,17 @@ in OPENAI_API_KEY = "${secrets.mealie.openai-api-key}"; }; - services.n8n.environment = { - N8N_EDITOR_BASE_URL = "https://${host_fqdn}:4433"; - N8N_HIRING_BANNER_ENABLED = "false"; - N8N_METRICS = "true"; - N8N_PORT = "8033"; - }; + # services.n8n.environment = { + # N8N_EDITOR_BASE_URL = "https://${host_fqdn}:4433"; + # N8N_DIAGNOSTICS_ENABLED = "false"; # Also disables "Ask AI" in the code node + # N8N_HIRING_BANNER_ENABLED = "false"; + # N8N_METRICS = "true"; + # N8N_PORT = "8033"; + # }; + + tmpfiles.rules = [ + "d /srv/src 0775 root users - -" + ]; }; @@ -549,6 +597,12 @@ in mode = "0600"; }; + "ipsec.secrets" = { + text = '' + ${secrets.aws-vpn.tunnel-address} : PSK "${secrets.aws-vpn.psk}" + ''; + mode = "0600"; + }; }; virtualisation.docker = { diff --git a/litten.brenise.dev/cursor/launch.sh b/litten.brenise.dev/cursor/launch.sh new file mode 100755 index 0000000..64bb786 --- /dev/null +++ b/litten.brenise.dev/cursor/launch.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash + +# Directory containing Cursor AppImages +CURSOR_DIR="/home/blee/apps/cursor" + +# Find the most recent Cursor AppImage +LATEST_APPIMAGE=$(ls -t "$CURSOR_DIR"/Cursor-*.AppImage | head -n 1) + +# Execute the AppImage if found +if [ -n "$LATEST_APPIMAGE" ]; then + exec "$LATEST_APPIMAGE" +else + echo "No Cursor AppImage found in $CURSOR_DIR" + exit 1 +fi diff --git a/litten.brenise.dev/opt/open-webui/Makefile b/litten.brenise.dev/opt/open-webui/Makefile deleted file mode 100644 index c3cf78d..0000000 --- a/litten.brenise.dev/opt/open-webui/Makefile +++ /dev/null @@ -1,40 +0,0 @@ -# Makefile for managing virtual environments - -VENV_OPEN_WEBUI=.venv_open_webui -VENV_LITELLM=.venv_litellm -ACTIVATE_OPEN_WEBUI=$(VENV_OPEN_WEBUI)/bin/activate.fish -ACTIVATE_LITELLM=$(VENV_LITELLM)/bin/activate.fish - -.PHONY: install install_open_webui install_litellm update update_open_webui update_litellm - -install_open_webui: - @echo "Creating open-webui environment..." - python -m venv $(VENV_OPEN_WEBUI) - source $(ACTIVATE_OPEN_WEBUI) && \ - pip install -U pip open-webui && \ - deactivate - -install_litellm: - @echo "Creating litellm environment..." - python -m venv $(VENV_LITELLM) - source $(ACTIVATE_LITELLM) && \ - pip install -U pip litellm[proxy] && \ - deactivate - -install: install_open_webui install_litellm - @echo "All environments created and packages installed." - -update_open_webui: - @echo "Updating open-webui environment..." - source $(ACTIVATE_OPEN_WEBUI) && \ - pip install -U open-webui && \ - deactivate - -update_litellm: - @echo "Updating litellm environment..." - source $(ACTIVATE_LITELLM) && \ - pip install -U litellm[proxy] && \ - deactivate - -update: update_open_webui update_litellm - @echo "All environments updated." diff --git a/litten.brenise.dev/opt/open-webui/README.md b/litten.brenise.dev/opt/open-webui/README.md deleted file mode 100644 index 2ec4142..0000000 --- a/litten.brenise.dev/opt/open-webui/README.md +++ /dev/null @@ -1,24 +0,0 @@ -# open-webui - -Installation: - -```sh -mkdir -p /opt/open-webui -cp -v opt/open-webui/{Makefile,run-open-webui.sh, shell.nix} /opt/open-webui/ -cd /opt/open-webui -make install -``` - -Update: - -```sh -cd /opt/open-webui -make update -``` - -Running: - -```sh -cd /opt/open-webui -nix-shell -``` diff --git a/litten.brenise.dev/opt/open-webui/litellm.yaml b/litten.brenise.dev/opt/open-webui/litellm.yaml deleted file mode 100644 index 8c6af6f..0000000 --- a/litten.brenise.dev/opt/open-webui/litellm.yaml +++ /dev/null @@ -1,54 +0,0 @@ -#general_settings: {} -#litellm_settings: {} -litellm_settings: - set_verbose: True - -# https://docs.litellm.ai/docs/providers/ -model_list: - - # https://docs.x.ai/docs/overview#featured-models - - model_name: grok-3 - litellm_params: - model: xai/grok-3-latest - api_key: os.environ/XAI_API_KEY - - # https://console.groq.com/docs/models - - model_name: deepseek-r1-distill-llama-70b - litellm_params: - model: groq/deepseek-r1-distill-llama-70b - api_key: os.environ/GROK_API_KEY - - # https://docs.anthropic.com/en/docs/about-claude/models - - model_name: claude-3.5-sonnet - litellm_params: - model: anthropic/claude-3-5-sonnet-latest - api_key: os.environ/ANTHROPIC_API_KEY - - # https://ai.google.dev/gemini-api/docs/models/gemini#gemini-2.0-flash - - model_name: gemini-2.0-flash - litellm_params: - model: gemini/gemini-2.0-flash-exp - api_key: os.environ/GOOGLE_API_KEY - safety_settings: - - category: HARM_CATEGORY_HARASSMENT - threshold: BLOCK_NONE - - category: HARM_CATEGORY_HATE_SPEECH - threshold: BLOCK_NONE - - category: HARM_CATEGORY_SEXUALLY_EXPLICIT - threshold: BLOCK_NONE - - category: HARM_CATEGORY_DANGEROUS_CONTENT - threshold: BLOCK_NONE - - # Note: no payment method added yet, identity not disclosed - # https://docs.mistral.ai/getting-started/models/models_overview/ -# - model_name: mistral-large -# litellm_params: -# model: mistral/mistral-large-latest -# api_key: os.environ/MISTRAL_API_KEY - - -#router_settings: {} - - -#router_settings: {} - diff --git a/litten.brenise.dev/opt/open-webui/run-open-webui.sh b/litten.brenise.dev/opt/open-webui/run-open-webui.sh deleted file mode 100755 index 2b42929..0000000 --- a/litten.brenise.dev/opt/open-webui/run-open-webui.sh +++ /dev/null @@ -1,31 +0,0 @@ -#!/usr/bin/env bash - -# Exit on error, undefined variables, and print commands -set -e - -# Default values (can be overridden by command-line arguments) -SESSION_NAME="${1:-textgen}" -LITELLM_PORT="${2:-8031}" - -if tmux has-session -t "$SESSION_NAME" 2>/dev/null; then - echo "Session '$SESSION_NAME' already exists. No action taken." - exit 0 -fi - -# Check if session already exists -if ! tmux has-session -t "$SESSION_NAME" 2>/dev/null; then - # Create new detached session - tmux new-session -d -s "$SESSION_NAME" - - # Start the open-webui server in its own virtual environment - tmux send-keys -t "$SESSION_NAME" "source .venv_open_webui/bin/activate.fish && open-webui serve" C-m - - # Start litellm in a new pane with its own virtual environment - tmux split-window -v -t "$SESSION_NAME" - tmux send-keys -t "$SESSION_NAME" "source .venv_litellm/bin/activate.fish && source .env && litellm --telemetry False --config ./litellm.yaml --host 127.0.0.1 --port $LITELLM_PORT" C-m - - echo "Session '$SESSION_NAME' created and configured. To connect, type: tmux attach -t $SESSION_NAME" -else - echo "Session '$SESSION_NAME' already exists. Exiting." - exit 1 -fi diff --git a/litten.brenise.dev/opt/open-webui/shell.nix b/litten.brenise.dev/opt/open-webui/shell.nix deleted file mode 100644 index df5723b..0000000 --- a/litten.brenise.dev/opt/open-webui/shell.nix +++ /dev/null @@ -1,11 +0,0 @@ -{ pkgs ? import {} }: -(pkgs.buildFHSEnv { - name = "open-webui-fhs-env"; - targetPkgs = pkgs: with pkgs; [ - tmux -# bash -# python311 - ]; - runScript = "./run-open-webui.sh"; -}).env - diff --git a/litten.brenise.dev/variables.nix b/litten.brenise.dev/variables.nix new file mode 100644 index 0000000..930f683 --- /dev/null +++ b/litten.brenise.dev/variables.nix @@ -0,0 +1,54 @@ +{ config, pkgs, lib, ... }: + +let + host_name = "litten"; + host_fqdn = "${host_name}.brenise.dev"; + + unstable = import { config = config.nixpkgs.config; }; + secrets = import ./secrets.nix; + + # https://nixos.wiki/wiki/Fish + interactiveShellInit = '' + if [[ $(${pkgs.procps}/bin/ps --no-header --pid=$PPID --format=comm) != "fish" && -z ''${BASH_EXECUTION_STRING} ]] + then + shopt -q login_shell && LOGIN_OPTION='--login' || LOGIN_OPTION="" + exec ${pkgs.fish}/bin/fish $LOGIN_OPTION + fi + ''; + + + # Caddy+namecheap plugin builds fail on v2.10.0+ due to libdns changes: https://github.com/caddy-dns/namecheap/issues/14 + # Pin caddy v2.9.1 and go 1.23 + pinnedPkgs = fetchTarball { + # This is the commit merged from the caddy: 2.9.1 PR 375655 + url = "https://github.com/NixOS/nixpkgs/archive/4ae25041b2c187c0f696b6fc39c196677fb41112.tar.gz"; + sha256 = "sha256:12009w2iph89i18bc646y2qhskv6gmq9fmchrcjrw1ibxadnkv4z"; + }; + caddy_pinned = import pinnedPkgs { config = config.nixpkgs.config; }; + caddyWithPlugins = caddy_pinned.caddy.withPlugins { + plugins = ["github.com/caddy-dns/namecheap@v0.0.0-20250228023406-ef9fadb67785"]; + hash = "sha256-WDA/qSMwMqLWaohNSCtc/lWFdTJKeONapLconR87cUI="; # This will change every update + }; + + + litellmEnvContent = '' + LITELLM_MASTER_KEY=${secrets.litellmApiKeys.LITELLM_MASTER_KEY} + XAI_API_KEY=${secrets.litellmApiKeys.XAI_API_KEY} + GROK_API_KEY=${secrets.litellmApiKeys.GROK_API_KEY} + ANTHROPIC_API_KEY=${secrets.litellmApiKeys.ANTHROPIC_API_KEY} + GOOGLE_API_KEY=${secrets.litellmApiKeys.GOOGLE_API_KEY} + ''; + litellmEnvFile = pkgs.writeText "litellm-env" litellmEnvContent; + +in +{ + inherit + host_name + host_fqdn + unstable + secrets + interactiveShellInit + litellmEnvContent + litellmEnvFile + caddyWithPlugins; +} \ No newline at end of file