Compare commits
2
Commits
main
..
c684beed09
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c684beed09
|
||
|
|
04dddc628f
|
@@ -14,25 +14,36 @@ jobs:
|
|||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@v6
|
||||||
|
|
||||||
|
- name: Debug environment
|
||||||
|
run: |
|
||||||
|
echo "=== System Info ==="
|
||||||
|
uname -a
|
||||||
|
nix --version
|
||||||
|
echo "=== Memory ==="
|
||||||
|
free -h || true
|
||||||
|
echo "=== Disk ==="
|
||||||
|
df -h /nix /tmp || true
|
||||||
|
echo "=== Nix Config ==="
|
||||||
|
nix show-config 2>&1 | head -20 || true
|
||||||
|
|
||||||
- name: Build aarch64 Image
|
- name: Build aarch64 Image
|
||||||
run: nix build .#dockerImages.aarch64-linux.node --out-link ./image-aarch64.tar.gz --option build-hook ""
|
run: nix build .#dockerImages.aarch64-linux.node --out-link ./image-aarch64.tar.gz --option build-hook ""
|
||||||
|
|
||||||
- name: Push to Gitea Registry
|
- name: Push to Gitea Registry
|
||||||
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
||||||
|
env:
|
||||||
|
GITEA_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
run: |
|
run: |
|
||||||
REGISTRY="${{ github.server_url }}"
|
|
||||||
REGISTRY="${REGISTRY#https://}"
|
|
||||||
REGISTRY="${REGISTRY#http://}"
|
|
||||||
|
|
||||||
# Push aarch64 image
|
# Push aarch64 image
|
||||||
skopeo copy \
|
skopeo copy \
|
||||||
--insecure-policy \
|
--insecure-policy \
|
||||||
--dest-creds "${{ github.repository_owner }}:${{ secrets.REGISTRY_TOKEN }}" \
|
--dest-creds "${{ github.actor }}:${GITEA_TOKEN}" \
|
||||||
"docker-archive:./image-aarch64.tar.gz" \
|
"docker-archive:./image-aarch64.tar.gz" \
|
||||||
"docker://${REGISTRY}/${{ github.repository }}/node:latest"
|
"docker://${{ github.server_url }}/${{ github.repository }}/node:latest-arm64"
|
||||||
|
|
||||||
skopeo copy \
|
# Create and push manifest for arm64
|
||||||
|
skopeo manifest create \
|
||||||
--insecure-policy \
|
--insecure-policy \
|
||||||
--dest-creds "${{ github.repository_owner }}:${{ secrets.REGISTRY_TOKEN }}" \
|
--dest-creds "${{ github.actor }}:${GITEA_TOKEN}" \
|
||||||
"docker-archive:./image-aarch64.tar.gz" \
|
"docker://${{ github.server_url }}/${{ github.repository }}/node:latest" \
|
||||||
"docker://${REGISTRY}/${{ github.repository }}/node:latest-arm64"
|
"docker://${{ github.server_url }}/${{ github.repository }}/node:latest-arm64"
|
||||||
|
|||||||
@@ -2,4 +2,3 @@
|
|||||||
/.direnv
|
/.direnv
|
||||||
/.gcroots
|
/.gcroots
|
||||||
*.qcow2
|
*.qcow2
|
||||||
hosts/turny/ssh_host_ed25519_key
|
|
||||||
|
|||||||
+7
-7
@@ -6,7 +6,7 @@
|
|||||||
"allow_concurrent_runs": true,
|
"allow_concurrent_runs": true,
|
||||||
"reveal": "no_focus",
|
"reveal": "no_focus",
|
||||||
"reveal_target": "dock",
|
"reveal_target": "dock",
|
||||||
"hide": "on_success",
|
"hide": "on_success"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"label": "Apply remote",
|
"label": "Apply remote",
|
||||||
@@ -16,26 +16,26 @@
|
|||||||
"allow_concurrent_runs": true,
|
"allow_concurrent_runs": true,
|
||||||
"reveal": "no_focus",
|
"reveal": "no_focus",
|
||||||
"reveal_target": "dock",
|
"reveal_target": "dock",
|
||||||
"hide": "on_success",
|
"hide": "on_success"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"label": "Apply on freun-dev",
|
"label": "Apply on freun-dev",
|
||||||
"command": "apply",
|
"command": "apply",
|
||||||
"args": ["--on", "freun-dev"],
|
"args": ["--build-on-target", "--on", "freun-dev"],
|
||||||
"use_new_terminal": false,
|
"use_new_terminal": false,
|
||||||
"allow_concurrent_runs": true,
|
"allow_concurrent_runs": true,
|
||||||
"reveal": "no_focus",
|
"reveal": "no_focus",
|
||||||
"reveal_target": "dock",
|
"reveal_target": "dock",
|
||||||
"hide": "on_success",
|
"hide": "on_success"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"label": "Apply on apu",
|
"label": "Apply on apu",
|
||||||
"command": "apply",
|
"command": "apply",
|
||||||
"args": ["--on", "apu"],
|
"args": ["--build-on-target", "--on", "apu"],
|
||||||
"use_new_terminal": false,
|
"use_new_terminal": false,
|
||||||
"allow_concurrent_runs": true,
|
"allow_concurrent_runs": true,
|
||||||
"reveal": "no_focus",
|
"reveal": "no_focus",
|
||||||
"reveal_target": "dock",
|
"reveal_target": "dock",
|
||||||
"hide": "on_success",
|
"hide": "on_success"
|
||||||
},
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
- `nix build .#nixosConfigurations.<host>.config.system.build.toplevel` - Build specific host
|
- `nix build .#nixosConfigurations.<host>.config.system.build.toplevel` - Build specific host
|
||||||
|
|
||||||
## Code Style
|
## Code Style
|
||||||
- **Format**: Use `nixfmt` for all .nix files
|
- **Format**: Use `nixfmt-rfc-style` for all .nix files
|
||||||
- **Imports**: List imports first, group by: local modules, external inputs (inputs.*)
|
- **Imports**: List imports first, group by: local modules, external inputs (inputs.*)
|
||||||
- **Function args**: Multi-line with trailing comma when >3 args: `{ config, lib, pkgs, ... }:`
|
- **Function args**: Multi-line with trailing comma when >3 args: `{ config, lib, pkgs, ... }:`
|
||||||
- **Options**: Define in `options.<service>` block, implement in `config = lib.mkIf cfg.enable { ... }`
|
- **Options**: Define in `options.<service>` block, implement in `config = lib.mkIf cfg.enable { ... }`
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
# My NixOS Configurations
|
# My NixOS Configurations
|
||||||
|
|
||||||
[](https://git.freun.dev/repomaa/nixos/actions?workflow=check.yml) [](https://git.freun.dev/repomaa/nixos/actions?workflow=build-images.yml)
|
[](https://builds.sr.ht/~repomaa/NixOS?)
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
{ util, ... }: util.package { }
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
{ pkgs, ... }:
|
||||||
|
let
|
||||||
|
inherit (pkgs) crystal libffi;
|
||||||
|
in
|
||||||
|
crystal.overrideAttrs (oldAttrs: {
|
||||||
|
makeFlags = oldAttrs.makeFlags ++ [
|
||||||
|
"interpreter=1"
|
||||||
|
];
|
||||||
|
buildInputs = oldAttrs.buildInputs ++ [
|
||||||
|
libffi
|
||||||
|
];
|
||||||
|
FLAGS = [ "--single-module" ];
|
||||||
|
doCheck = false;
|
||||||
|
})
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
{
|
|
||||||
util,
|
|
||||||
lib,
|
|
||||||
...
|
|
||||||
}:
|
|
||||||
util.package { inherit lib; }
|
|
||||||
@@ -1,80 +0,0 @@
|
|||||||
{
|
|
||||||
pkgs,
|
|
||||||
lib,
|
|
||||||
}:
|
|
||||||
let
|
|
||||||
version = "4.9.9";
|
|
||||||
|
|
||||||
src = pkgs.fetchFromGitHub {
|
|
||||||
owner = "kreuzberg-dev";
|
|
||||||
repo = "kreuzberg";
|
|
||||||
rev = "v${version}";
|
|
||||||
hash = "sha256-eu0qjyxoRiDLHsBLPPPAqb/DKVaPzqgP/4hHPjgOBoM=";
|
|
||||||
};
|
|
||||||
in
|
|
||||||
pkgs.rustPlatform.buildRustPackage {
|
|
||||||
pname = "kreuzberg-cli";
|
|
||||||
inherit version src;
|
|
||||||
|
|
||||||
cargoDeps = pkgs.rustPlatform.fetchCargoVendor {
|
|
||||||
inherit src;
|
|
||||||
hash = "sha256-j1tmjQ7KQQAMz3QxhQGTGracMqqdcaX/+74lpUfU/kE=";
|
|
||||||
};
|
|
||||||
|
|
||||||
buildFeatures = [ "all" ];
|
|
||||||
doCheck = false;
|
|
||||||
|
|
||||||
nativeBuildInputs = [
|
|
||||||
pkgs.cmake
|
|
||||||
pkgs.makeWrapper
|
|
||||||
pkgs.pkg-config
|
|
||||||
];
|
|
||||||
|
|
||||||
buildInputs = [
|
|
||||||
pkgs.pdfium-binaries
|
|
||||||
pkgs.openssl
|
|
||||||
pkgs.onnxruntime
|
|
||||||
pkgs.tesseract
|
|
||||||
pkgs.leptonica
|
|
||||||
];
|
|
||||||
|
|
||||||
dontUseCmakeConfigure = true;
|
|
||||||
|
|
||||||
env = {
|
|
||||||
KREUZBERG_PDFIUM_PREBUILT = pkgs.pdfium-binaries;
|
|
||||||
ORT_LIB_PATH = "${pkgs.onnxruntime}/lib";
|
|
||||||
ORT_PREFER_DYNAMIC_LINK = "1";
|
|
||||||
};
|
|
||||||
|
|
||||||
postPatch = ''
|
|
||||||
substituteInPlace crates/kreuzberg/Cargo.toml \
|
|
||||||
--replace 'kreuzberg-tesseract = { path = "../kreuzberg-tesseract", version = "4.9.9", optional = true }' \
|
|
||||||
'kreuzberg-tesseract = { path = "../kreuzberg-tesseract", version = "4.9.9", optional = true, default-features = false, features = ["dynamic-linking"] }'
|
|
||||||
|
|
||||||
for f in crates/kreuzberg-tesseract/src/*.rs; do
|
|
||||||
substituteInPlace "$f" \
|
|
||||||
--replace 'any(feature = "build-tesseract", feature = "build-tesseract-wasm")' \
|
|
||||||
'any(feature = "build-tesseract", feature = "build-tesseract-wasm", feature = "dynamic-linking")'
|
|
||||||
done
|
|
||||||
'';
|
|
||||||
|
|
||||||
postFixup = ''
|
|
||||||
wrapProgram $out/bin/kreuzberg \
|
|
||||||
--prefix LD_LIBRARY_PATH : ${
|
|
||||||
lib.makeLibraryPath [
|
|
||||||
pkgs.tesseract
|
|
||||||
pkgs.onnxruntime
|
|
||||||
]
|
|
||||||
} \
|
|
||||||
--set TESSDATA_PREFIX ${pkgs.tesseract}/share
|
|
||||||
ln -s kreuzberg $out/bin/kreuzberg-cli
|
|
||||||
'';
|
|
||||||
|
|
||||||
meta = {
|
|
||||||
description = "High-performance document intelligence CLI with a Rust core";
|
|
||||||
homepage = "https://github.com/kreuzberg-dev/kreuzberg";
|
|
||||||
license = lib.licenses.unfree;
|
|
||||||
platforms = [ "x86_64-linux" ];
|
|
||||||
mainProgram = "kreuzberg";
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
{
|
|
||||||
util,
|
|
||||||
lib,
|
|
||||||
...
|
|
||||||
}:
|
|
||||||
util.package { inherit lib; }
|
|
||||||
@@ -1,160 +0,0 @@
|
|||||||
{
|
|
||||||
lib,
|
|
||||||
pkgs,
|
|
||||||
}:
|
|
||||||
let
|
|
||||||
stylesheet = pkgs.writeText "md2pdf-style.css" ''
|
|
||||||
body {
|
|
||||||
font-family: "Noto Sans", sans-serif;
|
|
||||||
font-size: 11px;
|
|
||||||
max-width: 100%;
|
|
||||||
margin: 0;
|
|
||||||
padding: 15px;
|
|
||||||
line-height: 1.4;
|
|
||||||
}
|
|
||||||
h1 {
|
|
||||||
font-size: 22px;
|
|
||||||
border-bottom: 2px solid #333;
|
|
||||||
padding-bottom: 5px;
|
|
||||||
break-after: avoid;
|
|
||||||
}
|
|
||||||
h2 {
|
|
||||||
font-size: 18px;
|
|
||||||
margin-top: 25px;
|
|
||||||
border-bottom: 1px solid #ccc;
|
|
||||||
padding-bottom: 3px;
|
|
||||||
break-after: avoid;
|
|
||||||
}
|
|
||||||
h3 {
|
|
||||||
font-size: 14px;
|
|
||||||
margin-top: 15px;
|
|
||||||
break-after: avoid;
|
|
||||||
}
|
|
||||||
table {
|
|
||||||
border-collapse: collapse;
|
|
||||||
width: 100%;
|
|
||||||
font-size: 10px;
|
|
||||||
margin: 10px 0;
|
|
||||||
}
|
|
||||||
th {
|
|
||||||
background: #f0f0f0;
|
|
||||||
border: 1px solid #ccc;
|
|
||||||
padding: 4px 6px;
|
|
||||||
text-align: left;
|
|
||||||
break-after: avoid;
|
|
||||||
break-inside: avoid;
|
|
||||||
}
|
|
||||||
td {
|
|
||||||
border: 1px solid #ccc;
|
|
||||||
padding: 4px 6px;
|
|
||||||
vertical-align: top;
|
|
||||||
break-inside: avoid;
|
|
||||||
}
|
|
||||||
tr {
|
|
||||||
break-inside: avoid;
|
|
||||||
break-after: auto;
|
|
||||||
}
|
|
||||||
code {
|
|
||||||
background: #f5f5f5;
|
|
||||||
padding: 1px 3px;
|
|
||||||
border-radius: 2px;
|
|
||||||
font-family: "Noto Sans Mono", monospace;
|
|
||||||
font-size: 10px;
|
|
||||||
}
|
|
||||||
pre {
|
|
||||||
background: #f5f5f5;
|
|
||||||
padding: 10px;
|
|
||||||
border-radius: 4px;
|
|
||||||
overflow-x: auto;
|
|
||||||
font-size: 10px;
|
|
||||||
}
|
|
||||||
pre code {
|
|
||||||
background: none;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
blockquote {
|
|
||||||
border-left: 3px solid #ccc;
|
|
||||||
margin-left: 0;
|
|
||||||
padding-left: 10px;
|
|
||||||
color: #555;
|
|
||||||
}
|
|
||||||
img {
|
|
||||||
max-width: 100%;
|
|
||||||
height: auto;
|
|
||||||
border: 1px solid #ddd;
|
|
||||||
margin: 5px 0;
|
|
||||||
break-inside: avoid;
|
|
||||||
}
|
|
||||||
em {
|
|
||||||
color: #666;
|
|
||||||
}
|
|
||||||
a {
|
|
||||||
color: #0066cc;
|
|
||||||
}
|
|
||||||
'';
|
|
||||||
|
|
||||||
md-to-pdf = pkgs.buildNpmPackage {
|
|
||||||
pname = "md-to-pdf";
|
|
||||||
version = "5.2.5";
|
|
||||||
|
|
||||||
src = pkgs.fetchFromGitHub {
|
|
||||||
owner = "simonhaenisch";
|
|
||||||
repo = "md-to-pdf";
|
|
||||||
rev = "v5.2.5";
|
|
||||||
hash = "sha256-6Zr4jidQ6cX2t6jKFqEnCSqvRtw4gZkqDKdv9SIyDC4=";
|
|
||||||
};
|
|
||||||
|
|
||||||
npmDepsHash = "sha256-AGn8g/nbkvWi4o6ENCnt7VexjfBIoDz+tl9eYvy+bao=";
|
|
||||||
|
|
||||||
# canvas is an optional native dependency that fails to build in nix;
|
|
||||||
# md-to-pdf works fine without it (puppeteer is used for PDF generation)
|
|
||||||
npmFlags = [ "--ignore-scripts" ];
|
|
||||||
|
|
||||||
postInstall = ''
|
|
||||||
rm -rf $out/lib/node_modules/md-to-pdf/node_modules/canvas
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
in
|
|
||||||
pkgs.stdenv.mkDerivation {
|
|
||||||
pname = "md2pdf";
|
|
||||||
version = "1.0.0";
|
|
||||||
|
|
||||||
src = ./.;
|
|
||||||
|
|
||||||
dontBuild = true;
|
|
||||||
|
|
||||||
nativeBuildInputs = [ ];
|
|
||||||
|
|
||||||
installPhase = ''
|
|
||||||
runHook preInstall
|
|
||||||
|
|
||||||
mkdir -p $out/bin $out/share/md2pdf
|
|
||||||
|
|
||||||
cp ${stylesheet} $out/share/md2pdf/style.css
|
|
||||||
|
|
||||||
cat > $out/bin/md2pdf << 'WRAPPER'
|
|
||||||
#!/bin/sh
|
|
||||||
export PUPPETEER_EXECUTABLE_PATH="@chromium@/bin/chromium"
|
|
||||||
export PATH="@chromium@/bin''${PATH:+:''${PATH}}"
|
|
||||||
exec "@md-to-pdf@/bin/md-to-pdf" \
|
|
||||||
--stylesheet "@stylesheet@" \
|
|
||||||
--pdf-options '{"format":"A4","margin":{"top":"10mm","bottom":"10mm","left":"12mm","right":"12mm"},"printBackground":true}' \
|
|
||||||
"$@"
|
|
||||||
WRAPPER
|
|
||||||
|
|
||||||
substituteInPlace $out/bin/md2pdf \
|
|
||||||
--replace-fail "@chromium@" "${pkgs.chromium}" \
|
|
||||||
--replace-fail "@md-to-pdf@" "${md-to-pdf}" \
|
|
||||||
--replace-fail "@stylesheet@" "$out/share/md2pdf/style.css"
|
|
||||||
|
|
||||||
chmod +x $out/bin/md2pdf
|
|
||||||
|
|
||||||
runHook postInstall
|
|
||||||
'';
|
|
||||||
|
|
||||||
meta = {
|
|
||||||
description = "Convert Markdown to PDF with Noto Sans styling and tight margins";
|
|
||||||
mainProgram = "md2pdf";
|
|
||||||
platforms = lib.platforms.linux;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
Generated
+61
-407
@@ -23,64 +23,6 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"blueprint": {
|
|
||||||
"inputs": {
|
|
||||||
"nixpkgs": [
|
|
||||||
"llm-agents",
|
|
||||||
"nixpkgs"
|
|
||||||
],
|
|
||||||
"systems": [
|
|
||||||
"llm-agents",
|
|
||||||
"systems"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1776249299,
|
|
||||||
"narHash": "sha256-Dt9t1TGRmJFc0xVYhttNBD6QsAgHOHCArqGa0AyjrJY=",
|
|
||||||
"owner": "numtide",
|
|
||||||
"repo": "blueprint",
|
|
||||||
"rev": "56131e8628f173d24a27f6d27c0215eff57e40dd",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "numtide",
|
|
||||||
"repo": "blueprint",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"bun2nix": {
|
|
||||||
"inputs": {
|
|
||||||
"flake-parts": [
|
|
||||||
"llm-agents",
|
|
||||||
"flake-parts"
|
|
||||||
],
|
|
||||||
"nixpkgs": [
|
|
||||||
"llm-agents",
|
|
||||||
"nixpkgs"
|
|
||||||
],
|
|
||||||
"systems": [
|
|
||||||
"llm-agents",
|
|
||||||
"systems"
|
|
||||||
],
|
|
||||||
"treefmt-nix": [
|
|
||||||
"llm-agents",
|
|
||||||
"treefmt-nix"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1778446047,
|
|
||||||
"narHash": "sha256-oQvcadh2BCkrog+SGrG6YffKJrveYpjj3TdQJWaKhaM=",
|
|
||||||
"owner": "nix-community",
|
|
||||||
"repo": "bun2nix",
|
|
||||||
"rev": "f2bc12af1a6369648aac41041ceeaa0b866599c6",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "nix-community",
|
|
||||||
"repo": "bun2nix",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"colmena": {
|
"colmena": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"flake-compat": "flake-compat",
|
"flake-compat": "flake-compat",
|
||||||
@@ -92,11 +34,11 @@
|
|||||||
"stable": "stable"
|
"stable": "stable"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1781446832,
|
"lastModified": 1762034856,
|
||||||
"narHash": "sha256-/6+NHQstxRqkbNjtNcQgCojzu59SEbdwwpWqBw7Fm9s=",
|
"narHash": "sha256-QVey3iP3UEoiFVXgypyjTvCrsIlA4ecx6Acaz5C8/PQ=",
|
||||||
"owner": "zhaofengli",
|
"owner": "zhaofengli",
|
||||||
"repo": "colmena",
|
"repo": "colmena",
|
||||||
"rev": "938cb5cdd9b25d9774b6168e5f03d0afd04d321f",
|
"rev": "349b035a5027f23d88eeb3bc41085d7ee29f18ed",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -107,11 +49,11 @@
|
|||||||
},
|
},
|
||||||
"crane": {
|
"crane": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1781825982,
|
"lastModified": 1771121070,
|
||||||
"narHash": "sha256-SlXKwIRIhrOSAcTjCB3ftPLzJWZStQIPS7J1FlZPnKk=",
|
"narHash": "sha256-aIlv7FRXF9q70DNJPI237dEDAznSKaXmL5lfK/Id/bI=",
|
||||||
"owner": "ipetkov",
|
"owner": "ipetkov",
|
||||||
"repo": "crane",
|
"repo": "crane",
|
||||||
"rev": "469fd08d0bcf6926321fa973c6777fbc87785dd7",
|
"rev": "a2812c19f1ed2e5ed5ce2ef7109798b575c180e1",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -145,11 +87,11 @@
|
|||||||
"dnote": {
|
"dnote": {
|
||||||
"flake": false,
|
"flake": false,
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1774496111,
|
"lastModified": 1765604939,
|
||||||
"narHash": "sha256-F7sP/QtZhfoNJX7LAZRmL/MjRByzJFNPUQBNpifSimQ=",
|
"narHash": "sha256-5+grN/dsqRRFzUkz6ksvuEhgi4lYq64Rd0fejqzz8/Y=",
|
||||||
"owner": "dnote",
|
"owner": "dnote",
|
||||||
"repo": "dnote",
|
"repo": "dnote",
|
||||||
"rev": "1610b41e18a814b7aa7f4f784ba283794150853f",
|
"rev": "9fa312e3fc6139788533ca6cd1ada8c16a10519c",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -195,11 +137,11 @@
|
|||||||
"nixpkgs-lib": "nixpkgs-lib"
|
"nixpkgs-lib": "nixpkgs-lib"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1778716662,
|
"lastModified": 1769996383,
|
||||||
"narHash": "sha256-m1Yf0wZ8j1OHjTc2UwHwyQRSnNeSgLJOd7q5Y45hzi4=",
|
"narHash": "sha256-AnYjnFWgS49RlqX7LrC4uA+sCCDBj0Ry/WOJ5XWAsa0=",
|
||||||
"owner": "hercules-ci",
|
"owner": "hercules-ci",
|
||||||
"repo": "flake-parts",
|
"repo": "flake-parts",
|
||||||
"rev": "f7c1a2d347e4c52d5fb8d10cb4d94b5884e546fb",
|
"rev": "57928607ea566b5db3ad13af0e57e921e6b12381",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -244,27 +186,6 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"flake-parts_4": {
|
|
||||||
"inputs": {
|
|
||||||
"nixpkgs-lib": [
|
|
||||||
"llm-agents",
|
|
||||||
"nixpkgs"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1778716662,
|
|
||||||
"narHash": "sha256-m1Yf0wZ8j1OHjTc2UwHwyQRSnNeSgLJOd7q5Y45hzi4=",
|
|
||||||
"owner": "hercules-ci",
|
|
||||||
"repo": "flake-parts",
|
|
||||||
"rev": "f7c1a2d347e4c52d5fb8d10cb4d94b5884e546fb",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "hercules-ci",
|
|
||||||
"repo": "flake-parts",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"flake-utils": {
|
"flake-utils": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1659877975,
|
"lastModified": 1659877975,
|
||||||
@@ -336,7 +257,7 @@
|
|||||||
},
|
},
|
||||||
"flake-utils_5": {
|
"flake-utils_5": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"systems": "systems_6"
|
"systems": "systems_5"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1731533236,
|
"lastModified": 1731533236,
|
||||||
@@ -465,11 +386,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1779506708,
|
"lastModified": 1770260404,
|
||||||
"narHash": "sha256-QOD/CNm196nCJRheux/URi4/HE66fthdOMqCJoPP1Y0=",
|
"narHash": "sha256-3iVX1+7YUIt23hBx1WZsUllhbmP2EnXrV8tCRbLxHc8=",
|
||||||
"owner": "nix-community",
|
"owner": "nix-community",
|
||||||
"repo": "home-manager",
|
"repo": "home-manager",
|
||||||
"rev": "3ee51fbdac8c8bdfe1e7e1fcaba6520a563f394f",
|
"rev": "0d782ee42c86b196acff08acfbf41bb7d13eed5b",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -519,18 +440,16 @@
|
|||||||
"lanzaboote": {
|
"lanzaboote": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"crane": "crane",
|
"crane": "crane",
|
||||||
"nixpkgs": [
|
"nixpkgs": "nixpkgs_3",
|
||||||
"nixpkgs"
|
|
||||||
],
|
|
||||||
"pre-commit": "pre-commit",
|
"pre-commit": "pre-commit",
|
||||||
"rust-overlay": "rust-overlay"
|
"rust-overlay": "rust-overlay"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1782160136,
|
"lastModified": 1771492583,
|
||||||
"narHash": "sha256-APwmLI4UuSTim0JO/kZ6FXDPiZVp3zIj0uGDF2UZ5kU=",
|
"narHash": "sha256-nQzvnU4BGu8dA6BsPPCqmVcab/3ebVmHtX3ZWbW3Hxc=",
|
||||||
"owner": "nix-community",
|
"owner": "nix-community",
|
||||||
"repo": "lanzaboote",
|
"repo": "lanzaboote",
|
||||||
"rev": "6650fb7c6b48177c8200e21ffac99a4adc72b08d",
|
"rev": "5e9380994665ef66c87ab8e22c913ff837174ce4",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -539,121 +458,6 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"llm-agents": {
|
|
||||||
"inputs": {
|
|
||||||
"blueprint": "blueprint",
|
|
||||||
"bun2nix": "bun2nix",
|
|
||||||
"flake-parts": "flake-parts_4",
|
|
||||||
"nixpkgs": [
|
|
||||||
"nixpkgs-unstable"
|
|
||||||
],
|
|
||||||
"systems": "systems_5",
|
|
||||||
"treefmt-nix": "treefmt-nix"
|
|
||||||
},
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1782366663,
|
|
||||||
"narHash": "sha256-hcxEl/eWQQaYkUqygP4ca8S7WlX9c6vqgvQCqtV3vuw=",
|
|
||||||
"owner": "numtide",
|
|
||||||
"repo": "llm-agents.nix",
|
|
||||||
"rev": "5f9d86061f538b3f5f3568583c001fb9c0705b71",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "numtide",
|
|
||||||
"repo": "llm-agents.nix",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"models-dev": {
|
|
||||||
"flake": false,
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1782405514,
|
|
||||||
"narHash": "sha256-E25yt4q3YruuxZSepv2hXdNX4FyGk60LnbxlcthJL18=",
|
|
||||||
"owner": "anomalyco",
|
|
||||||
"repo": "models.dev",
|
|
||||||
"rev": "7ceac334bbacc5ece7fbaea510ac5d7b850cfdf0",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "anomalyco",
|
|
||||||
"repo": "models.dev",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"niri-flake": {
|
|
||||||
"inputs": {
|
|
||||||
"niri-stable": "niri-stable",
|
|
||||||
"niri-unstable": "niri-unstable",
|
|
||||||
"nixpkgs": [
|
|
||||||
"nixpkgs-unstable"
|
|
||||||
],
|
|
||||||
"nixpkgs-stable": "nixpkgs-stable",
|
|
||||||
"xwayland-satellite-stable": "xwayland-satellite-stable",
|
|
||||||
"xwayland-satellite-unstable": "xwayland-satellite-unstable"
|
|
||||||
},
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1782333733,
|
|
||||||
"narHash": "sha256-QYrNYMNPKErRgNlxWwk0cjNKftnq6WzSs84pcZnUskM=",
|
|
||||||
"owner": "sodiboo",
|
|
||||||
"repo": "niri-flake",
|
|
||||||
"rev": "a6351044a3d69877d1c23be54971d18f8531c930",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "sodiboo",
|
|
||||||
"repo": "niri-flake",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"niri-stable": {
|
|
||||||
"flake": false,
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1756556321,
|
|
||||||
"narHash": "sha256-RLD89dfjN0RVO86C/Mot0T7aduCygPGaYbog566F0Qo=",
|
|
||||||
"owner": "YaLTeR",
|
|
||||||
"repo": "niri",
|
|
||||||
"rev": "01be0e65f4eb91a9cd624ac0b76aaeab765c7294",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "YaLTeR",
|
|
||||||
"ref": "v25.08",
|
|
||||||
"repo": "niri",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"niri-unstable": {
|
|
||||||
"flake": false,
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1781781064,
|
|
||||||
"narHash": "sha256-Ii/koEm/sRyg65qbAQWqEgboSEIhdH0EL4KglAc14p0=",
|
|
||||||
"owner": "YaLTeR",
|
|
||||||
"repo": "niri",
|
|
||||||
"rev": "49fc6117fd6c043adaa2ead316b82db5ed735d36",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "YaLTeR",
|
|
||||||
"repo": "niri",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"nix-flatpak": {
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1767983141,
|
|
||||||
"narHash": "sha256-7ZCulYUD9RmJIDULTRkGLSW1faMpDlPKcbWJLYHoXcs=",
|
|
||||||
"owner": "gmodena",
|
|
||||||
"repo": "nix-flatpak",
|
|
||||||
"rev": "440818969ac2cbd77bfe025e884d0aa528991374",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "gmodena",
|
|
||||||
"ref": "latest",
|
|
||||||
"repo": "nix-flatpak",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"nix-github-actions": {
|
"nix-github-actions": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"nixpkgs": [
|
"nixpkgs": [
|
||||||
@@ -676,17 +480,12 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"nixos-hardware": {
|
"nixos-hardware": {
|
||||||
"inputs": {
|
|
||||||
"nixpkgs": [
|
|
||||||
"nixpkgs"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1782379505,
|
"lastModified": 1771423359,
|
||||||
"narHash": "sha256-zPvPiU+a7pqtH47xrtZLNRABJKpOjfZQclDbcvNtH+I=",
|
"narHash": "sha256-yRKJ7gpVmXbX2ZcA8nFi6CMPkJXZGjie2unsiMzj3Ig=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixos-hardware",
|
"repo": "nixos-hardware",
|
||||||
"rev": "603d3afd1b6145bd66e97ae38a34d91c95df70cf",
|
"rev": "740a22363033e9f1bb6270fbfb5a9574067af15b",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -712,29 +511,13 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"nixpkgs-25_11": {
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1782847189,
|
|
||||||
"narHash": "sha256-twXPFqFsrrY5r28Zh7Homgcp2gUMBgQ6WDS98Q/3xFI=",
|
|
||||||
"owner": "NixOS",
|
|
||||||
"repo": "nixpkgs",
|
|
||||||
"rev": "b6018f87da91d19d0ab4cf979885689b469cdd41",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "NixOS",
|
|
||||||
"ref": "nixos-25.11",
|
|
||||||
"repo": "nixpkgs",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"nixpkgs-lib": {
|
"nixpkgs-lib": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1777168982,
|
"lastModified": 1769909678,
|
||||||
"narHash": "sha256-GOkGPcboWE9BmGCRMLX3worL4EMnsnG8MyKmXNeYuhQ=",
|
"narHash": "sha256-cBEymOf4/o3FD5AZnzC3J9hLbiZ+QDT/KDuyHXVJOpM=",
|
||||||
"owner": "nix-community",
|
"owner": "nix-community",
|
||||||
"repo": "nixpkgs.lib",
|
"repo": "nixpkgs.lib",
|
||||||
"rev": "f5901329dade4a6ea039af1433fb087bd9c1fe14",
|
"rev": "72716169fe93074c333e8d0173151350670b824c",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -767,29 +550,13 @@
|
|||||||
"url": "https://github.com/NixOS/nixpkgs/archive/072a6db25e947df2f31aab9eccd0ab75d5b2da11.tar.gz"
|
"url": "https://github.com/NixOS/nixpkgs/archive/072a6db25e947df2f31aab9eccd0ab75d5b2da11.tar.gz"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"nixpkgs-stable": {
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1782188297,
|
|
||||||
"narHash": "sha256-imdcA5fgbHK259bhHlyiiSr7bMY1AZ6onOWDdAcydc4=",
|
|
||||||
"owner": "NixOS",
|
|
||||||
"repo": "nixpkgs",
|
|
||||||
"rev": "9a1a7dbb18f0eb31c49b031babb8def7eab0af54",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "NixOS",
|
|
||||||
"ref": "nixos-25.11",
|
|
||||||
"repo": "nixpkgs",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"nixpkgs-unstable": {
|
"nixpkgs-unstable": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1781577229,
|
"lastModified": 1771369470,
|
||||||
"narHash": "sha256-lrp67w8AulE9Ks53n27I45ADSzbOCn4H+CNW1Ck8B+8=",
|
"narHash": "sha256-0NBlEBKkN3lufyvFegY4TYv5mCNHbi5OmBDrzihbBMQ=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "567a49d1913ce81ac6e9582e3553dd90a955875f",
|
"rev": "0182a361324364ae3f436a63005877674cf45efb",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -817,48 +584,33 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs_3": {
|
"nixpkgs_3": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1782233679,
|
"lastModified": 1771008912,
|
||||||
"narHash": "sha256-QyuGP5+QOtmXpy4i2X4DhBVBaySBdDKQEhqKcphcp34=",
|
"narHash": "sha256-gf2AmWVTs8lEq7z/3ZAsgnZDhWIckkb+ZnAo5RzSxJg=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "667d5cf1c59585031d743c78b394b0a647537c35",
|
"rev": "a82ccc39b39b621151d6732718e3e250109076fa",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"ref": "nixos-26.05",
|
"ref": "nixos-unstable",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"nixpkgs_4": {
|
"nixpkgs_4": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1781577229,
|
"lastModified": 1771419570,
|
||||||
"narHash": "sha256-rcUHdUtJEvMdNEl2Wq+YpHraHKfcer3KsBscpZEF2Yg=",
|
"narHash": "sha256-bxAlQgre3pcQcaRUm/8A0v/X8d2nhfraWSFqVmMcBcU=",
|
||||||
"rev": "567a49d1913ce81ac6e9582e3553dd90a955875f",
|
"owner": "NixOS",
|
||||||
"type": "tarball",
|
"repo": "nixpkgs",
|
||||||
"url": "https://releases.nixos.org/nixos/unstable/nixos-26.11pre1017464.567a49d1913c/nixexprs.tar.xz"
|
"rev": "6d41bc27aaf7b6a3ba6b169db3bd5d6159cfaa47",
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"type": "tarball",
|
|
||||||
"url": "https://channels.nixos.org/nixos-unstable/nixexprs.tar.xz"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"noctalia": {
|
|
||||||
"inputs": {
|
|
||||||
"nixpkgs": "nixpkgs_4"
|
|
||||||
},
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1782405004,
|
|
||||||
"narHash": "sha256-7uAqPz5SmLBoIETAvZPeHu1e22suVEEcNWIZKoen2/8=",
|
|
||||||
"owner": "noctalia-dev",
|
|
||||||
"repo": "noctalia",
|
|
||||||
"rev": "6779a947a14738bf81c0d66ee083acff105f66a2",
|
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"owner": "noctalia-dev",
|
"owner": "NixOS",
|
||||||
"repo": "noctalia",
|
"ref": "nixos-25.11",
|
||||||
|
"repo": "nixpkgs",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -891,11 +643,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1781733627,
|
"lastModified": 1770726378,
|
||||||
"narHash": "sha256-U3yTuGBnmXvXoQI3qkpfEDsn9RovQPAjN7ndRco+3u0=",
|
"narHash": "sha256-kck+vIbGOaM/dHea7aTBxdFYpeUl/jHOy5W3eyRvVx8=",
|
||||||
"owner": "cachix",
|
"owner": "cachix",
|
||||||
"repo": "pre-commit-hooks.nix",
|
"repo": "pre-commit-hooks.nix",
|
||||||
"rev": "3bbec39bc90eadfa031e6f3b77272f3f60803e39",
|
"rev": "5eaaedde414f6eb1aea8b8525c466dc37bba95ae",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -916,20 +668,13 @@
|
|||||||
"ketchup": "ketchup",
|
"ketchup": "ketchup",
|
||||||
"ksoloti-pr": "ksoloti-pr",
|
"ksoloti-pr": "ksoloti-pr",
|
||||||
"lanzaboote": "lanzaboote",
|
"lanzaboote": "lanzaboote",
|
||||||
"llm-agents": "llm-agents",
|
|
||||||
"models-dev": "models-dev",
|
|
||||||
"niri-flake": "niri-flake",
|
|
||||||
"nix-flatpak": "nix-flatpak",
|
|
||||||
"nixos-hardware": "nixos-hardware",
|
"nixos-hardware": "nixos-hardware",
|
||||||
"nixpkgs": "nixpkgs_3",
|
"nixpkgs": "nixpkgs_4",
|
||||||
"nixpkgs-25_11": "nixpkgs-25_11",
|
|
||||||
"nixpkgs-unstable": "nixpkgs-unstable",
|
"nixpkgs-unstable": "nixpkgs-unstable",
|
||||||
"noctalia": "noctalia",
|
|
||||||
"syntax-renderer": "syntax-renderer",
|
"syntax-renderer": "syntax-renderer",
|
||||||
"tree_hugger": "tree_hugger",
|
"tonearm": "tonearm",
|
||||||
"turny": "turny",
|
"turny": "turny",
|
||||||
"voidauth": "voidauth",
|
"voidauth": "voidauth",
|
||||||
"voxtype": "voxtype",
|
|
||||||
"workout-sync": "workout-sync"
|
"workout-sync": "workout-sync"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -941,11 +686,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1782012058,
|
"lastModified": 1771125043,
|
||||||
"narHash": "sha256-9mWUnReOUXfKjZuJAL/bAFH3LUyECTRtXgSNVjRw3UY=",
|
"narHash": "sha256-ldf/s49n6rOAxl7pYLJGGS1N/assoHkCOWdEdLyNZkc=",
|
||||||
"owner": "oxalica",
|
"owner": "oxalica",
|
||||||
"repo": "rust-overlay",
|
"repo": "rust-overlay",
|
||||||
"rev": "8534567325bd8a8d2928e6afd81e0a87d19efd3c",
|
"rev": "4912f951a26dc8142b176be2c2ad834319dc06e8",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -1061,60 +806,23 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"systems_6": {
|
"tonearm": {
|
||||||
"locked": {
|
|
||||||
"lastModified": 1681028828,
|
|
||||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
|
||||||
"owner": "nix-systems",
|
|
||||||
"repo": "default",
|
|
||||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "nix-systems",
|
|
||||||
"repo": "default",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"tree_hugger": {
|
|
||||||
"inputs": {
|
"inputs": {
|
||||||
|
"flake-utils": "flake-utils_5",
|
||||||
"nixpkgs": [
|
"nixpkgs": [
|
||||||
"nixpkgs-unstable"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1774531531,
|
|
||||||
"narHash": "sha256-v4zPHfGQw79uyOUeqWAlOE0A7ZjQVYhpfyibnsUe1k4=",
|
|
||||||
"ref": "refs/heads/main",
|
|
||||||
"rev": "af16fcdcfcfc17f2be03879b7d8e858976936619",
|
|
||||||
"revCount": 67,
|
|
||||||
"type": "git",
|
|
||||||
"url": "ssh://git@github.com/everii-Group/tree_hugger"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "ssh://git@github.com/everii-Group/tree_hugger"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"treefmt-nix": {
|
|
||||||
"inputs": {
|
|
||||||
"nixpkgs": [
|
|
||||||
"llm-agents",
|
|
||||||
"nixpkgs"
|
"nixpkgs"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1780220602,
|
"lastModified": 1769118914,
|
||||||
"narHash": "sha256-eynAfOmbmxJnkp7YewvCEbShNnnYJ9gLLqkzsYtBPeM=",
|
"narHash": "sha256-ASM7MVDWxT+BBtcAJ1z8XNjyNdL5E1Kc0miCWIepf+o=",
|
||||||
"owner": "numtide",
|
"rev": "68ea2190d1b37f9030b7586d2030f9dc48915a85",
|
||||||
"repo": "treefmt-nix",
|
"type": "tarball",
|
||||||
"rev": "db947814a175b7ca6ded66e21383d938df01c227",
|
"url": "https://codeberg.org/api/v1/repos/dergs/Tonearm/archive/68ea2190d1b37f9030b7586d2030f9dc48915a85.tar.gz"
|
||||||
"type": "github"
|
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"owner": "numtide",
|
"type": "tarball",
|
||||||
"repo": "treefmt-nix",
|
"url": "https://codeberg.org/dergs/Tonearm/archive/v1.0.0.tar.gz"
|
||||||
"type": "github"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"turny": {
|
"turny": {
|
||||||
@@ -1127,11 +835,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1783250457,
|
"lastModified": 1752874301,
|
||||||
"narHash": "sha256-J7hZuGi7+SJRFeSpWJzzQy9IBH/hU8KokPV5A3rr65c=",
|
"narHash": "sha256-A6IZz46Lfopm5UhMtFfBDimepEUt9lGwhWoEIEQHsgk=",
|
||||||
"owner": "~repomaa",
|
"owner": "~repomaa",
|
||||||
"repo": "turny",
|
"repo": "turny",
|
||||||
"rev": "4b711b8814c91bc053729c5903d1ee924b8ccb8d",
|
"rev": "133c05151e77616c7973c1c1038506b2fdee8eab",
|
||||||
"type": "sourcehut"
|
"type": "sourcehut"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -1164,27 +872,6 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"voxtype": {
|
|
||||||
"inputs": {
|
|
||||||
"flake-utils": "flake-utils_5",
|
|
||||||
"nixpkgs": [
|
|
||||||
"nixpkgs-unstable"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1782311621,
|
|
||||||
"narHash": "sha256-K8mog5N+Qv01VyM5n74sOeXCn+2OYgrSZnVDAKQ33zc=",
|
|
||||||
"owner": "peteonrails",
|
|
||||||
"repo": "voxtype",
|
|
||||||
"rev": "31b7f38c4e22c4ce75d6350945729e5db001cb9a",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "peteonrails",
|
|
||||||
"repo": "voxtype",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"workout-sync": {
|
"workout-sync": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"nixpkgs": [
|
"nixpkgs": [
|
||||||
@@ -1204,39 +891,6 @@
|
|||||||
"repo": "workout-sync",
|
"repo": "workout-sync",
|
||||||
"type": "sourcehut"
|
"type": "sourcehut"
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"xwayland-satellite-stable": {
|
|
||||||
"flake": false,
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1755491097,
|
|
||||||
"narHash": "sha256-m+9tUfsmBeF2Gn4HWa6vSITZ4Gz1eA1F5Kh62B0N4oE=",
|
|
||||||
"owner": "Supreeeme",
|
|
||||||
"repo": "xwayland-satellite",
|
|
||||||
"rev": "388d291e82ffbc73be18169d39470f340707edaa",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "Supreeeme",
|
|
||||||
"ref": "v0.7",
|
|
||||||
"repo": "xwayland-satellite",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"xwayland-satellite-unstable": {
|
|
||||||
"flake": false,
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1781226823,
|
|
||||||
"narHash": "sha256-28696iIw8uE0ZUyFTtzhEM8xMh85clCYypMxkvUi+sc=",
|
|
||||||
"owner": "Supreeeme",
|
|
||||||
"repo": "xwayland-satellite",
|
|
||||||
"rev": "8575d0ef55d70f9b4c46b6bffb3accf912217e1e",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "Supreeeme",
|
|
||||||
"repo": "xwayland-satellite",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"root": "root",
|
"root": "root",
|
||||||
|
|||||||
@@ -2,20 +2,13 @@
|
|||||||
inputs = {
|
inputs = {
|
||||||
flake-parts.url = "github:hercules-ci/flake-parts";
|
flake-parts.url = "github:hercules-ci/flake-parts";
|
||||||
nixpkgs-unstable.url = "github:NixOS/nixpkgs/nixos-unstable";
|
nixpkgs-unstable.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-26.05";
|
nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.11";
|
||||||
nixpkgs-25_11.url = "github:NixOS/nixpkgs/nixos-25.11";
|
|
||||||
gtrackmap = {
|
gtrackmap = {
|
||||||
url = "github:gtrackmap/gtrackmap";
|
url = "github:gtrackmap/gtrackmap";
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
};
|
};
|
||||||
lanzaboote = {
|
lanzaboote.url = "github:nix-community/lanzaboote";
|
||||||
url = "github:nix-community/lanzaboote";
|
nixos-hardware.url = "github:NixOS/nixos-hardware/master";
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
|
||||||
};
|
|
||||||
nixos-hardware = {
|
|
||||||
url = "github:NixOS/nixos-hardware/master";
|
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
|
||||||
};
|
|
||||||
home-manager = {
|
home-manager = {
|
||||||
url = "github:nix-community/home-manager/release-25.11";
|
url = "github:nix-community/home-manager/release-25.11";
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
@@ -54,33 +47,15 @@
|
|||||||
inputs.flake-parts.follows = "flake-parts";
|
inputs.flake-parts.follows = "flake-parts";
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
};
|
};
|
||||||
|
tonearm = {
|
||||||
|
url = "https://codeberg.org/dergs/Tonearm/archive/v1.0.0.tar.gz";
|
||||||
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
|
};
|
||||||
voidauth = {
|
voidauth = {
|
||||||
url = "github:repomaa/voidauth/feat/nix-packaging";
|
url = "github:repomaa/voidauth/feat/nix-packaging";
|
||||||
inputs.nixpkgs.follows = "nixpkgs-unstable";
|
inputs.nixpkgs.follows = "nixpkgs-unstable";
|
||||||
inputs.flake-parts.follows = "flake-parts";
|
inputs.flake-parts.follows = "flake-parts";
|
||||||
};
|
};
|
||||||
voxtype = {
|
|
||||||
url = "github:peteonrails/voxtype";
|
|
||||||
inputs.nixpkgs.follows = "nixpkgs-unstable";
|
|
||||||
};
|
|
||||||
tree_hugger = {
|
|
||||||
url = "git+ssh://git@github.com/everii-Group/tree_hugger";
|
|
||||||
inputs.nixpkgs.follows = "nixpkgs-unstable";
|
|
||||||
};
|
|
||||||
models-dev = {
|
|
||||||
url = "github:anomalyco/models.dev";
|
|
||||||
flake = false;
|
|
||||||
};
|
|
||||||
noctalia.url = "github:noctalia-dev/noctalia";
|
|
||||||
niri-flake = {
|
|
||||||
url = "github:sodiboo/niri-flake";
|
|
||||||
inputs.nixpkgs.follows = "nixpkgs-unstable";
|
|
||||||
};
|
|
||||||
nix-flatpak.url = "github:gmodena/nix-flatpak/?ref=latest";
|
|
||||||
llm-agents = {
|
|
||||||
url = "github:numtide/llm-agents.nix";
|
|
||||||
inputs.nixpkgs.follows = "nixpkgs-unstable";
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
outputs =
|
outputs =
|
||||||
{
|
{
|
||||||
@@ -115,6 +90,7 @@
|
|||||||
packages = with pkgs; [
|
packages = with pkgs; [
|
||||||
agenix.packages.${system}.default
|
agenix.packages.${system}.default
|
||||||
colmena.packages.${system}.colmena
|
colmena.packages.${system}.colmena
|
||||||
|
colmena.packages.${system}.manual
|
||||||
(writeShellScriptBin "build" ''
|
(writeShellScriptBin "build" ''
|
||||||
colmena build --keep-result "$@"
|
colmena build --keep-result "$@"
|
||||||
'')
|
'')
|
||||||
@@ -124,49 +100,6 @@
|
|||||||
(writeShellScriptBin "apply-local" ''
|
(writeShellScriptBin "apply-local" ''
|
||||||
nh os switch .
|
nh os switch .
|
||||||
'')
|
'')
|
||||||
(writeShellScriptBin "build-sd" ''
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
echo "Building SD image for turny..."
|
|
||||||
nix build .#images.turny --system aarch64-linux --print-build-logs
|
|
||||||
|
|
||||||
STORE_IMG=$(readlink -f result/sd-image/*.img)
|
|
||||||
if [ -z "$STORE_IMG" ]; then
|
|
||||||
echo "Error: No .img file found in result/sd-image/"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
KEY_DIR="$(git rev-parse --show-toplevel)/hosts/turny"
|
|
||||||
if [ ! -f "$KEY_DIR/ssh_host_ed25519_key" ]; then
|
|
||||||
echo "Error: SSH host key not found at $KEY_DIR/ssh_host_ed25519_key"
|
|
||||||
echo "Generate it with: ssh-keygen -t ed25519 -f hosts/turny/ssh_host_ed25519_key -N \"\" -C turny"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
IMG="/tmp/turny-sd-image.img"
|
|
||||||
echo "Copying image to writable location: $IMG"
|
|
||||||
cp "$STORE_IMG" "$IMG"
|
|
||||||
|
|
||||||
echo "Injecting SSH host key into image..."
|
|
||||||
LOOP=$(sudo losetup --show -P -f "$IMG")
|
|
||||||
MNT=$(sudo mktemp -d)
|
|
||||||
cleanup() {
|
|
||||||
sudo umount "$MNT" 2>/dev/null || true
|
|
||||||
sudo rmdir "$MNT" 2>/dev/null || true
|
|
||||||
sudo losetup -d "$LOOP" 2>/dev/null || true
|
|
||||||
}
|
|
||||||
trap cleanup EXIT
|
|
||||||
|
|
||||||
sudo mount "''${LOOP}p2" "$MNT"
|
|
||||||
sudo mkdir -p "$MNT/etc/ssh"
|
|
||||||
sudo cp "$KEY_DIR/ssh_host_ed25519_key" "$MNT/etc/ssh/ssh_host_ed25519_key"
|
|
||||||
sudo cp "$KEY_DIR/ssh_host_ed25519_key.pub" "$MNT/etc/ssh/ssh_host_ed25519_key.pub"
|
|
||||||
sudo chmod 600 "$MNT/etc/ssh/ssh_host_ed25519_key"
|
|
||||||
sudo chmod 644 "$MNT/etc/ssh/ssh_host_ed25519_key.pub"
|
|
||||||
|
|
||||||
echo "Done! Image ready at: $IMG"
|
|
||||||
echo "Flash with: sudo dd if=$IMG of=/dev/sdX bs=4M conv=fsync status=progress"
|
|
||||||
'')
|
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -277,10 +210,4 @@
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
nixConfig = {
|
|
||||||
extra-substituters = [ "https://noctalia.cachix.org" ];
|
|
||||||
extra-trusted-public-keys = [
|
|
||||||
"noctalia.cachix.org-1:pCOR47nnMEo5thcxNDtzWpOxNFQsBRglJzxWPp3dkU4="
|
|
||||||
];
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|||||||
+36
-673
@@ -7,31 +7,14 @@
|
|||||||
self,
|
self,
|
||||||
...
|
...
|
||||||
}:
|
}:
|
||||||
let
|
|
||||||
noctalia-package = inputs.noctalia.packages.${pkgs.stdenv.hostPlatform.system}.default;
|
|
||||||
ns = lib.getExe noctalia-package;
|
|
||||||
ns-ipc =
|
|
||||||
args:
|
|
||||||
[
|
|
||||||
ns
|
|
||||||
"msg"
|
|
||||||
]
|
|
||||||
++ args;
|
|
||||||
in
|
|
||||||
{
|
{
|
||||||
imports = [
|
imports = [
|
||||||
../gnome
|
../gnome
|
||||||
./dnote.nix
|
./dnote.nix
|
||||||
../modules/zed
|
../modules/zed
|
||||||
../modules/voxtype
|
|
||||||
../modules/elephant
|
|
||||||
./secrets.nix
|
./secrets.nix
|
||||||
inputs.hastebin.nixosModules.hm
|
inputs.hastebin.nixosModules.hm
|
||||||
inputs.agenix.homeManagerModules.default
|
inputs.agenix.homeManagerModules.default
|
||||||
inputs.voxtype.homeManagerModules.default
|
|
||||||
inputs.noctalia.homeModules.default
|
|
||||||
inputs.niri-flake.homeModules.niri
|
|
||||||
inputs.nix-flatpak.homeManagerModules.nix-flatpak
|
|
||||||
];
|
];
|
||||||
|
|
||||||
# This value determines the Home Manager release that your configuration is
|
# This value determines the Home Manager release that your configuration is
|
||||||
@@ -43,11 +26,7 @@ in
|
|||||||
# release notes.
|
# release notes.
|
||||||
home.stateVersion = "23.11"; # Please read the comment before changing.
|
home.stateVersion = "23.11"; # Please read the comment before changing.
|
||||||
|
|
||||||
home.packages =
|
home.packages = with pkgs; [
|
||||||
with pkgs;
|
|
||||||
[
|
|
||||||
atool
|
|
||||||
unzip
|
|
||||||
htop
|
htop
|
||||||
gnupg
|
gnupg
|
||||||
pkgs-unstable.yubioath-flutter
|
pkgs-unstable.yubioath-flutter
|
||||||
@@ -60,16 +39,14 @@ in
|
|||||||
source-sans-pro
|
source-sans-pro
|
||||||
mosh
|
mosh
|
||||||
docker-compose
|
docker-compose
|
||||||
(signal-desktop.override {
|
signal-desktop
|
||||||
commandLineArgs = "--password-store=gnome-libsecret";
|
|
||||||
})
|
|
||||||
cargo
|
cargo
|
||||||
blanket
|
blanket
|
||||||
wl-clipboard
|
wl-clipboard
|
||||||
gnumeric
|
gnumeric
|
||||||
gh
|
gh
|
||||||
neovim-remote
|
neovim-remote
|
||||||
gradia
|
pkgs-unstable.gradia
|
||||||
crystal
|
crystal
|
||||||
shards
|
shards
|
||||||
moreutils
|
moreutils
|
||||||
@@ -95,132 +72,31 @@ in
|
|||||||
protonmail-bridge
|
protonmail-bridge
|
||||||
hydroxide
|
hydroxide
|
||||||
imapsync
|
imapsync
|
||||||
nixfmt
|
nixfmt-rfc-style
|
||||||
tree
|
tree
|
||||||
virt-manager
|
virt-manager
|
||||||
pkgs-unstable.ncspot
|
pkgs-unstable.ncspot
|
||||||
pkgs-unstable.devbox
|
pkgs-unstable.devbox
|
||||||
pkgs-unstable.feishin
|
pkgs-unstable.feishin
|
||||||
openscad
|
openscad
|
||||||
|
pkgs-unstable.walker
|
||||||
pkgs-unstable.shairport-sync-airplay2
|
pkgs-unstable.shairport-sync-airplay2
|
||||||
(writeShellScriptBin "pw" ''
|
(writeShellScriptBin "pw" ''
|
||||||
${lib.getExe rbw} ls --fields 'id,folder,name' | \
|
${lib.getExe rbw} ls --fields 'id,folder,name' | \
|
||||||
${lib.getExe gawk} -F '\t' '{print $1 "\t" ($2 == "" ? "" : $2 "/") $3}' | \
|
${lib.getExe gawk} -F '\t' '{print $1 "\t" ($2 == "" ? "" : $2 "/") $3}' | \
|
||||||
${lib.getExe config.services.walker.package} -d -l 2 | \
|
${lib.getExe pkgs-unstable.walker} -d -l 2 | \
|
||||||
xargs ${lib.getExe rbw} get "$@" | ${lib.getExe' wl-clipboard "wl-copy"}
|
xargs ${lib.getExe rbw} get "$@" | ${lib.getExe' wl-clipboard "wl-copy"}
|
||||||
'')
|
'')
|
||||||
(google-fonts.override { fonts = [ "Tajawal" ]; })
|
(google-fonts.override { fonts = [ "Tajawal" ]; })
|
||||||
|
pkgs-unstable.opencode
|
||||||
(writeShellScriptBin "nr" ''
|
(writeShellScriptBin "nr" ''
|
||||||
${lib.getExe nix} run "nixpkgs#''${1}" "$@"
|
${lib.getExe nix} run "nixpkgs#''${1}" "$@"
|
||||||
'')
|
'')
|
||||||
blueman
|
pkgs-unstable.tidal-hifi
|
||||||
pavucontrol
|
inputs.tonearm.packages.${pkgs.stdenv.hostPlatform.system}.tonearm
|
||||||
(writeShellScriptBin "voxtoggle" ''
|
];
|
||||||
status=$(${lib.getExe config.programs.voxtype.package} status)
|
|
||||||
pid=$(cat ''${XDG_RUNTIME_DIR}/voxtype/pid)
|
|
||||||
|
|
||||||
if [[ "$status" == "stopped" ]]; then
|
|
||||||
exit 1
|
|
||||||
elif [[ "$status" == "recording" ]]; then
|
|
||||||
kill -SIGUSR2 "$pid"
|
|
||||||
else
|
|
||||||
kill -SIGUSR1 "$pid"
|
|
||||||
fi
|
|
||||||
'')
|
|
||||||
(writeShellScriptBin "scrot" ''
|
|
||||||
set -eo pipefail
|
|
||||||
|
|
||||||
${lib.getExe grim} -t ppm -g "$(${lib.getExe slurp} -o -d -F monospace)" - | ${lib.getExe gradia}
|
|
||||||
'')
|
|
||||||
inputs.tree_hugger.packages.${stdenv.hostPlatform.system}.default
|
|
||||||
(writeShellScriptBin "ns" "${lib.join " " (ns-ipc [ ''"$@"'' ])}")
|
|
||||||
libnotify
|
|
||||||
supersonic-wayland
|
|
||||||
]
|
|
||||||
++ (with inputs.llm-agents.packages.${pkgs.stdenv.hostPlatform.system}; [
|
|
||||||
pi
|
|
||||||
crush
|
|
||||||
opencode
|
|
||||||
]);
|
|
||||||
|
|
||||||
programs = {
|
programs = {
|
||||||
noctalia = {
|
|
||||||
enable = true;
|
|
||||||
settings = {
|
|
||||||
bar.main = {
|
|
||||||
position = "top";
|
|
||||||
auto_hide = true;
|
|
||||||
reserve_space = false;
|
|
||||||
margin_edge = 5;
|
|
||||||
capsule = false;
|
|
||||||
start = [ "workspaces" ];
|
|
||||||
center = [ "clock" ];
|
|
||||||
end = [
|
|
||||||
"battery"
|
|
||||||
"notifications"
|
|
||||||
"network"
|
|
||||||
"bluetooth"
|
|
||||||
"control-center"
|
|
||||||
];
|
|
||||||
};
|
|
||||||
widget = {
|
|
||||||
workspaces = {
|
|
||||||
labels_only_when_occupied = true;
|
|
||||||
display = "none";
|
|
||||||
};
|
|
||||||
clock = {
|
|
||||||
format = "{:%H:%M}";
|
|
||||||
vertical_format = "{:%H\n%M}";
|
|
||||||
tooltip_format = "{:%A, %d. %B %Y}";
|
|
||||||
};
|
|
||||||
battery.warning_threshold = 20;
|
|
||||||
control-center.glyph = "snowflake";
|
|
||||||
};
|
|
||||||
|
|
||||||
audio.visualizerType = "linear";
|
|
||||||
|
|
||||||
wallpaper = {
|
|
||||||
directory = "~/Pictures/Wallpapers";
|
|
||||||
automation.enabled = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
backdrop.enabled = true;
|
|
||||||
location.auto_locate = true;
|
|
||||||
|
|
||||||
control_center.shortcuts = [
|
|
||||||
{ type = "power_profile"; }
|
|
||||||
{ type = "caffeine"; }
|
|
||||||
{ type = "nightlight"; }
|
|
||||||
{ type = "dark_mode"; }
|
|
||||||
];
|
|
||||||
|
|
||||||
calendar = {
|
|
||||||
enabled = true;
|
|
||||||
account = {
|
|
||||||
mailbox = {
|
|
||||||
type = "caldav";
|
|
||||||
name = "Yhteinen Kalenteri";
|
|
||||||
provider = "custom";
|
|
||||||
server_url = "https://dav.mailbox.org/caldav/Y2FsOi8vMC82NA";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
nightlight.enabled = true;
|
|
||||||
idle.enabled = true;
|
|
||||||
shell = {
|
|
||||||
avatar_path = "~/.face";
|
|
||||||
};
|
|
||||||
theme = {
|
|
||||||
mode = "auto";
|
|
||||||
source = "wallpaper";
|
|
||||||
wallpaper_scheme = "m3-tonal-spot";
|
|
||||||
};
|
|
||||||
hooks.theme_mode_changed = "gsettings set org.gnome.desktop.interface color-scheme prefer-\${NOCTALIA_THEME_MODE}";
|
|
||||||
};
|
|
||||||
package = noctalia-package;
|
|
||||||
};
|
|
||||||
|
|
||||||
nh = {
|
nh = {
|
||||||
enable = true;
|
enable = true;
|
||||||
flake = self;
|
flake = self;
|
||||||
@@ -425,11 +301,6 @@ in
|
|||||||
gh pr checkout {{.Form.pr | quote}}
|
gh pr checkout {{.Form.pr | quote}}
|
||||||
'';
|
'';
|
||||||
}
|
}
|
||||||
{
|
|
||||||
key = "D";
|
|
||||||
context = "localBranches";
|
|
||||||
command = "th prune {{.SelectedLocalBranch.Name}}";
|
|
||||||
}
|
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -454,6 +325,7 @@ in
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
"apu" = {
|
"apu" = {
|
||||||
|
hostname = "apu.tempel-vibes.ts.net";
|
||||||
user = "root";
|
user = "root";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -555,50 +427,6 @@ in
|
|||||||
enable = true;
|
enable = true;
|
||||||
defaultEditor = true;
|
defaultEditor = true;
|
||||||
};
|
};
|
||||||
voxtype = {
|
|
||||||
enable = true;
|
|
||||||
package = inputs.voxtype.packages.${pkgs.stdenv.hostPlatform.system}.vulkan;
|
|
||||||
model.name = "large-v3-turbo";
|
|
||||||
service.enable = true;
|
|
||||||
settings = {
|
|
||||||
hotkey.enabled = false;
|
|
||||||
whisper.language = "auto";
|
|
||||||
output.notification = {
|
|
||||||
on_recording_start = false;
|
|
||||||
on_recording_stop = false;
|
|
||||||
on_transcription = false;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
postProcessing = {
|
|
||||||
enable = true;
|
|
||||||
settings = {
|
|
||||||
model = "qwen3:4b-instruct";
|
|
||||||
commonInstructions = "no quotes, no emojis, no explanations";
|
|
||||||
prompts = [
|
|
||||||
{
|
|
||||||
title = "Clean up";
|
|
||||||
instructions = "First identify what language this is, then respond in that language. Clean up: remove filler words, fix grammar and punctuation. Preserve all nuances of the original dictation. Output ONLY the cleaned text";
|
|
||||||
}
|
|
||||||
{
|
|
||||||
title = "Make a title";
|
|
||||||
instructions = "First identify what language this is, then respond in that language. Create a concise and descriptive title";
|
|
||||||
}
|
|
||||||
{
|
|
||||||
title = "Summarize";
|
|
||||||
instructions = "First identify what language this is, then respond in that language. Summarize in a few sentences";
|
|
||||||
}
|
|
||||||
{
|
|
||||||
title = "Commit message";
|
|
||||||
instructions = "Write a concise and descriptive git commit message for this dictation in English";
|
|
||||||
}
|
|
||||||
{
|
|
||||||
title = "Translate to English";
|
|
||||||
instructions = "Translate this dictation. Remove filler words, fix grammar and punctuation. Output ONLY the translation";
|
|
||||||
}
|
|
||||||
];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
gnome = {
|
gnome = {
|
||||||
@@ -746,20 +574,15 @@ in
|
|||||||
#
|
#
|
||||||
# /etc/profiles/per-user/jokke/etc/profile.d/hm-session-vars.sh
|
# /etc/profiles/per-user/jokke/etc/profile.d/hm-session-vars.sh
|
||||||
#
|
#
|
||||||
|
systemd.user.sessionVariables = {
|
||||||
home.sessionVariables = {
|
|
||||||
NIXOS_OZONE_WL = 1;
|
NIXOS_OZONE_WL = 1;
|
||||||
NVIM_LISTEN_ADDRESS = "$XDG_RUNTIME_DIR/nvimsocket";
|
NVIM_LISTEN_ADDRESS = "$XDG_RUNTIME_DIR/nvimsocket";
|
||||||
PAGER = "bat --paging=always --style=plain";
|
PAGER = "bat --paging=always --style=plain";
|
||||||
MANPAGER = "sh -c 'col -bx | bat -l man -p'";
|
MANPAGER = "sh -c 'col -bx | bat -l man -p'";
|
||||||
MANROFFOPT = "-c";
|
MANROFFOPT = "-c";
|
||||||
DO_NOT_TRACK = 1;
|
DO_NOT_TRACK = 1;
|
||||||
DOCKER_HOST = "unix://$XDG_RUNTIME_DIR/podman/podman.sock";
|
|
||||||
};
|
};
|
||||||
|
|
||||||
systemd.user.sessionVariables = lib.mapAttrs (_: v: toString v) config.home.sessionVariables;
|
|
||||||
programs.zsh.sessionVariables = config.home.sessionVariables;
|
|
||||||
|
|
||||||
home.shellAliases = {
|
home.shellAliases = {
|
||||||
_ = "sudo";
|
_ = "sudo";
|
||||||
icr = "crystal i";
|
icr = "crystal i";
|
||||||
@@ -770,52 +593,11 @@ in
|
|||||||
ls = "ls --color=auto";
|
ls = "ls --color=auto";
|
||||||
};
|
};
|
||||||
|
|
||||||
services = {
|
services.gpg-agent = with pkgs; {
|
||||||
gpg-agent = with pkgs; {
|
|
||||||
enable = true;
|
enable = true;
|
||||||
enableSshSupport = true;
|
enableSshSupport = true;
|
||||||
pinentry.package = pinentry-gnome3;
|
pinentry.package = pinentry-gnome3;
|
||||||
};
|
};
|
||||||
};
|
|
||||||
|
|
||||||
services.flatpak = {
|
|
||||||
update.onActivation = true;
|
|
||||||
|
|
||||||
packages = [
|
|
||||||
"dev.diegovsky.Riff"
|
|
||||||
"app.zen_browser.zen"
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
services.walker = {
|
|
||||||
enable = true;
|
|
||||||
package = pkgs.symlinkJoin {
|
|
||||||
inherit (pkgs-unstable.walker) name meta;
|
|
||||||
paths = [ pkgs-unstable.walker ];
|
|
||||||
nativeBuildInputs = [ pkgs.makeBinaryWrapper ];
|
|
||||||
postBuild = ''
|
|
||||||
wrapProgram $out/bin/walker \
|
|
||||||
--prefix PATH : ${lib.makeBinPath [ config.services.elephant.package ]}
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
systemd = {
|
|
||||||
enable = true;
|
|
||||||
};
|
|
||||||
settings = {
|
|
||||||
providers = {
|
|
||||||
default = [
|
|
||||||
"desktopapplications"
|
|
||||||
"calc"
|
|
||||||
"websearch"
|
|
||||||
"bitwarden"
|
|
||||||
];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
services.elephant.enable = true;
|
|
||||||
|
|
||||||
systemd.user.services.walker.Install.WantedBy = lib.mkForce [ "graphical-session.target" ];
|
|
||||||
|
|
||||||
systemd.user.services.shairport-sync = {
|
systemd.user.services.shairport-sync = {
|
||||||
Unit = {
|
Unit = {
|
||||||
@@ -844,12 +626,11 @@ in
|
|||||||
|
|
||||||
xdg.configFile."opencode/opencode.jsonc".text = builtins.toJSON {
|
xdg.configFile."opencode/opencode.jsonc".text = builtins.toJSON {
|
||||||
"$schema" = "https://opencode.ai/config.json";
|
"$schema" = "https://opencode.ai/config.json";
|
||||||
model = "fireworks-ai/accounts/fireworks/models/glm-5p2";
|
model = "opencode/kimi-k2.5";
|
||||||
small_model = "fireworks-ai/accounts/fireworks/models/minimax-m3";
|
small_model = "opencode/minimax-m2.1";
|
||||||
agent = {
|
agent = {
|
||||||
explore.model = "fireworks-ai/accounts/fireworks/models/minimax-m3";
|
explore.model = "opencode/minimax-m2.1";
|
||||||
};
|
};
|
||||||
theme = "system";
|
|
||||||
lsp = {
|
lsp = {
|
||||||
ruby-lsp = {
|
ruby-lsp = {
|
||||||
initialization = {
|
initialization = {
|
||||||
@@ -884,6 +665,7 @@ in
|
|||||||
"bundle"
|
"bundle"
|
||||||
"exec"
|
"exec"
|
||||||
"ruby-lsp"
|
"ruby-lsp"
|
||||||
|
"--lsp"
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
rubocop = {
|
rubocop = {
|
||||||
@@ -899,20 +681,27 @@ in
|
|||||||
".haml"
|
".haml"
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
nil = {
|
|
||||||
command = [
|
|
||||||
(lib.getExe pkgs.nil)
|
|
||||||
];
|
|
||||||
extensions = [ ".nix" ];
|
|
||||||
};
|
|
||||||
ameba-ls = {
|
|
||||||
command = [
|
|
||||||
(lib.getExe pkgs-unstable.ameba-ls)
|
|
||||||
];
|
|
||||||
extensions = [ ".cr" ];
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
mcp = {
|
mcp = {
|
||||||
|
memory = {
|
||||||
|
type = "local";
|
||||||
|
command = [
|
||||||
|
"npx"
|
||||||
|
"-y"
|
||||||
|
"@modelcontextprotocol/server-memory"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
browser = {
|
||||||
|
type = "local";
|
||||||
|
command = [
|
||||||
|
"npx"
|
||||||
|
"-y"
|
||||||
|
"@agent-infra/mcp-server-browser"
|
||||||
|
"--headless"
|
||||||
|
"--executable-path"
|
||||||
|
(lib.getExe pkgs-unstable.chromium)
|
||||||
|
];
|
||||||
|
};
|
||||||
context7 = {
|
context7 = {
|
||||||
type = "remote";
|
type = "remote";
|
||||||
url = "https://mcp.context7.com/mcp";
|
url = "https://mcp.context7.com/mcp";
|
||||||
@@ -924,432 +713,6 @@ in
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
programs.niri =
|
|
||||||
let
|
|
||||||
bind =
|
|
||||||
{
|
|
||||||
modifiers,
|
|
||||||
key,
|
|
||||||
action,
|
|
||||||
args ? [ ],
|
|
||||||
}:
|
|
||||||
{
|
|
||||||
${lib.join "+" (modifiers ++ [ key ])}.action.${action} = args;
|
|
||||||
};
|
|
||||||
in
|
|
||||||
{
|
|
||||||
enable = true;
|
|
||||||
package = pkgs-unstable.niri;
|
|
||||||
settings = {
|
|
||||||
input = {
|
|
||||||
keyboard = {
|
|
||||||
xkb = {
|
|
||||||
layout = "us";
|
|
||||||
variant = "altgr-intl";
|
|
||||||
};
|
|
||||||
numlock = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
touchpad = {
|
|
||||||
tap = true;
|
|
||||||
dwt = true;
|
|
||||||
dwtp = true;
|
|
||||||
natural-scroll = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
focus-follows-mouse = {
|
|
||||||
enable = true;
|
|
||||||
max-scroll-amount = "10%";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
outputs = {
|
|
||||||
"eDP-1" = {
|
|
||||||
scale = 1.5;
|
|
||||||
};
|
|
||||||
"DP-5" = {
|
|
||||||
scale = 1.0;
|
|
||||||
};
|
|
||||||
"DP-3" = {
|
|
||||||
scale = 1.2;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
layout = {
|
|
||||||
gaps = 5;
|
|
||||||
center-focused-column = "never";
|
|
||||||
|
|
||||||
preset-column-widths = [
|
|
||||||
{ proportion = 1.0 / 3.0; }
|
|
||||||
{ proportion = 1.0 / 2.0; }
|
|
||||||
{ proportion = 2.0 / 3.0; }
|
|
||||||
];
|
|
||||||
|
|
||||||
default-column-width = {
|
|
||||||
proportion = 1.0 / 2.0;
|
|
||||||
};
|
|
||||||
|
|
||||||
focus-ring = {
|
|
||||||
enable = true;
|
|
||||||
width = 4;
|
|
||||||
active.color = "#7fc8ff";
|
|
||||||
inactive.color = "#505050";
|
|
||||||
};
|
|
||||||
|
|
||||||
border = {
|
|
||||||
enable = false;
|
|
||||||
width = 4;
|
|
||||||
active.color = "#ffc87f";
|
|
||||||
inactive.color = "#505050";
|
|
||||||
urgent.color = "#9b0000";
|
|
||||||
};
|
|
||||||
|
|
||||||
shadow = {
|
|
||||||
enable = true;
|
|
||||||
softness = 30;
|
|
||||||
spread = 5;
|
|
||||||
offset = {
|
|
||||||
x = 0;
|
|
||||||
y = 5;
|
|
||||||
};
|
|
||||||
color = "#0007";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
spawn-at-startup = [
|
|
||||||
{ argv = [ ns ]; }
|
|
||||||
];
|
|
||||||
|
|
||||||
hotkey-overlay.skip-at-startup = true;
|
|
||||||
screenshot-path = "~/Pictures/Screenshots/Screenshot from %Y-%m-%d %H-%M-%S.png";
|
|
||||||
debug.honor-xdg-activation-with-invalid-serial = true;
|
|
||||||
|
|
||||||
layer-rules = [
|
|
||||||
{
|
|
||||||
matches = [ { namespace = "^noctalia-backdrop*"; } ];
|
|
||||||
place-within-backdrop = true;
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
window-rules = [
|
|
||||||
{
|
|
||||||
matches = [
|
|
||||||
{
|
|
||||||
app-id = "firefox$";
|
|
||||||
title = "^Picture-in-Picture$";
|
|
||||||
}
|
|
||||||
];
|
|
||||||
open-floating = true;
|
|
||||||
}
|
|
||||||
{
|
|
||||||
geometry-corner-radius = {
|
|
||||||
top-left = 14.;
|
|
||||||
top-right = 14.;
|
|
||||||
bottom-left = 14.;
|
|
||||||
bottom-right = 14.;
|
|
||||||
};
|
|
||||||
clip-to-geometry = true;
|
|
||||||
}
|
|
||||||
#{
|
|
||||||
# background-effect = {
|
|
||||||
# blur = true;
|
|
||||||
# xray = false;
|
|
||||||
# };
|
|
||||||
{
|
|
||||||
matches = [ { app-id = "dev.noctalia.Noctalia.Settings"; } ];
|
|
||||||
open-floating = true;
|
|
||||||
default-column-width.fixed = 1080;
|
|
||||||
default-window-height.fixed = 920;
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
binds = lib.foldl' (acc: x: acc // x) { } (
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"Mod+Shift+Slash" = {
|
|
||||||
action.show-hotkey-overlay = [ ];
|
|
||||||
};
|
|
||||||
"Mod+Return" = {
|
|
||||||
hotkey-overlay.title = "Open a Terminal: kitty";
|
|
||||||
action.spawn = "kitty";
|
|
||||||
};
|
|
||||||
"Mod+Z" = {
|
|
||||||
hotkey-overlay.title = "Open a launcher: walker";
|
|
||||||
action.spawn = "walker";
|
|
||||||
};
|
|
||||||
"Mod+Space" = {
|
|
||||||
action.spawn = "voxtoggle";
|
|
||||||
};
|
|
||||||
"Mod+Alt+L" = {
|
|
||||||
hotkey-overlay.title = "Lock the Screen: hyprlock";
|
|
||||||
action.spawn = ns-ipc [
|
|
||||||
"session"
|
|
||||||
"lock"
|
|
||||||
];
|
|
||||||
};
|
|
||||||
"XF86AudioRaiseVolume" = {
|
|
||||||
allow-when-locked = true;
|
|
||||||
action.spawn = ns-ipc [
|
|
||||||
"volume-up"
|
|
||||||
];
|
|
||||||
};
|
|
||||||
"XF86AudioLowerVolume" = {
|
|
||||||
allow-when-locked = true;
|
|
||||||
action.spawn = ns-ipc [
|
|
||||||
"volume-down"
|
|
||||||
];
|
|
||||||
};
|
|
||||||
"XF86AudioMute" = {
|
|
||||||
allow-when-locked = true;
|
|
||||||
action.spawn = ns-ipc [
|
|
||||||
"volume-mute"
|
|
||||||
];
|
|
||||||
};
|
|
||||||
"XF86AudioMicMute" = {
|
|
||||||
allow-when-locked = true;
|
|
||||||
action.spawn = ns-ipc [
|
|
||||||
"mic-mute"
|
|
||||||
];
|
|
||||||
};
|
|
||||||
"XF86AudioPlay" = {
|
|
||||||
allow-when-locked = true;
|
|
||||||
action.spawn = ns-ipc [
|
|
||||||
"media"
|
|
||||||
"toggle"
|
|
||||||
];
|
|
||||||
};
|
|
||||||
"XF86AudioPrev" = {
|
|
||||||
allow-when-locked = true;
|
|
||||||
action.spawn = ns-ipc [
|
|
||||||
"media"
|
|
||||||
"previous"
|
|
||||||
];
|
|
||||||
};
|
|
||||||
"XF86AudioNext" = {
|
|
||||||
allow-when-locked = true;
|
|
||||||
action.spawn = ns-ipc [
|
|
||||||
"media"
|
|
||||||
"next"
|
|
||||||
];
|
|
||||||
};
|
|
||||||
"XF86MonBrightnessUp" = {
|
|
||||||
allow-when-locked = true;
|
|
||||||
action.spawn = ns-ipc [
|
|
||||||
"brightness-up"
|
|
||||||
];
|
|
||||||
};
|
|
||||||
"XF86MonBrightnessDown" = {
|
|
||||||
allow-when-locked = true;
|
|
||||||
action.spawn = ns-ipc [
|
|
||||||
"brightness-down"
|
|
||||||
];
|
|
||||||
};
|
|
||||||
"Mod+O" = {
|
|
||||||
repeat = false;
|
|
||||||
action.toggle-overview = [ ];
|
|
||||||
};
|
|
||||||
"Mod+Backspace" = {
|
|
||||||
repeat = false;
|
|
||||||
action.close-window = [ ];
|
|
||||||
};
|
|
||||||
"Mod+BracketLeft".action.consume-or-expel-window-left = [ ];
|
|
||||||
"Mod+BracketRight".action.consume-or-expel-window-right = [ ];
|
|
||||||
"Mod+Comma".action.consume-window-into-column = [ ];
|
|
||||||
"Mod+Period".action.expel-window-from-column = [ ];
|
|
||||||
"Mod+R".action.switch-preset-column-width = [ ];
|
|
||||||
"Mod+Ctrl+R".action.switch-preset-window-height = [ ];
|
|
||||||
"Mod+Shift+R".action.reset-window-height = [ ];
|
|
||||||
"Mod+F".action.maximize-column = [ ];
|
|
||||||
"Mod+Shift+F".action.fullscreen-window = [ ];
|
|
||||||
"Mod+M".action.maximize-window-to-edges = [ ];
|
|
||||||
"Mod+Ctrl+F".action.expand-column-to-available-width = [ ];
|
|
||||||
"Mod+C".action.center-column = [ ];
|
|
||||||
"Mod+Ctrl+C".action.center-visible-columns = [ ];
|
|
||||||
"Mod+Minus".action.set-column-width = "-10%";
|
|
||||||
"Mod+Equal".action.set-column-width = "+10%";
|
|
||||||
"Mod+Shift+Minus".action.set-window-height = "-10%";
|
|
||||||
"Mod+Shift+Equal".action.set-window-height = "+10%";
|
|
||||||
"Mod+V".action.toggle-window-floating = [ ];
|
|
||||||
"Mod+Shift+V".action.switch-focus-between-floating-and-tiling = [ ];
|
|
||||||
"Mod+W".action.toggle-column-tabbed-display = [ ];
|
|
||||||
"Print".action.spawn = "scrot";
|
|
||||||
"Mod+Escape" = {
|
|
||||||
allow-inhibiting = true;
|
|
||||||
action.toggle-keyboard-shortcuts-inhibit = [ ];
|
|
||||||
};
|
|
||||||
"Mod+Shift+E".action.quit = [ ];
|
|
||||||
"Ctrl+Alt+Delete".action.quit = [ ];
|
|
||||||
"Mod+Shift+P".action.power-off-monitors = [ ];
|
|
||||||
}
|
|
||||||
]
|
|
||||||
++ (map
|
|
||||||
(
|
|
||||||
{
|
|
||||||
dir,
|
|
||||||
keys,
|
|
||||||
}:
|
|
||||||
(lib.mergeAttrsList (
|
|
||||||
map
|
|
||||||
(
|
|
||||||
{ modifiers, action }: (lib.mergeAttrsList (map (key: bind { inherit modifiers key action; }) keys))
|
|
||||||
)
|
|
||||||
[
|
|
||||||
{
|
|
||||||
modifiers = [ "Mod" ];
|
|
||||||
action = "focus-column-${dir}";
|
|
||||||
}
|
|
||||||
{
|
|
||||||
modifiers = [
|
|
||||||
"Mod"
|
|
||||||
"Shift"
|
|
||||||
];
|
|
||||||
action = "move-column-${dir}";
|
|
||||||
}
|
|
||||||
{
|
|
||||||
modifiers = [
|
|
||||||
"Mod"
|
|
||||||
"Ctrl"
|
|
||||||
];
|
|
||||||
action = "focus-monitor-${dir}";
|
|
||||||
}
|
|
||||||
{
|
|
||||||
modifiers = [
|
|
||||||
"Mod"
|
|
||||||
"Shift"
|
|
||||||
"Ctrl"
|
|
||||||
];
|
|
||||||
action = "move-column-to-monitor-${dir}";
|
|
||||||
}
|
|
||||||
]
|
|
||||||
))
|
|
||||||
)
|
|
||||||
[
|
|
||||||
{
|
|
||||||
dir = "left";
|
|
||||||
keys = [
|
|
||||||
"Left"
|
|
||||||
"H"
|
|
||||||
];
|
|
||||||
}
|
|
||||||
{
|
|
||||||
dir = "right";
|
|
||||||
keys = [
|
|
||||||
"Right"
|
|
||||||
"L"
|
|
||||||
];
|
|
||||||
}
|
|
||||||
]
|
|
||||||
)
|
|
||||||
++ (map
|
|
||||||
(
|
|
||||||
{
|
|
||||||
dir,
|
|
||||||
keys,
|
|
||||||
}:
|
|
||||||
(lib.mergeAttrsList (
|
|
||||||
map
|
|
||||||
(
|
|
||||||
{ modifiers, action }: (lib.mergeAttrsList (map (key: bind { inherit modifiers key action; }) keys))
|
|
||||||
)
|
|
||||||
[
|
|
||||||
{
|
|
||||||
modifiers = [ "Mod" ];
|
|
||||||
action = "focus-window-or-workspace-${dir}";
|
|
||||||
}
|
|
||||||
{
|
|
||||||
modifiers = [
|
|
||||||
"Mod"
|
|
||||||
"Shift"
|
|
||||||
];
|
|
||||||
action = "move-window-${dir}-or-to-workspace-${dir}";
|
|
||||||
}
|
|
||||||
{
|
|
||||||
modifiers = [
|
|
||||||
"Mod"
|
|
||||||
"Ctrl"
|
|
||||||
];
|
|
||||||
action = "focus-monitor-${dir}";
|
|
||||||
}
|
|
||||||
{
|
|
||||||
modifiers = [
|
|
||||||
"Mod"
|
|
||||||
"Shift"
|
|
||||||
"Ctrl"
|
|
||||||
];
|
|
||||||
action = "move-column-to-monitor-${dir}";
|
|
||||||
}
|
|
||||||
]
|
|
||||||
))
|
|
||||||
)
|
|
||||||
[
|
|
||||||
{
|
|
||||||
dir = "up";
|
|
||||||
keys = [
|
|
||||||
"Up"
|
|
||||||
"K"
|
|
||||||
];
|
|
||||||
}
|
|
||||||
{
|
|
||||||
dir = "down";
|
|
||||||
keys = [
|
|
||||||
"Down"
|
|
||||||
"J"
|
|
||||||
];
|
|
||||||
}
|
|
||||||
]
|
|
||||||
)
|
|
||||||
++ [
|
|
||||||
{
|
|
||||||
"Mod+Home".action.focus-column-first = [ ];
|
|
||||||
"Mod+End".action.focus-column-last = [ ];
|
|
||||||
"Mod+Shift+Home".action.move-column-to-first = [ ];
|
|
||||||
"Mod+Shift+End".action.move-column-to-last = [ ];
|
|
||||||
"Mod+Page_Down".action.focus-workspace-down = [ ];
|
|
||||||
"Mod+Page_Up".action.focus-workspace-up = [ ];
|
|
||||||
"Mod+U".action.focus-workspace-down = [ ];
|
|
||||||
"Mod+I".action.focus-workspace-up = [ ];
|
|
||||||
"Mod+Ctrl+Page_Down".action.move-column-to-workspace-down = [ ];
|
|
||||||
"Mod+Ctrl+Page_Up".action.move-column-to-workspace-up = [ ];
|
|
||||||
"Mod+Ctrl+U".action.move-column-to-workspace-down = [ ];
|
|
||||||
"Mod+Ctrl+I".action.move-column-to-workspace-up = [ ];
|
|
||||||
"Mod+Shift+Page_Down".action.move-workspace-down = [ ];
|
|
||||||
"Mod+Shift+Page_Up".action.move-workspace-up = [ ];
|
|
||||||
"Mod+Shift+U".action.move-workspace-down = [ ];
|
|
||||||
"Mod+Shift+I".action.move-workspace-up = [ ];
|
|
||||||
"Mod+WheelScrollDown" = {
|
|
||||||
cooldown-ms = 150;
|
|
||||||
action.focus-workspace-down = [ ];
|
|
||||||
};
|
|
||||||
"Mod+WheelScrollUp" = {
|
|
||||||
cooldown-ms = 150;
|
|
||||||
action.focus-workspace-up = [ ];
|
|
||||||
};
|
|
||||||
"Mod+Ctrl+WheelScrollDown" = {
|
|
||||||
cooldown-ms = 150;
|
|
||||||
action.move-column-to-workspace-down = [ ];
|
|
||||||
};
|
|
||||||
"Mod+Ctrl+WheelScrollUp" = {
|
|
||||||
cooldown-ms = 150;
|
|
||||||
action.move-column-to-workspace-up = [ ];
|
|
||||||
};
|
|
||||||
"Mod+WheelScrollRight".action.focus-column-right = [ ];
|
|
||||||
"Mod+WheelScrollLeft".action.focus-column-left = [ ];
|
|
||||||
"Mod+Ctrl+WheelScrollRight".action.move-column-right = [ ];
|
|
||||||
"Mod+Ctrl+WheelScrollLeft".action.move-column-left = [ ];
|
|
||||||
"Mod+Shift+WheelScrollDown".action.focus-column-right = [ ];
|
|
||||||
"Mod+Shift+WheelScrollUp".action.focus-column-left = [ ];
|
|
||||||
"Mod+Ctrl+Shift+WheelScrollDown".action.move-column-right = [ ];
|
|
||||||
"Mod+Ctrl+Shift+WheelScrollUp".action.move-column-left = [ ];
|
|
||||||
}
|
|
||||||
]
|
|
||||||
++ (map (number: {
|
|
||||||
"Mod+${toString number}".action.focus-workspace = number;
|
|
||||||
"Mod+Shift+${toString number}".action.move-column-to-workspace = number;
|
|
||||||
}) (lib.range 1 9))
|
|
||||||
);
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
gnome.automaticTimeZone = true;
|
gnome.automaticTimeZone = true;
|
||||||
gtk.enable = true;
|
gtk.enable = true;
|
||||||
|
|
||||||
|
|||||||
@@ -14,13 +14,14 @@
|
|||||||
ffmpeg
|
ffmpeg
|
||||||
mediainfo
|
mediainfo
|
||||||
git-lfs
|
git-lfs
|
||||||
|
telegram-desktop
|
||||||
shards
|
shards
|
||||||
deno
|
deno
|
||||||
virt-manager
|
virt-manager
|
||||||
gimp
|
gimp
|
||||||
lftp
|
lftp
|
||||||
inkscape
|
inkscape
|
||||||
wineWow64Packages.waylandFull
|
wineWowPackages.waylandFull
|
||||||
reaper
|
reaper
|
||||||
inputs.ksoloti-pr.legacyPackages.${pkgs.stdenv.hostPlatform.system}.ksoloti
|
inputs.ksoloti-pr.legacyPackages.${pkgs.stdenv.hostPlatform.system}.ksoloti
|
||||||
calibre
|
calibre
|
||||||
|
|||||||
+1
-29
@@ -1,18 +1,6 @@
|
|||||||
{
|
{ pkgs, pkgs-unstable, ... }:
|
||||||
pkgs,
|
|
||||||
pkgs-unstable,
|
|
||||||
lib,
|
|
||||||
...
|
|
||||||
}:
|
|
||||||
let
|
let
|
||||||
homeDirectory = "/home/moco";
|
homeDirectory = "/home/moco";
|
||||||
nmcli = lib.getExe' pkgs.networkmanager "nmcli";
|
|
||||||
ykman = lib.getExe pkgs.yubikey-manager;
|
|
||||||
isMocoVpnActive = lib.getExe (
|
|
||||||
pkgs.writeShellScriptBin "is-moco-vpn-active" ''
|
|
||||||
${nmcli} -t -f NAME connection show --active | grep -q '^moco$'
|
|
||||||
''
|
|
||||||
);
|
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
# Home Manager needs a bit of information about you and the paths it should
|
# Home Manager needs a bit of information about you and the paths it should
|
||||||
@@ -45,15 +33,6 @@ in
|
|||||||
(writeShellScriptBin "moco-reto-otp" ''
|
(writeShellScriptBin "moco-reto-otp" ''
|
||||||
ykman oath accounts code -s 'MOCO Reto' | wl-copy -n
|
ykman oath accounts code -s 'MOCO Reto' | wl-copy -n
|
||||||
'')
|
'')
|
||||||
(writeShellScriptBin "toggle-moco-vpn" ''
|
|
||||||
if ${isMocoVpnActive}; then
|
|
||||||
${nmcli} c down moco
|
|
||||||
else
|
|
||||||
${ykman} oath accounts code -s 'MOCO Reto' | ${nmcli} c up moco --ask
|
|
||||||
fi
|
|
||||||
'')
|
|
||||||
kreuzberg-cli
|
|
||||||
tesseract
|
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -219,11 +198,4 @@ in
|
|||||||
user = "butler";
|
user = "butler";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
programs.noctalia.settings.calendar.account.moco = {
|
|
||||||
type = "caldav";
|
|
||||||
provider = "custom";
|
|
||||||
name = "MOCO";
|
|
||||||
server_url = "https://intern.mocoapp.com/users/933620701/calendar/9df64d69e29c97a75cd6d93aeec0d5ae.ics";
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,43 +0,0 @@
|
|||||||
{
|
|
||||||
config,
|
|
||||||
lib,
|
|
||||||
pkgs,
|
|
||||||
pkgs-unstable,
|
|
||||||
...
|
|
||||||
}:
|
|
||||||
let
|
|
||||||
cfg = config.services.elephant;
|
|
||||||
wrappedPackage = pkgs.symlinkJoin {
|
|
||||||
inherit (cfg.package) name meta;
|
|
||||||
paths = [ cfg.package ];
|
|
||||||
nativeBuildInputs = [ pkgs.makeBinaryWrapper ];
|
|
||||||
postBuild = ''
|
|
||||||
wrapProgram $out/bin/elephant \
|
|
||||||
--prefix PATH : ${lib.makeBinPath [ pkgs.bash ]}
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
in
|
|
||||||
{
|
|
||||||
options.services.elephant = {
|
|
||||||
enable = lib.mkEnableOption "Enable Elephant";
|
|
||||||
package = lib.mkOption {
|
|
||||||
type = lib.types.package;
|
|
||||||
default = pkgs-unstable.elephant;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
config = lib.mkIf cfg.enable {
|
|
||||||
systemd.user.services.elephant = {
|
|
||||||
Unit = {
|
|
||||||
Description = "Elephant";
|
|
||||||
After = [ "niri.service" ];
|
|
||||||
Wants = [ "niri.service" ];
|
|
||||||
};
|
|
||||||
Service = {
|
|
||||||
ExecStart = lib.getExe wrappedPackage;
|
|
||||||
Restart = "on-failure";
|
|
||||||
};
|
|
||||||
Install.WantedBy = [ "graphical-session.target" ];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,100 +0,0 @@
|
|||||||
{
|
|
||||||
config,
|
|
||||||
osConfig,
|
|
||||||
lib,
|
|
||||||
pkgs,
|
|
||||||
pkgs-unstable,
|
|
||||||
...
|
|
||||||
}:
|
|
||||||
let
|
|
||||||
cfg = config.programs.voxtype;
|
|
||||||
postProcessUnwrapped =
|
|
||||||
pkgs.runCommand "voxtype-post-process-unwrapped"
|
|
||||||
{
|
|
||||||
code = ./post-process.cr;
|
|
||||||
nativeBuildInputs = [
|
|
||||||
pkgs-unstable.crystal
|
|
||||||
];
|
|
||||||
}
|
|
||||||
''
|
|
||||||
mkdir -p $out/bin
|
|
||||||
crystal build $code -o $out/bin/voxtype-post-process
|
|
||||||
'';
|
|
||||||
|
|
||||||
postProcess = pkgs.symlinkJoin {
|
|
||||||
name = "voxtype-post-process";
|
|
||||||
paths = [ postProcessUnwrapped ];
|
|
||||||
nativeBuildInputs = [ pkgs.makeBinaryWrapper ];
|
|
||||||
postBuild = ''
|
|
||||||
wrapProgram $out/bin/voxtype-post-process \
|
|
||||||
--set OLLAMA_PORT ${toString osConfig.services.ollama.port} \
|
|
||||||
--set WALKER_BIN ${lib.getExe config.services.walker.package}
|
|
||||||
'';
|
|
||||||
meta.mainProgram = "voxtype-post-process";
|
|
||||||
};
|
|
||||||
|
|
||||||
postProcessingConfig = pkgs.writeText "voxtype-post-processing.json" (
|
|
||||||
builtins.toJSON cfg.postProcessing.settings
|
|
||||||
);
|
|
||||||
in
|
|
||||||
{
|
|
||||||
options.programs.voxtype = {
|
|
||||||
postProcessing = lib.mkOption {
|
|
||||||
type = lib.types.submodule {
|
|
||||||
options = {
|
|
||||||
enable = lib.mkEnableOption "Enable post-processing of transcriptions";
|
|
||||||
settings = lib.mkOption {
|
|
||||||
type = lib.types.submodule {
|
|
||||||
options = {
|
|
||||||
model = lib.mkOption {
|
|
||||||
type = lib.types.str;
|
|
||||||
description = "The ollama model to use for post-processing";
|
|
||||||
};
|
|
||||||
commonInstructions = lib.mkOption {
|
|
||||||
type = lib.types.str;
|
|
||||||
default = "no quotes, no emojis, no explanations";
|
|
||||||
description = "Instructions to include in every post-processing prompt";
|
|
||||||
};
|
|
||||||
prompts = lib.mkOption {
|
|
||||||
type = lib.types.listOf (
|
|
||||||
lib.types.submodule {
|
|
||||||
options = {
|
|
||||||
title = lib.mkOption {
|
|
||||||
type = lib.types.str;
|
|
||||||
description = "A title for this prompt, used in the selector";
|
|
||||||
};
|
|
||||||
instructions = lib.mkOption {
|
|
||||||
type = lib.types.str;
|
|
||||||
description = "Instructions to include in the post-processing prompt, in addition to the common instructions";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
);
|
|
||||||
default = [
|
|
||||||
{
|
|
||||||
title = "Clean up";
|
|
||||||
instructions = "Clean up this dictation. Remove filler words, fix grammar and punctuation. Output ONLY the cleaned text";
|
|
||||||
}
|
|
||||||
];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
default = { };
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
config = lib.mkIf cfg.postProcessing.enable {
|
|
||||||
xdg.configFile."voxtype/post-processing.json".source = postProcessingConfig;
|
|
||||||
programs.voxtype.settings.output.post_process = {
|
|
||||||
command = lib.getExe postProcess;
|
|
||||||
timeout_ms = 5 * 60 * 1000; # 5 minutes
|
|
||||||
};
|
|
||||||
|
|
||||||
systemd.user.services.voxtype.Service.X-Reload-If-Changed = [
|
|
||||||
postProcess
|
|
||||||
];
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
require "json"
|
|
||||||
require "http/client"
|
|
||||||
|
|
||||||
struct OllamaResponse
|
|
||||||
include JSON::Serializable
|
|
||||||
|
|
||||||
getter response : String
|
|
||||||
end
|
|
||||||
|
|
||||||
struct Prompt
|
|
||||||
include JSON::Serializable
|
|
||||||
|
|
||||||
getter title : String
|
|
||||||
getter instructions : String
|
|
||||||
end
|
|
||||||
|
|
||||||
struct Config
|
|
||||||
include JSON::Serializable
|
|
||||||
|
|
||||||
getter model : String
|
|
||||||
getter prompts : Array(Prompt)
|
|
||||||
@[JSON::Field(key: "commonInstructions")]
|
|
||||||
getter common_instructions : String
|
|
||||||
end
|
|
||||||
|
|
||||||
config_path = "#{ENV.fetch("XDG_CONFIG_HOME", "~/.config")}/voxtype/post-processing.json"
|
|
||||||
config = File.open(config_path) { |file| Config.from_json(file) }
|
|
||||||
client = HTTP::Client.new("localhost", ENV.fetch("OLLAMA_PORT", "11434").to_i)
|
|
||||||
|
|
||||||
prompt_selection = Process.run(ENV["WALKER_BIN"], ["--dmenu"]) do |process|
|
|
||||||
config.prompts.each do |prompt|
|
|
||||||
process.input.puts prompt.title
|
|
||||||
end
|
|
||||||
|
|
||||||
process.input.close
|
|
||||||
process.output.gets_to_end.chomp
|
|
||||||
end
|
|
||||||
|
|
||||||
instructions = config.prompts.find { |prompt| prompt.title == prompt_selection }.try(&.instructions) || prompt_selection
|
|
||||||
|
|
||||||
payload = {
|
|
||||||
model: config.model,
|
|
||||||
prompt: "#{instructions} - #{config.common_instructions}:\n\n#{STDIN.gets_to_end.chomp}",
|
|
||||||
think: false,
|
|
||||||
stream: false,
|
|
||||||
}
|
|
||||||
|
|
||||||
client.post("/api/generate", body: payload.to_json) do |response|
|
|
||||||
if response.status_code == 200
|
|
||||||
puts OllamaResponse.from_json(response.body_io).response.strip
|
|
||||||
else
|
|
||||||
abort "Ollama API error: #{response.status_code} #{response.body}"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
+173
-98
@@ -3,70 +3,10 @@
|
|||||||
lib,
|
lib,
|
||||||
pkgs,
|
pkgs,
|
||||||
pkgs-unstable,
|
pkgs-unstable,
|
||||||
inputs,
|
|
||||||
...
|
...
|
||||||
}:
|
}:
|
||||||
let
|
let
|
||||||
cfg = config.programs.zed-editor;
|
cfg = config.programs.zed-editor;
|
||||||
models-dev = inputs.models-dev;
|
|
||||||
|
|
||||||
getModelsFromProvider =
|
|
||||||
provider:
|
|
||||||
let
|
|
||||||
providerPath = "${models-dev}/providers/${provider}";
|
|
||||||
modelsPath = "${providerPath}/models";
|
|
||||||
providerToml = builtins.fromTOML (builtins.readFile "${providerPath}/provider.toml");
|
|
||||||
|
|
||||||
findAllTomlFiles =
|
|
||||||
path:
|
|
||||||
let
|
|
||||||
entries = builtins.readDir path;
|
|
||||||
files = builtins.filter (name: builtins.match ".*\\.toml$" name != null) (
|
|
||||||
builtins.attrNames (lib.filterAttrs (_: type: type == "regular") entries)
|
|
||||||
);
|
|
||||||
dirs = builtins.attrNames (lib.filterAttrs (_: type: type == "directory") entries);
|
|
||||||
nestedFiles = builtins.concatMap (
|
|
||||||
dir:
|
|
||||||
let
|
|
||||||
nestedPath = "${path}/${dir}";
|
|
||||||
nestedTomlFiles = findAllTomlFiles nestedPath;
|
|
||||||
in
|
|
||||||
map (file: "${dir}/${file}") nestedTomlFiles
|
|
||||||
) dirs;
|
|
||||||
in
|
|
||||||
files ++ nestedFiles;
|
|
||||||
|
|
||||||
modelFiles = findAllTomlFiles modelsPath;
|
|
||||||
modelEntries = map (
|
|
||||||
file:
|
|
||||||
let
|
|
||||||
filePath = "${modelsPath}/${file}";
|
|
||||||
parsed = builtins.fromTOML (builtins.readFile filePath);
|
|
||||||
modelName = builtins.substring 0 (builtins.stringLength file - 5) file;
|
|
||||||
in
|
|
||||||
{
|
|
||||||
name = modelName;
|
|
||||||
display_name = parsed.name or modelName;
|
|
||||||
max_tokens = parsed.limit.context or parsed.limit.output or 128000;
|
|
||||||
}
|
|
||||||
) modelFiles;
|
|
||||||
in
|
|
||||||
{
|
|
||||||
api_url = providerToml.api;
|
|
||||||
available_models = builtins.sort (a: b: a.name < b.name) modelEntries;
|
|
||||||
name = providerToml.name;
|
|
||||||
};
|
|
||||||
|
|
||||||
opencodeProviders = map getModelsFromProvider [
|
|
||||||
"opencode"
|
|
||||||
"opencode-go"
|
|
||||||
"fireworks-ai"
|
|
||||||
];
|
|
||||||
|
|
||||||
openaiCompatibleProviders = builtins.listToAttrs (
|
|
||||||
map (p: lib.nameValuePair p.name p) opencodeProviders
|
|
||||||
);
|
|
||||||
|
|
||||||
ameba-ls = pkgs.stdenv.mkDerivation rec {
|
ameba-ls = pkgs.stdenv.mkDerivation rec {
|
||||||
pname = "ameba-ls";
|
pname = "ameba-ls";
|
||||||
version = "0.1.0";
|
version = "0.1.0";
|
||||||
@@ -141,15 +81,8 @@ in
|
|||||||
agent = {
|
agent = {
|
||||||
enabled = true;
|
enabled = true;
|
||||||
default_model = {
|
default_model = {
|
||||||
provider = "fireworks-ai";
|
provider = "openai";
|
||||||
model = "accounts/fireworks/models/glm-5p2";
|
model = "kimi-k2.5";
|
||||||
};
|
|
||||||
};
|
|
||||||
agent_servers = {
|
|
||||||
OpenCode = {
|
|
||||||
command = "opencode";
|
|
||||||
args = [ "acp" ];
|
|
||||||
type = "custom";
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
auto_update = false;
|
auto_update = false;
|
||||||
@@ -180,7 +113,7 @@ in
|
|||||||
};
|
};
|
||||||
Nix = {
|
Nix = {
|
||||||
formatter.external = {
|
formatter.external = {
|
||||||
command = lib.getExe pkgs.nixfmt;
|
command = lib.getExe pkgs.nixfmt-rfc-style;
|
||||||
arguments = [ "-q" ];
|
arguments = [ "-q" ];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -208,10 +141,6 @@ in
|
|||||||
];
|
];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
TypeScript.language_servers = [
|
|
||||||
"tsgo"
|
|
||||||
"vtsls"
|
|
||||||
];
|
|
||||||
};
|
};
|
||||||
lsp = with pkgs; {
|
lsp = with pkgs; {
|
||||||
nixd.binary.path = lib.getExe nixd;
|
nixd.binary.path = lib.getExe nixd;
|
||||||
@@ -255,7 +184,7 @@ in
|
|||||||
arguments = [ "--stdio" ];
|
arguments = [ "--stdio" ];
|
||||||
};
|
};
|
||||||
json-language-server.binary = {
|
json-language-server.binary = {
|
||||||
path = lib.getExe vscode-json-languageserver;
|
path = lib.getExe nodePackages.vscode-json-languageserver;
|
||||||
arguments = [ "--stdio" ];
|
arguments = [ "--stdio" ];
|
||||||
};
|
};
|
||||||
package-version-server.binary.path = lib.getExe pkgs-unstable.package-version-server;
|
package-version-server.binary.path = lib.getExe pkgs-unstable.package-version-server;
|
||||||
@@ -275,14 +204,9 @@ in
|
|||||||
font_family = "IosevkaTerm Nerd Font";
|
font_family = "IosevkaTerm Nerd Font";
|
||||||
};
|
};
|
||||||
edit_predictions = {
|
edit_predictions = {
|
||||||
|
enabled = true;
|
||||||
mode = "eager";
|
mode = "eager";
|
||||||
provider = "open_ai_compatible_api";
|
provider = "copilot";
|
||||||
open_ai_compatible_api = {
|
|
||||||
api_url = "https://api.fireworks.ai/inference/v1/completions";
|
|
||||||
model = "accounts/fireworks/models/qwen3p6-plus";
|
|
||||||
prompt_format = "qwen";
|
|
||||||
max_output_tokens = 1024;
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
buffer_font_family = "Iosevka Nerd Font";
|
buffer_font_family = "Iosevka Nerd Font";
|
||||||
buffer_font_size = 16;
|
buffer_font_size = 16;
|
||||||
@@ -295,7 +219,173 @@ in
|
|||||||
];
|
];
|
||||||
};
|
};
|
||||||
language_models = {
|
language_models = {
|
||||||
openai_compatible = openaiCompatibleProviders;
|
openai = {
|
||||||
|
api_url = "https://opencode.ai/zen/v1";
|
||||||
|
available_models = [
|
||||||
|
# Claude models
|
||||||
|
{
|
||||||
|
name = "claude-opus-4-6";
|
||||||
|
display_name = "OpenCode Zen - Claude Opus 4.6";
|
||||||
|
max_tokens = 1048576;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "claude-opus-4-5";
|
||||||
|
display_name = "OpenCode Zen - Claude Opus 4.5";
|
||||||
|
max_tokens = 200000;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "claude-opus-4-1";
|
||||||
|
display_name = "OpenCode Zen - Claude Opus 4.1";
|
||||||
|
max_tokens = 200000;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "claude-sonnet-4";
|
||||||
|
display_name = "OpenCode Zen - Claude Sonnet 4";
|
||||||
|
max_tokens = 1048576;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "claude-sonnet-4-5";
|
||||||
|
display_name = "OpenCode Zen - Claude Sonnet 4.5";
|
||||||
|
max_tokens = 1048576;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "claude-3-5-haiku";
|
||||||
|
display_name = "OpenCode Zen - Claude 3.5 Haiku";
|
||||||
|
max_tokens = 200000;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "claude-haiku-4-5";
|
||||||
|
display_name = "OpenCode Zen - Claude Haiku 4.5";
|
||||||
|
max_tokens = 200000;
|
||||||
|
}
|
||||||
|
# GPT models
|
||||||
|
{
|
||||||
|
name = "gpt-5.2";
|
||||||
|
display_name = "OpenCode Zen - GPT 5.2";
|
||||||
|
max_tokens = 400000;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "gpt-5.2-codex";
|
||||||
|
display_name = "OpenCode Zen - GPT 5.2 Codex";
|
||||||
|
max_tokens = 400000;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "gpt-5.1";
|
||||||
|
display_name = "OpenCode Zen - GPT 5.1";
|
||||||
|
max_tokens = 400000;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "gpt-5.1-codex-max";
|
||||||
|
display_name = "OpenCode Zen - GPT 5.1 Codex Max";
|
||||||
|
max_tokens = 400000;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "gpt-5.1-codex";
|
||||||
|
display_name = "OpenCode Zen - GPT 5.1 Codex";
|
||||||
|
max_tokens = 400000;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "gpt-5.1-codex-mini";
|
||||||
|
display_name = "OpenCode Zen - GPT 5.1 Codex Mini";
|
||||||
|
max_tokens = 400000;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "gpt-5";
|
||||||
|
display_name = "OpenCode Zen - GPT 5";
|
||||||
|
max_tokens = 400000;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "gpt-5-codex";
|
||||||
|
display_name = "OpenCode Zen - GPT 5 Codex";
|
||||||
|
max_tokens = 400000;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "gpt-5-nano";
|
||||||
|
display_name = "OpenCode Zen - GPT 5 Nano";
|
||||||
|
max_tokens = 400000;
|
||||||
|
}
|
||||||
|
# Gemini models
|
||||||
|
{
|
||||||
|
name = "gemini-3-pro";
|
||||||
|
display_name = "OpenCode Zen - Gemini 3 Pro";
|
||||||
|
max_tokens = 1048576;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "gemini-3-flash";
|
||||||
|
display_name = "OpenCode Zen - Gemini 3 Flash";
|
||||||
|
max_tokens = 1048576;
|
||||||
|
}
|
||||||
|
# GLM models
|
||||||
|
{
|
||||||
|
name = "glm-4.7";
|
||||||
|
display_name = "OpenCode Zen - GLM 4.7";
|
||||||
|
max_tokens = 205000;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "glm-4.6";
|
||||||
|
display_name = "OpenCode Zen - GLM 4.6";
|
||||||
|
max_tokens = 205000;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "glm-4.7-free";
|
||||||
|
display_name = "OpenCode Zen - GLM 4.7 Free";
|
||||||
|
max_tokens = 205000;
|
||||||
|
}
|
||||||
|
# Kimi models
|
||||||
|
{
|
||||||
|
name = "kimi-k2.5";
|
||||||
|
display_name = "OpenCode Zen - Kimi K2.5";
|
||||||
|
max_tokens = 262000;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "kimi-k2.5-free";
|
||||||
|
display_name = "OpenCode Zen - Kimi K2.5 Free";
|
||||||
|
max_tokens = 262000;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "kimi-k2";
|
||||||
|
display_name = "OpenCode Zen - Kimi K2";
|
||||||
|
max_tokens = 262000;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "kimi-k2-thinking";
|
||||||
|
display_name = "OpenCode Zen - Kimi K2 Thinking";
|
||||||
|
max_tokens = 262000;
|
||||||
|
}
|
||||||
|
# MiniMax models
|
||||||
|
{
|
||||||
|
name = "minimax-m2.1";
|
||||||
|
display_name = "OpenCode Zen - MiniMax M2.1";
|
||||||
|
max_tokens = 205000;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "minimax-m2.5-free";
|
||||||
|
display_name = "OpenCode Zen - MiniMax M2.5 Free";
|
||||||
|
max_tokens = 205000;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "minimax-m2.1-free";
|
||||||
|
display_name = "OpenCode Zen - MiniMax M2.1 Free";
|
||||||
|
max_tokens = 205000;
|
||||||
|
}
|
||||||
|
# Other models
|
||||||
|
{
|
||||||
|
name = "trinity-large-preview-free";
|
||||||
|
display_name = "OpenCode Zen - Trinity Large Preview Free";
|
||||||
|
max_tokens = 131000;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "big-pickle";
|
||||||
|
display_name = "OpenCode Zen - Big Pickle";
|
||||||
|
max_tokens = 200000;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "alpha-g5";
|
||||||
|
display_name = "OpenCode Zen - Alpha G5";
|
||||||
|
max_tokens = 200000;
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
userKeymaps = [
|
userKeymaps = [
|
||||||
@@ -309,13 +399,6 @@ in
|
|||||||
reveal_target = "center";
|
reveal_target = "center";
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
"space o" = [
|
|
||||||
"task::Spawn"
|
|
||||||
{
|
|
||||||
task_name = "opencode";
|
|
||||||
reveal_target = "center";
|
|
||||||
}
|
|
||||||
];
|
|
||||||
"space r" = "pane::DeploySearch";
|
"space r" = "pane::DeploySearch";
|
||||||
"space f" = [
|
"space f" = [
|
||||||
"file_finder::Toggle"
|
"file_finder::Toggle"
|
||||||
@@ -361,14 +444,6 @@ in
|
|||||||
use_new_terminal = false;
|
use_new_terminal = false;
|
||||||
hide = "on_success";
|
hide = "on_success";
|
||||||
}
|
}
|
||||||
{
|
|
||||||
label = "opencode";
|
|
||||||
command = "opencode";
|
|
||||||
reveal = "always";
|
|
||||||
allow_concurrent_runs = true;
|
|
||||||
use_new_terminal = false;
|
|
||||||
hide = "on_success";
|
|
||||||
}
|
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -258,25 +258,61 @@
|
|||||||
openFirewall = true;
|
openFirewall = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
services.invidious-companion = {
|
services.home-assistant = {
|
||||||
enable = true;
|
enable = true;
|
||||||
host = "0.0.0.0";
|
extraComponents = [
|
||||||
port = 8282;
|
# Components required to complete the onboarding
|
||||||
secretKeyFile = config.age.secrets.invidious-companion.path;
|
"esphome"
|
||||||
binaryHash = "sha256-RU18Zt/dSMVF/hLeAg+J3BrmDUnO7L4mv/+g7A91WL8=";
|
"met"
|
||||||
|
"radio_browser"
|
||||||
|
|
||||||
|
"yeelight"
|
||||||
|
"xiaomi_aqara"
|
||||||
|
"shelly"
|
||||||
|
];
|
||||||
|
subdomain = "home";
|
||||||
|
extraPackages =
|
||||||
|
python3Packages: with python3Packages; [
|
||||||
|
gtts
|
||||||
|
numpy
|
||||||
|
];
|
||||||
|
config = {
|
||||||
|
homeassistant = {
|
||||||
|
name = "Koti";
|
||||||
|
unit_system = "metric";
|
||||||
|
time_zone = "Europe/Helsinki";
|
||||||
|
};
|
||||||
|
http = {
|
||||||
|
use_x_forwarded_for = true;
|
||||||
|
trusted_proxies = "127.0.0.1";
|
||||||
|
};
|
||||||
|
default_config = { };
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
networking.firewall = {
|
services = {
|
||||||
|
webserver = {
|
||||||
enable = true;
|
enable = true;
|
||||||
interfaces.tailscale0.allowedTCPPorts = [ 8282 ];
|
acme.dnsChallenge = true;
|
||||||
|
vHosts."koti.repomaa.com" = {
|
||||||
|
proxyBuffering = false;
|
||||||
|
locations."/".proxyPort = 8123;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
invidious = {
|
||||||
|
enable = true;
|
||||||
|
subdomain = "vid";
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
security.acme.defaults.environmentFile = config.age.secrets.hetzner.path;
|
security.acme.defaults.environmentFile = config.age.secrets.hetzner.path;
|
||||||
|
|
||||||
networking = {
|
networking = {
|
||||||
nftables.enable = true;
|
nftables.enable = true;
|
||||||
|
firewall.enable = true;
|
||||||
useDHCP = false;
|
useDHCP = false;
|
||||||
domain = "apu.home.arpa";
|
domain = "repomaa.com";
|
||||||
};
|
};
|
||||||
|
|
||||||
system.stateVersion = "24.05";
|
system.stateVersion = "24.05";
|
||||||
|
|||||||
@@ -10,7 +10,6 @@
|
|||||||
})
|
})
|
||||||
[
|
[
|
||||||
"hetzner"
|
"hetzner"
|
||||||
"invidious-companion"
|
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,8 +20,6 @@ in
|
|||||||
"flakes"
|
"flakes"
|
||||||
];
|
];
|
||||||
auto-optimise-store = true;
|
auto-optimise-store = true;
|
||||||
min-free = 5 * 1024 * 1024 * 1024;
|
|
||||||
max-free = 10 * 1024 * 1024 * 1024;
|
|
||||||
};
|
};
|
||||||
gc = {
|
gc = {
|
||||||
automatic = true;
|
automatic = true;
|
||||||
@@ -30,11 +28,8 @@ in
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
services.journald.extraConfig = "SystemMaxUse=500M";
|
|
||||||
|
|
||||||
boot.loader.systemd-boot.enable = true;
|
boot.loader.systemd-boot.enable = true;
|
||||||
boot.loader.efi.canTouchEfiVariables = true;
|
boot.loader.efi.canTouchEfiVariables = true;
|
||||||
boot.binfmt.emulatedSystems = [ "x86_64-linux" ];
|
|
||||||
|
|
||||||
networking.hostName = "freun-dev"; # Define your hostname.
|
networking.hostName = "freun-dev"; # Define your hostname.
|
||||||
networking.domain = "freun.dev";
|
networking.domain = "freun.dev";
|
||||||
@@ -110,12 +105,6 @@ in
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
services.resolved = {
|
|
||||||
enable = true;
|
|
||||||
dnsovertls = "opportunistic";
|
|
||||||
dnssec = "allow-downgrade";
|
|
||||||
};
|
|
||||||
|
|
||||||
services.networkd-dispatcher = {
|
services.networkd-dispatcher = {
|
||||||
enable = true;
|
enable = true;
|
||||||
rules."50-tailscale" = {
|
rules."50-tailscale" = {
|
||||||
@@ -168,8 +157,6 @@ in
|
|||||||
"builder"
|
"builder"
|
||||||
];
|
];
|
||||||
|
|
||||||
modules.tempBuildStorage.enable = true;
|
|
||||||
|
|
||||||
# List packages installed in system profile. To search, run:
|
# List packages installed in system profile. To search, run:
|
||||||
# $ nix search wget
|
# $ nix search wget
|
||||||
environment.systemPackages = with pkgs; [
|
environment.systemPackages = with pkgs; [
|
||||||
|
|||||||
@@ -32,9 +32,6 @@
|
|||||||
"voidauth"
|
"voidauth"
|
||||||
"gitea"
|
"gitea"
|
||||||
"gitea-actions-runner"
|
"gitea-actions-runner"
|
||||||
"searx"
|
|
||||||
"open-webui"
|
|
||||||
"open-terminal-api-key"
|
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
// {
|
// {
|
||||||
@@ -54,13 +51,5 @@
|
|||||||
file = ../../secrets/hledger-basic-auth.age;
|
file = ../../secrets/hledger-basic-auth.age;
|
||||||
owner = if (config.services.hledger-web.enable) then "nginx" else "root";
|
owner = if (config.services.hledger-web.enable) then "nginx" else "root";
|
||||||
};
|
};
|
||||||
invidious = lib.mkIf config.services.invidious.enable {
|
|
||||||
file = ../../secrets/invidious.age;
|
|
||||||
owner = config.systemd.services.invidious.serviceConfig.User;
|
|
||||||
};
|
|
||||||
"grafana/secret-key" = lib.mkIf config.services.grafana.enable {
|
|
||||||
file = ../../secrets/grafana/secret-key.age;
|
|
||||||
owner = config.systemd.services.grafana.serviceConfig.User;
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
pkgs-unstable,
|
pkgs-unstable,
|
||||||
config,
|
config,
|
||||||
inputs,
|
inputs,
|
||||||
lib,
|
|
||||||
...
|
...
|
||||||
}:
|
}:
|
||||||
let
|
let
|
||||||
@@ -19,7 +18,6 @@ let
|
|||||||
secrets = config.age.secrets;
|
secrets = config.age.secrets;
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
nixpkgs.config.allowUnfree = true;
|
|
||||||
imports = [
|
imports = [
|
||||||
./glance.nix
|
./glance.nix
|
||||||
];
|
];
|
||||||
@@ -120,7 +118,6 @@ in
|
|||||||
user = smtp.username;
|
user = smtp.username;
|
||||||
from_address = smtp.from;
|
from_address = smtp.from;
|
||||||
};
|
};
|
||||||
security.secret_key = "$__file{${secrets."grafana/secret-key".path}}";
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -136,21 +133,14 @@ in
|
|||||||
};
|
};
|
||||||
|
|
||||||
invidious = {
|
invidious = {
|
||||||
enable = true;
|
enable = false;
|
||||||
subdomain = "vid";
|
subdomain = "vid";
|
||||||
extraSettingsFile = secrets.invidious.path;
|
|
||||||
settings = {
|
|
||||||
invidious_companion = [
|
|
||||||
{ private_url = "http://apu:8282/companion"; }
|
|
||||||
];
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
syncthing = {
|
syncthing = {
|
||||||
enable = true;
|
enable = true;
|
||||||
subdomain = "sync";
|
subdomain = "sync";
|
||||||
dataDir = syncthingDataDir;
|
dataDir = syncthingDataDir;
|
||||||
configDir = "/var/lib/syncthing";
|
|
||||||
};
|
};
|
||||||
|
|
||||||
tailscale.enable = true;
|
tailscale.enable = true;
|
||||||
@@ -248,25 +238,7 @@ in
|
|||||||
|
|
||||||
mosquitto = {
|
mosquitto = {
|
||||||
enable = true;
|
enable = true;
|
||||||
subdomain = "mqtt";
|
|
||||||
listeners = [
|
listeners = [
|
||||||
{
|
|
||||||
users = {
|
|
||||||
homie = {
|
|
||||||
acl = [
|
|
||||||
"readwrite homie/#"
|
|
||||||
];
|
|
||||||
hashedPasswordFile = secrets."mosquitto/homie".path;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
port = 8084;
|
|
||||||
settings = {
|
|
||||||
protocol = "websockets";
|
|
||||||
cafile = "/run/credentials/mosquitto.service/fullchain.pem";
|
|
||||||
certfile = "/run/credentials/mosquitto.service/fullchain.pem";
|
|
||||||
keyfile = "/run/credentials/mosquitto.service/key.pem";
|
|
||||||
};
|
|
||||||
}
|
|
||||||
{
|
{
|
||||||
users = {
|
users = {
|
||||||
homie = {
|
homie = {
|
||||||
@@ -323,11 +295,99 @@ in
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
home-assistant = {
|
||||||
|
enable = false;
|
||||||
|
subdomain = "home";
|
||||||
|
config = {
|
||||||
|
homeassistant = {
|
||||||
|
name = "Koti";
|
||||||
|
unit_system = "metric";
|
||||||
|
time_zone = "Europe/Helsinki";
|
||||||
|
};
|
||||||
|
http = {
|
||||||
|
server_port = 8123;
|
||||||
|
use_x_forwarded_for = true;
|
||||||
|
trusted_proxies = [
|
||||||
|
"127.0.0.1"
|
||||||
|
"::1"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
mqtt = [
|
||||||
|
{
|
||||||
|
climate = {
|
||||||
|
unique_id = "nappula";
|
||||||
|
name = "Nappula";
|
||||||
|
current_humidity_topic = "homie/nappula/humidity/value";
|
||||||
|
current_humidity_template = "{{ value | float }}";
|
||||||
|
current_temperature_topic = "homie/nappula/temperature/value";
|
||||||
|
current_temperature_template = "{{ value | float }}";
|
||||||
|
mode_state_topic = "homie/nappula/ac/trigger";
|
||||||
|
mode_state_template = "{% if value == 'true' %}heat{% else %}off{% endif %}";
|
||||||
|
availability = {
|
||||||
|
topic = "homie/nappula/$online";
|
||||||
|
payload_available = "true";
|
||||||
|
payload_not_available = "false";
|
||||||
|
};
|
||||||
|
modes = [
|
||||||
|
"off"
|
||||||
|
"heat"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
{
|
||||||
|
button = {
|
||||||
|
unique_id = "nappula_button";
|
||||||
|
name = "Nappula anschalten";
|
||||||
|
command_topic = "homie/nappula/button/trigger/set";
|
||||||
|
payload_press = "true";
|
||||||
|
availability = {
|
||||||
|
topic = "homie/nappula/$online";
|
||||||
|
payload_available = "true";
|
||||||
|
payload_not_available = "false";
|
||||||
|
};
|
||||||
|
icon = "mdi:power";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
{
|
||||||
|
sensor = {
|
||||||
|
unique_id = "nappula_pressure";
|
||||||
|
name = "Luftdruck";
|
||||||
|
state_topic = "homie/nappula/pressure/value";
|
||||||
|
device_class = "atmospheric_pressure";
|
||||||
|
unit_of_measurement = "hPa";
|
||||||
|
state_class = "measurement";
|
||||||
|
value_template = "{{ value | float // 100 }}";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
extraComponents = [
|
||||||
|
"default_config"
|
||||||
|
"esphome"
|
||||||
|
"met"
|
||||||
|
"radio_browser"
|
||||||
|
"mqtt"
|
||||||
|
];
|
||||||
|
extraPackages = (
|
||||||
|
python3Packages: with python3Packages; [
|
||||||
|
paho-mqtt
|
||||||
|
]
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
weechat = {
|
weechat = {
|
||||||
enable = true;
|
enable = true;
|
||||||
subdomain = "irc";
|
subdomain = "irc";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
sillytavern = {
|
||||||
|
enable = true;
|
||||||
|
subdomain = "st";
|
||||||
|
listen = true;
|
||||||
|
whitelist = false;
|
||||||
|
port = 3100;
|
||||||
|
};
|
||||||
|
|
||||||
nginx.virtualHosts."isarepomaa.com" = {
|
nginx.virtualHosts."isarepomaa.com" = {
|
||||||
forceSSL = true;
|
forceSSL = true;
|
||||||
enableACME = true;
|
enableACME = true;
|
||||||
@@ -391,7 +451,7 @@ in
|
|||||||
labels = [
|
labels = [
|
||||||
"linux_arm64"
|
"linux_arm64"
|
||||||
"ubuntu-latest:docker://node:latest"
|
"ubuntu-latest:docker://node:latest"
|
||||||
"nixos-latest:docker://git.freun.dev/repomaa/nixos/node:latest"
|
"nixos-latest:docker://repomaa/node:latest"
|
||||||
];
|
];
|
||||||
tokenFile = secrets.gitea-actions-runner.path;
|
tokenFile = secrets.gitea-actions-runner.path;
|
||||||
url = "https://${config.services.gitea.subdomain}.${config.networking.domain}";
|
url = "https://${config.services.gitea.subdomain}.${config.networking.domain}";
|
||||||
@@ -399,73 +459,11 @@ in
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
searx = {
|
|
||||||
enable = true;
|
|
||||||
subdomain = "q";
|
|
||||||
port = 3400;
|
|
||||||
environmentFile = secrets.searx.path;
|
|
||||||
settings = {
|
|
||||||
general = {
|
|
||||||
instance_name = "freun.dev SearXNG";
|
|
||||||
};
|
|
||||||
server = {
|
|
||||||
public_instance = true;
|
|
||||||
image_proxy = true;
|
|
||||||
method = "GET";
|
|
||||||
secret_key = "$SEARX_SECRET_KEY";
|
|
||||||
};
|
|
||||||
engines = lib.mapAttrsToList (name: value: { inherit name; } // value) {
|
|
||||||
"google".disabled = true;
|
|
||||||
"duckduckgo".disabled = false;
|
|
||||||
"duckduckgo images".disabled = false;
|
|
||||||
"wolframalpha".disabled = false;
|
|
||||||
};
|
|
||||||
search = {
|
|
||||||
formats = [
|
|
||||||
"html"
|
|
||||||
"json"
|
|
||||||
];
|
|
||||||
};
|
|
||||||
enabled_plugins = [
|
|
||||||
"Basic Calculator"
|
|
||||||
"Hash plugin"
|
|
||||||
"Open Access DOI rewrite"
|
|
||||||
"Hostnames plugin"
|
|
||||||
"Unit converter plugin"
|
|
||||||
"Tracker URL remover"
|
|
||||||
];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
open-webui = {
|
|
||||||
enable = true;
|
|
||||||
port = 3500;
|
|
||||||
environmentFile = secrets.open-webui.path;
|
|
||||||
environment = {
|
|
||||||
ENABLE_WEB_SEARCH = "True";
|
|
||||||
ENABLE_OLLAMA_API = "False";
|
|
||||||
};
|
|
||||||
subdomain = "owu";
|
|
||||||
};
|
|
||||||
|
|
||||||
webserver = {
|
webserver = {
|
||||||
acme.dnsChallenge = true;
|
acme.dnsChallenge = true;
|
||||||
tailscaleAuth.expectedTailnet = "tempel-vibes.ts.net";
|
tailscaleAuth.expectedTailnet = "tempel-vibes.ts.net";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
virtualisation.oci-containers.containers.open-terminal = {
|
|
||||||
image = "ghcr.io/open-webui/open-terminal:latest";
|
|
||||||
autoStart = true;
|
|
||||||
ports = [ "127.0.0.1:3700:8000" ];
|
|
||||||
environmentFiles = [ config.age.secrets."open-terminal-api-key".path ];
|
|
||||||
environment = {
|
|
||||||
OPEN_TERMINAL_MULTI_USER = "true";
|
|
||||||
};
|
|
||||||
volumes = [
|
|
||||||
"open-terminal-data:/home/user"
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
virtualisation.docker.autoPrune.enable = true;
|
virtualisation.docker.autoPrune.enable = true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,14 +8,7 @@
|
|||||||
maxJobs = 8;
|
maxJobs = 8;
|
||||||
protocol = "ssh";
|
protocol = "ssh";
|
||||||
sshUser = "builder";
|
sshUser = "builder";
|
||||||
systems = [
|
system = "aarch64-linux";
|
||||||
"aarch64-linux"
|
|
||||||
];
|
|
||||||
supportedFeatures = [
|
|
||||||
"kvm"
|
|
||||||
"big-parallel"
|
|
||||||
"nixos-test"
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
settings = {
|
settings = {
|
||||||
|
|||||||
@@ -1,19 +1,11 @@
|
|||||||
{ pkgs-unstable, ... }:
|
{ ... }:
|
||||||
{
|
{
|
||||||
virtualisation = {
|
virtualisation.docker = {
|
||||||
containers = {
|
storageDriver = "btrfs";
|
||||||
enable = true;
|
|
||||||
storage.settings.storage.driver = "btrfs";
|
|
||||||
containersConf.settings.compose_warning_logs = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
oci-containers.backend = "podman";
|
|
||||||
|
|
||||||
podman = {
|
|
||||||
enable = true;
|
enable = true;
|
||||||
autoPrune.enable = true;
|
autoPrune.enable = true;
|
||||||
defaultNetwork.settings.dns_enabled = true;
|
|
||||||
dockerCompat = true;
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
users.users.jokke.extraGroups = [ "docker" ];
|
||||||
|
users.users.moco.extraGroups = [ "docker" ];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,30 +1,15 @@
|
|||||||
{ lib, pkgs-unstable, ... }:
|
{ ... }:
|
||||||
{
|
{
|
||||||
services.displayManager.gdm.enable = true;
|
services.displayManager.gdm.enable = true;
|
||||||
services.desktopManager.gnome.enable = true;
|
services.desktopManager.gnome.enable = true;
|
||||||
services.displayManager.sessionPackages = [ pkgs-unstable.niri ];
|
|
||||||
|
|
||||||
services.printing.enable = true;
|
services.printing.enable = true;
|
||||||
services.keyd = {
|
|
||||||
enable = true;
|
|
||||||
keyboards.default = {
|
|
||||||
ids = [ "*" ];
|
|
||||||
settings = {
|
|
||||||
global = {
|
|
||||||
overload_tap_timeout = 200;
|
|
||||||
};
|
|
||||||
main = {
|
|
||||||
leftmeta = "overload(meta, macro(leftmeta+z))";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
security.rtkit.enable = true;
|
security.rtkit.enable = true;
|
||||||
services.pipewire = {
|
services.pipewire = {
|
||||||
enable = true;
|
enable = true;
|
||||||
alsa.enable = true;
|
alsa.enable = true;
|
||||||
alsa.support32Bit = lib.mkForce false;
|
alsa.support32Bit = true;
|
||||||
pulse.enable = true;
|
pulse.enable = true;
|
||||||
jack.enable = true;
|
jack.enable = true;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -69,15 +69,9 @@
|
|||||||
enable = true;
|
enable = true;
|
||||||
extraPackages = with pkgs; [ rocmPackages.clr.icd ];
|
extraPackages = with pkgs; [ rocmPackages.clr.icd ];
|
||||||
};
|
};
|
||||||
services.resolved = {
|
|
||||||
enable = true;
|
|
||||||
dnsovertls = "opportunistic";
|
|
||||||
dnssec = "allow-downgrade";
|
|
||||||
};
|
|
||||||
networking.networkmanager = {
|
networking.networkmanager = {
|
||||||
enable = true;
|
enable = true;
|
||||||
wifi.backend = "iwd";
|
wifi.backend = "iwd";
|
||||||
dns = "systemd-resolved";
|
|
||||||
plugins = with pkgs; [
|
plugins = with pkgs; [
|
||||||
networkmanager-openvpn
|
networkmanager-openvpn
|
||||||
];
|
];
|
||||||
@@ -92,50 +86,4 @@
|
|||||||
}
|
}
|
||||||
];
|
];
|
||||||
networking.firewall.allowedUDPPorts = [ 5353 ];
|
networking.firewall.allowedUDPPorts = [ 5353 ];
|
||||||
|
|
||||||
networking.wg-quick.interfaces = lib.mkMerge (
|
|
||||||
lib.map
|
|
||||||
(
|
|
||||||
{
|
|
||||||
region,
|
|
||||||
ipOctet,
|
|
||||||
publicKey,
|
|
||||||
}:
|
|
||||||
{
|
|
||||||
"everii-${region}" = {
|
|
||||||
address = [ "10.${toString ipOctet}.8.48/22" ];
|
|
||||||
dns = [ "10.${toString ipOctet}.1.1" ];
|
|
||||||
privateKeyFile = config.age.secrets."everii-vpn/${region}.key".path;
|
|
||||||
peers = [
|
|
||||||
{
|
|
||||||
allowedIPs = [ "10.${toString ipOctet}.0.0/16" ];
|
|
||||||
endpoint = "vpn.${region}.infra.everii.io:51821";
|
|
||||||
inherit publicKey;
|
|
||||||
}
|
|
||||||
];
|
|
||||||
postUp = ''
|
|
||||||
${pkgs.systemd}/bin/resolvectl domain everii-${region} ~${region}.everii ~${toString ipOctet}.10.in-addr.arpa
|
|
||||||
${pkgs.systemd}/bin/resolvectl dnssec everii-${region} no
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
}
|
|
||||||
)
|
|
||||||
[
|
|
||||||
{
|
|
||||||
region = "de1";
|
|
||||||
ipOctet = 13;
|
|
||||||
publicKey = "uBUgSTZb6WbfE960S3qFP/UUMtdsgNWqtkTaBkp6Xxo=";
|
|
||||||
}
|
|
||||||
{
|
|
||||||
region = "fi1";
|
|
||||||
ipOctet = 14;
|
|
||||||
publicKey = "Yoakl0lrL6IK1nT8x5SGpaS39fQxRAsP9Zjpu8/1RRs=";
|
|
||||||
}
|
|
||||||
{
|
|
||||||
region = "ch1";
|
|
||||||
ipOctet = 15;
|
|
||||||
publicKey = "gfciqAk+X02zoEKXSvRamx5+TGL3i4GpT7oUvHMD0xo=";
|
|
||||||
}
|
|
||||||
]
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,8 +33,6 @@ in
|
|||||||
nerd-fonts.iosevka-term
|
nerd-fonts.iosevka-term
|
||||||
];
|
];
|
||||||
services = {
|
services = {
|
||||||
gnome.evolution-data-server.enable = true;
|
|
||||||
flatpak.enable = true;
|
|
||||||
pcscd = {
|
pcscd = {
|
||||||
enable = true;
|
enable = true;
|
||||||
plugins = [ pkgs.ccid ];
|
plugins = [ pkgs.ccid ];
|
||||||
@@ -53,12 +51,10 @@ in
|
|||||||
|
|
||||||
ollama = {
|
ollama = {
|
||||||
enable = true;
|
enable = true;
|
||||||
package = pkgs-unstable.ollama-vulkan;
|
acceleration = "rocm";
|
||||||
syncModels = true;
|
environmentVariables = {
|
||||||
loadModels = [
|
HSA_OVERRIDE_GFX_VERSION = "11.0.3";
|
||||||
"qwen3:4b-instruct"
|
};
|
||||||
"qwen3:8b"
|
|
||||||
];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
borgbackup.jobs.root = {
|
borgbackup.jobs.root = {
|
||||||
@@ -143,31 +139,4 @@ in
|
|||||||
environment.etc."1password/custom_allowed_browsers".text = ''
|
environment.etc."1password/custom_allowed_browsers".text = ''
|
||||||
vivaldi
|
vivaldi
|
||||||
'';
|
'';
|
||||||
|
|
||||||
systemd.services.ollama-keep-alive =
|
|
||||||
let
|
|
||||||
ollamaURL = "http://localhost:${toString config.services.ollama.port}/api/generate";
|
|
||||||
payload = {
|
|
||||||
model = lib.elemAt config.services.ollama.loadModels 0;
|
|
||||||
keep_alive = -1;
|
|
||||||
};
|
|
||||||
in
|
|
||||||
{
|
|
||||||
enable = true;
|
|
||||||
description = "Keep Ollama primary model loaded by pinging it";
|
|
||||||
after = [
|
|
||||||
"ollama.service"
|
|
||||||
"network-online.target"
|
|
||||||
];
|
|
||||||
wants = [ "network-online.target" ];
|
|
||||||
bindsTo = [ "ollama.service" ];
|
|
||||||
wantedBy = [
|
|
||||||
"multi-user.target"
|
|
||||||
"ollama.service"
|
|
||||||
];
|
|
||||||
serviceConfig = {
|
|
||||||
Type = "oneshot";
|
|
||||||
ExecStart = "${pkgs.curl}/bin/curl -s '${ollamaURL}' -d '${builtins.toJSON payload}'";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,9 +10,6 @@
|
|||||||
})
|
})
|
||||||
[
|
[
|
||||||
"borgbackup-radish"
|
"borgbackup-radish"
|
||||||
"everii-vpn/de1.key"
|
|
||||||
"everii-vpn/ch1.key"
|
|
||||||
"everii-vpn/fi1.key"
|
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,10 +4,7 @@
|
|||||||
users.users.jokke = {
|
users.users.jokke = {
|
||||||
uid = 1001;
|
uid = 1001;
|
||||||
isNormalUser = true;
|
isNormalUser = true;
|
||||||
extraGroups = [
|
extraGroups = [ "wheel" ];
|
||||||
"wheel"
|
|
||||||
"networkmanager"
|
|
||||||
];
|
|
||||||
shell = pkgs.zsh;
|
shell = pkgs.zsh;
|
||||||
initialPassword = "changeme";
|
initialPassword = "changeme";
|
||||||
};
|
};
|
||||||
@@ -15,10 +12,7 @@
|
|||||||
users.users.moco = {
|
users.users.moco = {
|
||||||
uid = 1000;
|
uid = 1000;
|
||||||
isNormalUser = true;
|
isNormalUser = true;
|
||||||
extraGroups = [
|
extraGroups = [ "wheel" ];
|
||||||
"wheel"
|
|
||||||
"networkmanager"
|
|
||||||
];
|
|
||||||
shell = pkgs.zsh;
|
shell = pkgs.zsh;
|
||||||
initialPassword = "changeme";
|
initialPassword = "changeme";
|
||||||
subUidRanges = [
|
subUidRanges = [
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
|
|
||||||
{
|
{
|
||||||
pkgs,
|
pkgs,
|
||||||
lib,
|
|
||||||
ssh,
|
ssh,
|
||||||
config,
|
config,
|
||||||
inputs,
|
inputs,
|
||||||
@@ -58,10 +57,10 @@ in
|
|||||||
# Select internationalisation properties.
|
# Select internationalisation properties.
|
||||||
i18n.defaultLocale = "en_US.UTF-8";
|
i18n.defaultLocale = "en_US.UTF-8";
|
||||||
|
|
||||||
# Define a user account. Don't forget to set a password with 'passwd'.
|
# Define a user account. Don't forget to set a password with ‘passwd’.
|
||||||
users.users.jokke = {
|
users.users.jokke = {
|
||||||
isNormalUser = true;
|
isNormalUser = true;
|
||||||
extraGroups = [ "wheel" ]; # Enable 'sudo' for the user.
|
extraGroups = [ "wheel" ]; # Enable ‘sudo’ for the user.
|
||||||
packages = [ pkgs.nh ];
|
packages = [ pkgs.nh ];
|
||||||
openssh.authorizedKeys.keys = [ ssh.publicKeys.yubikey ];
|
openssh.authorizedKeys.keys = [ ssh.publicKeys.yubikey ];
|
||||||
initialPassword = "changeme";
|
initialPassword = "changeme";
|
||||||
@@ -75,7 +74,7 @@ in
|
|||||||
wget
|
wget
|
||||||
htop
|
htop
|
||||||
git
|
git
|
||||||
alsa-utils
|
inputs.turny.packages.${stdenv.hostPlatform.system}.default
|
||||||
];
|
];
|
||||||
|
|
||||||
# Enable the OpenSSH daemon.
|
# Enable the OpenSSH daemon.
|
||||||
@@ -89,159 +88,11 @@ in
|
|||||||
allInterfaces = [ "ssh" ];
|
allInterfaces = [ "ssh" ];
|
||||||
};
|
};
|
||||||
|
|
||||||
# Enable audio (ALSA)
|
|
||||||
hardware.alsa.enable = true;
|
|
||||||
|
|
||||||
# Complete config.txt settings: boot-critical u-boot lines plus SPI enable.
|
|
||||||
# The configtxt module generates the full config.txt, overriding the
|
|
||||||
# hardcoded one in sd-image-aarch64.nix which ignores these settings.
|
|
||||||
hardware.raspberry-pi.configtxt.settings = {
|
|
||||||
pi3 = {
|
|
||||||
kernel = "u-boot-rpi3.bin";
|
|
||||||
core_freq = 250;
|
|
||||||
};
|
|
||||||
pi02 = {
|
|
||||||
kernel = "u-boot-rpi3.bin";
|
|
||||||
};
|
|
||||||
pi4 = {
|
|
||||||
kernel = "u-boot-rpi4.bin";
|
|
||||||
enable_gic = true;
|
|
||||||
armstub = "armstub8-gic.bin";
|
|
||||||
};
|
|
||||||
all = {
|
|
||||||
arm_64bit = true;
|
|
||||||
enable_uart = true;
|
|
||||||
avoid_warnings = true;
|
|
||||||
dtparam = [
|
|
||||||
"audio=on"
|
|
||||||
"spi=on"
|
|
||||||
];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
# Use the configtxt module's generated config.txt when building SD images
|
|
||||||
sdImage.populateFirmwareCommands = let
|
|
||||||
inherit (config.hardware.raspberry-pi.configtxt) file;
|
|
||||||
in ''
|
|
||||||
(cd ${pkgs.raspberrypifw}/share/raspberrypi/boot && cp bootcode.bin fixup*.dat start*.elf $NIX_BUILD_TOP/firmware/)
|
|
||||||
|
|
||||||
cp ${file} firmware/config.txt
|
|
||||||
|
|
||||||
cp ${pkgs.ubootRaspberryPi3_64bit}/u-boot.bin firmware/u-boot-rpi3.bin
|
|
||||||
cp ${pkgs.raspberrypifw}/share/raspberrypi/boot/bcm2710-rpi-2-b.dtb firmware/
|
|
||||||
cp ${pkgs.raspberrypifw}/share/raspberrypi/boot/bcm2710-rpi-3-b.dtb firmware/
|
|
||||||
cp ${pkgs.raspberrypifw}/share/raspberrypi/boot/bcm2710-rpi-3-b-plus.dtb firmware/
|
|
||||||
cp ${pkgs.raspberrypifw}/share/raspberrypi/boot/bcm2710-rpi-cm3.dtb firmware/
|
|
||||||
cp ${pkgs.raspberrypifw}/share/raspberrypi/boot/bcm2710-rpi-zero-2.dtb firmware/
|
|
||||||
cp ${pkgs.raspberrypifw}/share/raspberrypi/boot/bcm2710-rpi-zero-2-w.dtb firmware/
|
|
||||||
|
|
||||||
cp ${pkgs.ubootRaspberryPi4_64bit}/u-boot.bin firmware/u-boot-rpi4.bin
|
|
||||||
cp ${pkgs.raspberrypi-armstubs}/armstub8-gic.bin firmware/armstub8-gic.bin
|
|
||||||
cp ${pkgs.raspberrypifw}/share/raspberrypi/boot/bcm2711-rpi-4-b.dtb firmware/
|
|
||||||
cp ${pkgs.raspberrypifw}/share/raspberrypi/boot/bcm2711-rpi-400.dtb firmware/
|
|
||||||
cp ${pkgs.raspberrypifw}/share/raspberrypi/boot/bcm2711-rpi-cm4.dtb firmware/
|
|
||||||
cp ${pkgs.raspberrypifw}/share/raspberrypi/boot/bcm2711-rpi-cm4s.dtb firmware/
|
|
||||||
'';
|
|
||||||
|
|
||||||
# Load SPI and GPIO kernel modules so /dev/spidev* and /dev/gpiomem exist
|
|
||||||
boot.kernelModules = [
|
|
||||||
"spi_bcm2835"
|
|
||||||
"bcm2835_gpiomem"
|
|
||||||
];
|
|
||||||
|
|
||||||
# Use the firmware-passed device tree (with dtparam/dtoverlay applied)
|
|
||||||
# instead of u-boot loading a fresh DTB from FDTDIR
|
|
||||||
boot.loader.generic-extlinux-compatible.useGenerationDeviceTree = false;
|
|
||||||
|
|
||||||
# Mount the firmware partition so config.txt is accessible and can be
|
|
||||||
# updated on deploys. The sd-image module defaults to noauto; we need it
|
|
||||||
# mounted so the activation script can sync config.txt.
|
|
||||||
fileSystems."/boot/firmware".options = [
|
|
||||||
"nofail"
|
|
||||||
"defaults"
|
|
||||||
];
|
|
||||||
|
|
||||||
# Sync the generated config.txt to the firmware partition on each deploy
|
|
||||||
# so dtparam/dtoverlay changes take effect without reflashing the SD card.
|
|
||||||
system.activationScripts.syncConfigTxt = {
|
|
||||||
text = ''
|
|
||||||
if mountpoint -q /boot/firmware; then
|
|
||||||
cp -f ${config.hardware.raspberry-pi.configtxt.file} /boot/firmware/config.txt
|
|
||||||
fi
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
# Turny service
|
|
||||||
systemd.services.turny = {
|
|
||||||
description = "Turny Spotify RFID Controller";
|
|
||||||
documentation = [ "https://github.com/repomaa/turny" ];
|
|
||||||
after = [
|
|
||||||
"network.target"
|
|
||||||
"sound.target"
|
|
||||||
];
|
|
||||||
wants = [ "network.target" ];
|
|
||||||
wantedBy = [ "multi-user.target" ];
|
|
||||||
serviceConfig = {
|
|
||||||
Type = "simple";
|
|
||||||
ExecStart = lib.getExe inputs.turny.packages.${pkgs.stdenv.hostPlatform.system}.default;
|
|
||||||
User = "turny";
|
|
||||||
Group = "turny";
|
|
||||||
WorkingDirectory = "/var/lib/turny";
|
|
||||||
Restart = "always";
|
|
||||||
RestartSec = 10;
|
|
||||||
RestartPreventExitStatus = 23;
|
|
||||||
TimeoutStopSec = 10;
|
|
||||||
KillSignal = "SIGINT";
|
|
||||||
NoNewPrivileges = true;
|
|
||||||
PrivateTmp = true;
|
|
||||||
ProtectSystem = "strict";
|
|
||||||
ProtectHome = true;
|
|
||||||
ReadWritePaths = [ "/var/lib/turny" ];
|
|
||||||
ProtectKernelTunables = true;
|
|
||||||
ProtectKernelModules = true;
|
|
||||||
ProtectControlGroups = true;
|
|
||||||
LimitNOFILE = 1024;
|
|
||||||
MemoryAccounting = true;
|
|
||||||
MemoryMax = "100M";
|
|
||||||
StandardOutput = "journal";
|
|
||||||
StandardError = "journal";
|
|
||||||
SyslogIdentifier = "turny";
|
|
||||||
};
|
|
||||||
environment = {
|
|
||||||
RUST_LOG = "info,turny=debug";
|
|
||||||
RUST_BACKTRACE = "1";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
users.users.turny = {
|
|
||||||
isSystemUser = true;
|
|
||||||
group = "turny";
|
|
||||||
extraGroups = [
|
|
||||||
"gpio"
|
|
||||||
"spi"
|
|
||||||
"audio"
|
|
||||||
];
|
|
||||||
home = "/var/lib/turny";
|
|
||||||
createHome = true;
|
|
||||||
};
|
|
||||||
users.groups.turny = { };
|
|
||||||
users.groups.gpio = { };
|
|
||||||
users.groups.spi = { };
|
|
||||||
|
|
||||||
# Udev rules for GPIO and SPI access
|
|
||||||
services.udev.extraRules = ''
|
|
||||||
SUBSYSTEM=="gpio", GROUP="gpio", MODE="0660"
|
|
||||||
SUBSYSTEM=="gpiomem", GROUP="gpio", MODE="0660"
|
|
||||||
SUBSYSTEM=="spidev", GROUP="spi", MODE="0660"
|
|
||||||
'';
|
|
||||||
|
|
||||||
# This value determines the NixOS release from which the default
|
# This value determines the NixOS release from which the default
|
||||||
# settings for stateful data, like user locations and database versions
|
# settings for stateful data, like file locations and database versions
|
||||||
# on the system were taken. It's perfectly fine and recommended to leave
|
# on your system were taken. It's perfectly fine and recommended to leave
|
||||||
# this value at the release version of the first install of this system.
|
# this value at the release version of the first install of this system.
|
||||||
# Before changing this value read the documentation for this option
|
# Before changing this value read the documentation for this option
|
||||||
# (e.g. man configuration.nix or on https://nixos.org/nixos/options.html).
|
# (e.g. man configuration.nix or on https://nixos.org/nixos/options.html).
|
||||||
system.stateVersion = "25.05"; # Did you read the comment?
|
system.stateVersion = "25.05"; # Did you read the comment?
|
||||||
|
|
||||||
sdImage.compressImage = false;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIM3To9DeTGzwiQN+uqNWZXKzGG1BJnLvYRgLm0/iI92Y turny
|
|
||||||
@@ -11,7 +11,6 @@
|
|||||||
./vlans.nix
|
./vlans.nix
|
||||||
./firewall.nix
|
./firewall.nix
|
||||||
./storage-box-mounts.nix
|
./storage-box-mounts.nix
|
||||||
./temp-build-storage.nix
|
|
||||||
./services
|
./services
|
||||||
inputs.agenix.nixosModules.default
|
inputs.agenix.nixosModules.default
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -46,18 +46,7 @@ in
|
|||||||
certificate_chain_path = "/run/credentials/adguardhome.service/fullchain.pem";
|
certificate_chain_path = "/run/credentials/adguardhome.service/fullchain.pem";
|
||||||
private_key_path = "/run/credentials/adguardhome.service/key.pem";
|
private_key_path = "/run/credentials/adguardhome.service/key.pem";
|
||||||
};
|
};
|
||||||
dns = {
|
dns.bind_hosts = tailscaleIps;
|
||||||
bind_hosts = tailscaleIps;
|
|
||||||
upstream_dns = [
|
|
||||||
"https://dns10.quad9.net/dns-query"
|
|
||||||
"https://cloudflare-dns.com/dns-query"
|
|
||||||
"https://dns.google/dns-query"
|
|
||||||
];
|
|
||||||
fallback_dns = [
|
|
||||||
"https://cloudflare-dns.com/dns-query"
|
|
||||||
"https://dns.google/dns-query"
|
|
||||||
];
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -26,13 +26,11 @@
|
|||||||
./weechat.nix
|
./weechat.nix
|
||||||
./hledger-web.nix
|
./hledger-web.nix
|
||||||
./glance.nix
|
./glance.nix
|
||||||
|
./sillytavern.nix
|
||||||
./nqptp.nix
|
./nqptp.nix
|
||||||
./actual.nix
|
./actual.nix
|
||||||
./voidauth.nix
|
./voidauth.nix
|
||||||
./gitea.nix
|
./gitea.nix
|
||||||
./dhcp-dns-sync
|
./dhcp-dns-sync
|
||||||
./invidious-companion.nix
|
|
||||||
./searx.nix
|
|
||||||
./open-webui.nix
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,11 +6,6 @@
|
|||||||
}:
|
}:
|
||||||
let
|
let
|
||||||
cfg = config.modules.services.dhcp-dns-sync;
|
cfg = config.modules.services.dhcp-dns-sync;
|
||||||
ownAddress = (
|
|
||||||
lib.elemAt (lib.splitString "/"
|
|
||||||
config.systemd.network.networks."30-${cfg.interface}".networkConfig.Address
|
|
||||||
) 0
|
|
||||||
);
|
|
||||||
|
|
||||||
dhcp-leases-to-unbound =
|
dhcp-leases-to-unbound =
|
||||||
pkgs.runCommand "dhcp-leases-to-unbound"
|
pkgs.runCommand "dhcp-leases-to-unbound"
|
||||||
@@ -64,10 +59,9 @@ in
|
|||||||
users.groups.dhcp-dns-sync = { };
|
users.groups.dhcp-dns-sync = { };
|
||||||
|
|
||||||
# Ensure directories and files exist with proper permissions
|
# Ensure directories and files exist with proper permissions
|
||||||
# Directory needs to be group-writable for unbound group
|
|
||||||
systemd.tmpfiles.rules = [
|
systemd.tmpfiles.rules = [
|
||||||
"d /var/lib/unbound 0775 unbound unbound -"
|
"d /var/lib/unbound 0755 unbound unbound -"
|
||||||
"f ${cfg.unboundConfigPath} 0644 dhcp-dns-sync unbound -"
|
"f ${cfg.unboundConfigPath} 0644 dhcp-dns-sync dhcp-dns-sync -"
|
||||||
];
|
];
|
||||||
|
|
||||||
# Extend Unbound configuration to include generated file
|
# Extend Unbound configuration to include generated file
|
||||||
@@ -75,8 +69,6 @@ in
|
|||||||
server = {
|
server = {
|
||||||
local-zone = [ "${cfg.domain}. static" ];
|
local-zone = [ "${cfg.domain}. static" ];
|
||||||
include = cfg.unboundConfigPath;
|
include = cfg.unboundConfigPath;
|
||||||
local-data = [ ''"apu.home.arpa. IN A ${ownAddress}"'' ];
|
|
||||||
local-data-ptr = [ ''"${ownAddress} apu.home.arpa."'' ];
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -96,7 +88,7 @@ in
|
|||||||
serviceConfig = {
|
serviceConfig = {
|
||||||
Type = "oneshot";
|
Type = "oneshot";
|
||||||
User = "dhcp-dns-sync";
|
User = "dhcp-dns-sync";
|
||||||
Group = "unbound";
|
Group = "dhcp-dns-sync";
|
||||||
# Allow access to networkctl via D-Bus
|
# Allow access to networkctl via D-Bus
|
||||||
SupplementaryGroups = [ "systemd-network" ];
|
SupplementaryGroups = [ "systemd-network" ];
|
||||||
# Read/write paths
|
# Read/write paths
|
||||||
|
|||||||
@@ -58,6 +58,13 @@ def sanitize_hostname(hostname : String) : String?
|
|||||||
sanitized
|
sanitized
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def reverse_ptr(ip : String) : String?
|
||||||
|
parts = ip.split('.')
|
||||||
|
return nil unless parts.size == 4
|
||||||
|
|
||||||
|
"#{parts[3]}.#{parts[2]}.#{parts[1]}.#{parts[0]}.in-addr.arpa."
|
||||||
|
end
|
||||||
|
|
||||||
def generate_unbound_config(leases : Array(Lease), domain : String) : String
|
def generate_unbound_config(leases : Array(Lease), domain : String) : String
|
||||||
lines = [] of String
|
lines = [] of String
|
||||||
|
|
||||||
@@ -75,25 +82,19 @@ def generate_unbound_config(leases : Array(Lease), domain : String) : String
|
|||||||
# A record
|
# A record
|
||||||
lines << %{local-data: "#{fqdn} IN A #{lease.address}"}
|
lines << %{local-data: "#{fqdn} IN A #{lease.address}"}
|
||||||
|
|
||||||
# PTR record - local-data-ptr expects IP in normal form, unbound reverses it
|
# PTR record
|
||||||
lines << %{local-data-ptr: "#{lease.address} #{fqdn}"}
|
if ptr = reverse_ptr(lease.address)
|
||||||
|
lines << %{local-data-ptr: "#{ptr} #{fqdn}"}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
lines.join("\n") + "\n"
|
lines.join("\n") + "\n"
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_leases(interface : String, networkctl_path : String? = nil) : Array(Lease)
|
def get_leases(interface : String, networkctl_path : String? = nil) : Array(Lease)
|
||||||
cmd = networkctl_path ? "#{networkctl_path}" : "networkctl"
|
cmd = networkctl_path ? "#{networkctl_path} status #{interface} --json=short" : "networkctl status #{interface} --json=short"
|
||||||
args = ["status", interface, "--json=short"]
|
output = `#{cmd}`
|
||||||
|
raise "networkctl failed (exit code #{$?.exit_status}): #{output}" unless $?.success?
|
||||||
process = Process.new(cmd, args, output: Process::Redirect::Pipe, error: Process::Redirect::Pipe)
|
|
||||||
output = process.output.gets_to_end
|
|
||||||
error = process.error.gets_to_end
|
|
||||||
result = process.wait
|
|
||||||
|
|
||||||
unless result.success?
|
|
||||||
raise "networkctl failed (exit code #{result.exit_code}): #{error.empty? ? output : error}"
|
|
||||||
end
|
|
||||||
|
|
||||||
status = NetworkStatus.from_json(output)
|
status = NetworkStatus.from_json(output)
|
||||||
status.dhcp_server.try(&.leases) || [] of Lease
|
status.dhcp_server.try(&.leases) || [] of Lease
|
||||||
@@ -150,18 +151,13 @@ OptionParser.parse do |parser|
|
|||||||
end
|
end
|
||||||
|
|
||||||
def reload_unbound(unbound_control_path : String?)
|
def reload_unbound(unbound_control_path : String?)
|
||||||
cmd = unbound_control_path ? "#{unbound_control_path}" : "unbound-control"
|
cmd = unbound_control_path ? "#{unbound_control_path} reload" : "unbound-control reload"
|
||||||
puts "Reloading Unbound..."
|
puts "Reloading Unbound..."
|
||||||
|
result = system(cmd)
|
||||||
process = Process.new(cmd, ["reload"], output: Process::Redirect::Pipe, error: Process::Redirect::Pipe)
|
unless result
|
||||||
error = process.error.gets_to_end
|
# Fallback to systemctl
|
||||||
result = process.wait
|
system("systemctl reload unbound")
|
||||||
|
|
||||||
unless result.success?
|
|
||||||
raise "unbound reload failed (exit code #{result.exit_code}): #{error}"
|
|
||||||
end
|
end
|
||||||
|
|
||||||
puts "Unbound reloaded successfully."
|
|
||||||
end
|
end
|
||||||
|
|
||||||
begin
|
begin
|
||||||
|
|||||||
@@ -37,9 +37,6 @@ let
|
|||||||
};
|
};
|
||||||
|
|
||||||
immutableEnvironment = {
|
immutableEnvironment = {
|
||||||
HOME = "/var/lib/dnote";
|
|
||||||
XDG_DATA_HOME = "/var/lib/dnote";
|
|
||||||
XDG_CONFIG_HOME = "/var/lib/dnote";
|
|
||||||
GO_ENV = "PRODUCTION";
|
GO_ENV = "PRODUCTION";
|
||||||
OnPremises = true;
|
OnPremises = true;
|
||||||
DBHost = "/var/run/postgresql";
|
DBHost = "/var/run/postgresql";
|
||||||
@@ -93,10 +90,26 @@ in
|
|||||||
after = [ "postgresql.service" ];
|
after = [ "postgresql.service" ];
|
||||||
requires = [ "postgresql.service" ];
|
requires = [ "postgresql.service" ];
|
||||||
serviceConfig = {
|
serviceConfig = {
|
||||||
ExecStart = "${pkgs-unstable.dnote}/bin/dnote-server start --port ${toString cfg.port}";
|
ExecStart = "${pkgs-unstable.dnote}/bin/dnote-server -port ${toString cfg.port} start";
|
||||||
EnvironmentFile = lib.mkIf (cfg.environmentFile != null) cfg.environmentFile;
|
EnvironmentFile = lib.mkIf (cfg.environmentFile != null) cfg.environmentFile;
|
||||||
User = cfg.user;
|
User = cfg.user;
|
||||||
StateDirectory = "dnote";
|
BindPaths = [
|
||||||
|
"/var/run/postgresql"
|
||||||
|
];
|
||||||
|
BindReadOnlyPaths = [
|
||||||
|
/run/systemd/resolve/stub-resolv.conf
|
||||||
|
/etc/ssl
|
||||||
|
/etc/static/ssl
|
||||||
|
/etc/resolv.conf
|
||||||
|
/etc/static/resolv.conf
|
||||||
|
/etc/nsswitch.conf
|
||||||
|
/etc/static/nsswitch.conf
|
||||||
|
/etc/hosts
|
||||||
|
];
|
||||||
|
};
|
||||||
|
confinement = {
|
||||||
|
enable = true;
|
||||||
|
packages = [ pkgs.cacert ];
|
||||||
};
|
};
|
||||||
wantedBy = [ "multi-user.target" ];
|
wantedBy = [ "multi-user.target" ];
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -46,10 +46,7 @@ in
|
|||||||
services = {
|
services = {
|
||||||
webserver = {
|
webserver = {
|
||||||
enable = lib.mkDefault true;
|
enable = lib.mkDefault true;
|
||||||
vHosts.${fqdn}.locations."/" = {
|
vHosts.${fqdn}.locations."/".proxyPort = 3008;
|
||||||
proxyPort = 3008;
|
|
||||||
extraConfig = "client_max_body_size 0;";
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
postgresql = {
|
postgresql = {
|
||||||
enable = lib.mkDefault true;
|
enable = lib.mkDefault true;
|
||||||
|
|||||||
@@ -1,99 +0,0 @@
|
|||||||
{
|
|
||||||
config,
|
|
||||||
lib,
|
|
||||||
pkgs,
|
|
||||||
...
|
|
||||||
}:
|
|
||||||
let
|
|
||||||
cfg = config.services.invidious-companion;
|
|
||||||
companionRelease = "release-master";
|
|
||||||
hostPlatform = pkgs.stdenv.hostPlatform.system;
|
|
||||||
|
|
||||||
# Invidious Companion package - fetches binary release and patches for NixOS
|
|
||||||
unwrappedCompanion = pkgs.stdenv.mkDerivation {
|
|
||||||
pname = "unwrapped-invidious-companion";
|
|
||||||
version = companionRelease;
|
|
||||||
|
|
||||||
src =
|
|
||||||
let
|
|
||||||
archMap = {
|
|
||||||
x86_64-linux = "x86_64-unknown-linux-gnu";
|
|
||||||
aarch64-linux = "aarch64-unknown-linux-gnu";
|
|
||||||
};
|
|
||||||
platform = archMap.${hostPlatform} or (throw "Unsupported platform: ${hostPlatform}");
|
|
||||||
in
|
|
||||||
pkgs.fetchzip {
|
|
||||||
url = "https://github.com/iv-org/invidious-companion/releases/download/${companionRelease}/invidious_companion-${platform}.tar.gz";
|
|
||||||
sha256 = cfg.binaryHash;
|
|
||||||
};
|
|
||||||
|
|
||||||
dontStrip = true;
|
|
||||||
dontPatchELF = true;
|
|
||||||
|
|
||||||
installPhase = ''
|
|
||||||
mkdir -p $out/bin
|
|
||||||
cp invidious_companion $out/bin/invidious_companion
|
|
||||||
chmod +x $out/bin/invidious_companion
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
invidiousCompanion = pkgs.buildFHSEnv {
|
|
||||||
name = "invidious-companion";
|
|
||||||
targetPkgs = pkgs: [ unwrappedCompanion ];
|
|
||||||
runScript = "invidious_companion";
|
|
||||||
meta = {
|
|
||||||
description = "Invidious companion for handling video streams";
|
|
||||||
homepage = "https://github.com/iv-org/invidious-companion";
|
|
||||||
license = lib.licenses.agpl3Only;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
in
|
|
||||||
{
|
|
||||||
options.services.invidious-companion = {
|
|
||||||
enable = lib.mkEnableOption "Enable Invidious Companion service";
|
|
||||||
host = lib.mkOption {
|
|
||||||
type = lib.types.str;
|
|
||||||
default = "localhost";
|
|
||||||
};
|
|
||||||
port = lib.mkOption {
|
|
||||||
type = lib.types.port;
|
|
||||||
default = 8282;
|
|
||||||
description = "Port for Invidious Companion to listen on";
|
|
||||||
};
|
|
||||||
secretKeyFile = lib.mkOption {
|
|
||||||
type = lib.types.str;
|
|
||||||
description = "Path to file containing the companion secret key";
|
|
||||||
};
|
|
||||||
binaryHash = lib.mkOption {
|
|
||||||
type = lib.types.nullOr lib.types.str;
|
|
||||||
default = null;
|
|
||||||
description = "SHA256 hash of the invidious companion binary release";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
config = lib.mkIf cfg.enable {
|
|
||||||
systemd.services.invidious-companion = {
|
|
||||||
description = "Invidious Companion - video stream handler";
|
|
||||||
wantedBy = [ "multi-user.target" ];
|
|
||||||
after = [ "network.target" ];
|
|
||||||
|
|
||||||
serviceConfig = {
|
|
||||||
Type = "simple";
|
|
||||||
User = "invidious";
|
|
||||||
Group = "invidious";
|
|
||||||
DynamicUser = true;
|
|
||||||
ExecStart = lib.getExe invidiousCompanion;
|
|
||||||
Environment = [
|
|
||||||
"HOST=${cfg.host}"
|
|
||||||
"PORT=${toString cfg.port}"
|
|
||||||
"TMPDIR=/var/cache/invidious-companion"
|
|
||||||
];
|
|
||||||
EnvironmentFile = [ cfg.secretKeyFile ];
|
|
||||||
CacheDirectory = "invidious-companion";
|
|
||||||
WorkingDirectory = "%C/invidious-companion";
|
|
||||||
Restart = "always";
|
|
||||||
RestartSec = 5;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,8 +1,4 @@
|
|||||||
{
|
{ config, lib, ... }:
|
||||||
config,
|
|
||||||
lib,
|
|
||||||
...
|
|
||||||
}:
|
|
||||||
let
|
let
|
||||||
cfg = config.services.invidious;
|
cfg = config.services.invidious;
|
||||||
fqdn = "${cfg.subdomain}.${config.networking.domain}";
|
fqdn = "${cfg.subdomain}.${config.networking.domain}";
|
||||||
@@ -36,14 +32,5 @@ in
|
|||||||
vHosts.${fqdn}.locations."/".proxyPort = cfg.port;
|
vHosts.${fqdn}.locations."/".proxyPort = cfg.port;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
systemd.services.invidious.serviceConfig.DynamicUser = lib.mkForce false;
|
|
||||||
|
|
||||||
users.groups.invidious = { };
|
|
||||||
users.users.invidious = {
|
|
||||||
isSystemUser = true;
|
|
||||||
group = "invidious";
|
|
||||||
description = "Invidious user";
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,39 +1,14 @@
|
|||||||
{
|
{ config, lib, ... }:
|
||||||
config,
|
|
||||||
lib,
|
|
||||||
...
|
|
||||||
}:
|
|
||||||
let
|
let
|
||||||
cfg = config.services.mosquitto;
|
cfg = config.services.mosquitto;
|
||||||
fqdn = "${cfg.subdomain}.${config.networking.domain}";
|
|
||||||
acme = config.security.acme;
|
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
options = {
|
options = {
|
||||||
services.mosquitto = {
|
services.mosquitto = {
|
||||||
openFirewall = lib.mkEnableOption "Open firewall port for Mosquitto";
|
openFirewall = lib.mkEnableOption "Open firewall port for Mosquitto";
|
||||||
subdomain = lib.mkOption {
|
|
||||||
type = lib.types.nullOr lib.types.str;
|
|
||||||
default = null;
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
config = lib.mkIf cfg.enable {
|
||||||
|
networking.firewall.allowedTCPPorts = map ({ port, ... }: port) cfg.listeners;
|
||||||
};
|
};
|
||||||
config = lib.mkIf cfg.enable (
|
|
||||||
lib.mkMerge [
|
|
||||||
{ networking.firewall.allowedTCPPorts = map ({ port, ... }: port) cfg.listeners; }
|
|
||||||
(lib.mkIf (cfg.subdomain != null) {
|
|
||||||
security.acme.certs.${fqdn}.postRun = ''
|
|
||||||
systemctl restart mosquitto
|
|
||||||
'';
|
|
||||||
|
|
||||||
systemd.services.mosquitto = {
|
|
||||||
requires = [ "acme-${fqdn}.service" ];
|
|
||||||
serviceConfig.LoadCredential = [
|
|
||||||
"fullchain.pem:${acme.certs.${fqdn}.directory}/fullchain.pem"
|
|
||||||
"key.pem:${acme.certs.${fqdn}.directory}/key.pem"
|
|
||||||
];
|
|
||||||
};
|
|
||||||
})
|
|
||||||
]
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -204,7 +204,6 @@ let
|
|||||||
hetzner = {
|
hetzner = {
|
||||||
class = "octodns_hetzner.HetznerProvider";
|
class = "octodns_hetzner.HetznerProvider";
|
||||||
token = "env/HETZNER_API_TOKEN";
|
token = "env/HETZNER_API_TOKEN";
|
||||||
backend = "hcloud";
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,78 +0,0 @@
|
|||||||
{
|
|
||||||
lib,
|
|
||||||
config,
|
|
||||||
pkgs-unstable,
|
|
||||||
...
|
|
||||||
}:
|
|
||||||
let
|
|
||||||
cfg = config.services.open-webui;
|
|
||||||
fqdn = "${cfg.subdomain}.${config.networking.domain}";
|
|
||||||
|
|
||||||
open-webui-pkg = pkgs-unstable.open-webui.overridePythonAttrs (oldAttrs: {
|
|
||||||
dependencies =
|
|
||||||
oldAttrs.dependencies
|
|
||||||
++ (with pkgs-unstable.python3Packages; [
|
|
||||||
pgvector
|
|
||||||
psycopg2
|
|
||||||
])
|
|
||||||
++ [
|
|
||||||
pkgs-unstable.ffmpeg
|
|
||||||
];
|
|
||||||
});
|
|
||||||
in
|
|
||||||
{
|
|
||||||
options.services.open-webui = {
|
|
||||||
subdomain = lib.mkOption {
|
|
||||||
type = lib.types.str;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
config = lib.mkIf cfg.enable {
|
|
||||||
services = {
|
|
||||||
open-webui = {
|
|
||||||
package = open-webui-pkg;
|
|
||||||
environment = {
|
|
||||||
ANONYMIZED_TELEMETRY = "False";
|
|
||||||
DO_NOT_TRACK = "True";
|
|
||||||
SCARF_NO_ANALYTICS = "True";
|
|
||||||
WEBUI_URL = "https://${fqdn}";
|
|
||||||
VECTOR_DB = "pgvector";
|
|
||||||
PGVECTOR_CREATE_EXTENSION = "False";
|
|
||||||
DATABASE_URL = "postgresql:///open-webui?host=/var/run/postgresql";
|
|
||||||
CORS_ALLOW_ORIGIN = "https://${fqdn};http://localhost";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
webserver.vHosts.${fqdn}.locations."/".proxyPort = cfg.port;
|
|
||||||
postgresql = {
|
|
||||||
enable = lib.mkDefault true;
|
|
||||||
ensureDatabases = [ "open-webui" ];
|
|
||||||
ensureUsers = [
|
|
||||||
{
|
|
||||||
name = "open-webui";
|
|
||||||
ensureDBOwnership = true;
|
|
||||||
}
|
|
||||||
];
|
|
||||||
extensions = ps: with ps; [ pgvector ];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
systemd.services.open-webui-pgvector-setup = {
|
|
||||||
description = "Ensure pgvector extension exists for open-webui";
|
|
||||||
wantedBy = [ "open-webui.service" ];
|
|
||||||
before = [ "open-webui.service" ];
|
|
||||||
after = [
|
|
||||||
"postgresql.service"
|
|
||||||
"postgresql-setup.service"
|
|
||||||
];
|
|
||||||
requires = [ "postgresql.service" ];
|
|
||||||
serviceConfig = {
|
|
||||||
Type = "oneshot";
|
|
||||||
User = "postgres";
|
|
||||||
Group = "postgres";
|
|
||||||
ExecStart = "${config.services.postgresql.package}/bin/psql -d open-webui -c 'CREATE EXTENSION IF NOT EXISTS vector;'";
|
|
||||||
RemainAfterExit = true;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
{
|
|
||||||
pkgs-unstable,
|
|
||||||
config,
|
|
||||||
lib,
|
|
||||||
inputs,
|
|
||||||
...
|
|
||||||
}:
|
|
||||||
let
|
|
||||||
cfg = config.services.searx;
|
|
||||||
fqdn = "${cfg.subdomain}.${config.networking.domain}";
|
|
||||||
in
|
|
||||||
{
|
|
||||||
disabledModules = [ "services/networking/searx.nix" ];
|
|
||||||
|
|
||||||
imports = [
|
|
||||||
"${inputs.nixpkgs-unstable}/nixos/modules/services/networking/searx.nix"
|
|
||||||
];
|
|
||||||
|
|
||||||
options.services.searx = {
|
|
||||||
port = lib.mkOption {
|
|
||||||
type = lib.types.int;
|
|
||||||
};
|
|
||||||
subdomain = lib.mkOption {
|
|
||||||
type = lib.types.str;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
config = lib.mkIf cfg.enable {
|
|
||||||
services.searx = {
|
|
||||||
redisCreateLocally = true;
|
|
||||||
package = pkgs-unstable.searxng;
|
|
||||||
limiterSettings = {
|
|
||||||
botdetection = {
|
|
||||||
ipv4_prefix = 32;
|
|
||||||
ipv6_prefix = 56;
|
|
||||||
ip_limit = {
|
|
||||||
filter_link_local = true;
|
|
||||||
link_token = false;
|
|
||||||
};
|
|
||||||
ip_lists = {
|
|
||||||
pass_ip = [
|
|
||||||
"127.0.0.1/32"
|
|
||||||
"::1"
|
|
||||||
];
|
|
||||||
};
|
|
||||||
trusted_proxies = [
|
|
||||||
"127.0.0.0/8"
|
|
||||||
"::1"
|
|
||||||
];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
settings = {
|
|
||||||
server = {
|
|
||||||
bind_address = "localhost";
|
|
||||||
port = cfg.port;
|
|
||||||
limiter = true;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
services.webserver.vHosts.${fqdn}.locations."/".proxyPort = cfg.port;
|
|
||||||
|
|
||||||
# searx expects limiter.toml in the same directory as settings.yml (/run/searx)
|
|
||||||
systemd.services.searx-init.script = lib.mkAfter ''
|
|
||||||
ln -sf /etc/searxng/limiter.toml /run/searx/limiter.toml
|
|
||||||
'';
|
|
||||||
|
|
||||||
users.groups.searx.members = [ "nginx" ];
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
cfg = config.services.sillytavern;
|
||||||
|
fqdn = "${cfg.subdomain}.${config.networking.domain}";
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options.services.sillytavern = {
|
||||||
|
subdomain = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
default = "sillytavern";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = lib.mkIf cfg.enable {
|
||||||
|
services.webserver = {
|
||||||
|
enable = lib.mkDefault true;
|
||||||
|
vHosts.${fqdn} = {
|
||||||
|
tailscaleAuth = true;
|
||||||
|
proxyBuffering = false;
|
||||||
|
locations."/".proxyPort = cfg.port;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -27,14 +27,5 @@ in
|
|||||||
vHosts.${fqdn}.locations."/".proxyPort = cfg.port;
|
vHosts.${fqdn}.locations."/".proxyPort = cfg.port;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
systemd.services.syncthing.serviceConfig =
|
|
||||||
lib.mkIf
|
|
||||||
(
|
|
||||||
cfg.dataDir == "/var/lib/syncthing" || cfg.dataDir == null || cfg.configDir == "/var/lib/syncthing"
|
|
||||||
)
|
|
||||||
{
|
|
||||||
StateDirectory = "syncthing";
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,38 +0,0 @@
|
|||||||
{
|
|
||||||
lib,
|
|
||||||
config,
|
|
||||||
...
|
|
||||||
}:
|
|
||||||
let
|
|
||||||
cfg = config.modules.tempBuildStorage;
|
|
||||||
in
|
|
||||||
{
|
|
||||||
options.modules.tempBuildStorage = {
|
|
||||||
enable = lib.mkEnableOption "temporary build storage volume";
|
|
||||||
|
|
||||||
device = lib.mkOption {
|
|
||||||
type = lib.types.str;
|
|
||||||
default = "/dev/sdb";
|
|
||||||
description = "Block device to mount for temporary nix build storage.";
|
|
||||||
};
|
|
||||||
|
|
||||||
mountPoint = lib.mkOption {
|
|
||||||
type = lib.types.str;
|
|
||||||
default = "/mnt/build";
|
|
||||||
description = "Mount point for the temporary build storage.";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
config = lib.mkIf cfg.enable {
|
|
||||||
fileSystems.${cfg.mountPoint} = {
|
|
||||||
device = cfg.device;
|
|
||||||
fsType = "ext4";
|
|
||||||
options = [
|
|
||||||
"nofail"
|
|
||||||
"defaults"
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
nix.settings.build-dir = cfg.mountPoint;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
+9
-8
@@ -1,9 +1,10 @@
|
|||||||
age-encryption.org/v1
|
age-encryption.org/v1
|
||||||
-> ssh-ed25519 osOCZA tKc6OuybMhchwMgREDpacwxz/IlBCOOD9OQp/UMLBys
|
-> ssh-ed25519 osOCZA OPYYw6SBBoMNoJ/DfIyvxLVpZo0n0v08uhrPa2Uk/xw
|
||||||
7mOmTzMUnLYnQPHOWwIiZ4vFyPxQpwzPmnQMuWsQWfA
|
b9mSzuzgznPl5qu73kJie2PgJGIlxEOR/MSNzp2TZ2A
|
||||||
-> ssh-ed25519 DFiohQ +ysb8W20OD73awl7we7/XfiX5q8sRtMlerr5r4gavTA
|
-> ssh-ed25519 DFiohQ rYD5ueKrIAbtphPZe7BxyHNPdRGCV0o4G3lXGGHFmD0
|
||||||
5OHmlJHoVRXho2Swe/XDTtQKn0pW9PH1Ky4rvhR7o/8
|
caGATSGVUaJkTDIHDk40oJnNh8oNRRQYTpKn4jET72c
|
||||||
-> ssh-ed25519 PT7ffg 0vvEHkjNJFH/UUcwX8O+F0StVBqNkQNp7u2qasHZLCE
|
-> ssh-ed25519 PT7ffg 2HVnNQxY5BubUZGTuwcOHMKDZ7O1f+gji9d2mquLECs
|
||||||
xKySMrX3T59dCKEUHyR/cdT+8KCFdCerUyUg7r7ZBYQ
|
rJzRjFokTzDkn/fjA1eXaZdISEXWtGqLSsYpUYEd0qw
|
||||||
--- ielXPF0xxxqSrBc1yeOxLdz50ZjCLnZJ45Wsg84yPso
|
--- U6CH4TvqpEfQg4VuQ3fr7gcqBojVEV2r4+NPfMEVxbA
|
||||||
×S;ëqø»äá û˜…Å@ðŒ±S,a¾¬ÛÜH3;(¥¡%o#‘ýx$Qgw”žãÊ>ô�ias °ù»%fjr¼ÊÜ^cïrô©´Ñ‚â°`ä—e›¼níû_�–w›×¶çõvÓCØÅ‡x}|µFnÿjݹ°²À ß
|
q5šòú4ÍÝMêÿ�—Ô–³ÏEòx'í’6éu�§¤eìëŠàÜh‚4D;óN¿ƒÔ<:P¡)Š."CÞ›èˆÿ
|
||||||
|
xgóv˜/ãN%W2™¥ÙPâQÖdA½VêÉ—üœ^_B|Íëío׌àkf¦ÛŠá›WÞ~ ³ú̪’ϽևÄÖDñõfo
|
||||||
Binary file not shown.
@@ -1,9 +1,9 @@
|
|||||||
age-encryption.org/v1
|
age-encryption.org/v1
|
||||||
-> ssh-ed25519 osOCZA IY+dU1oe5ps4gli4bqRVdIaKaOWfEieIX0b/8q6+Pys
|
-> ssh-ed25519 osOCZA wDdoFnPs/OUczxmB6WgzSQ0HtguSmIsX1di6gH5guFM
|
||||||
L5Bkf3dTH9k0HUYrrTvLq5hvS7CK1nJp1mJumBlVGb8
|
b6PAgnY1Kki92V+ky+/Cuq5UezYdchS3g5MtqtRxDMQ
|
||||||
-> ssh-ed25519 DFiohQ BjbGXY9CmrAEcv4+hk+A1AIP06NeP4NhymTeByfxi3c
|
-> ssh-ed25519 DFiohQ oZmlLt1Em4PnIZAUwqxed5EH1ZG/Bkm3FPiZI9uxVUY
|
||||||
94qpDg6nEowmkNUzeDGtNw8VgT8vsWR4y28/lMyfZmc
|
/dAEjEHp9mcygfYj+taalhqjJ6ywkWu45ymwnmtlzaY
|
||||||
-> ssh-ed25519 hRPDBg 7Lq1rjiz1FhgFkWaZJaLR24k+HAKLraw9YjVsWPspVE
|
-> ssh-ed25519 hRPDBg MKpKkBgl0baf6qJh940ekh1z/0bKbzYTBb9In2yVd3Q
|
||||||
3eBnzsN96eLmIlu9MNiBhv41csudd4WQUF5Q9vpGkj0
|
1uX/3y+A/a+vxSl41DlUnZVdh6lpDYFwunzLV3DRblk
|
||||||
--- l10qNh0gcX57lTZsiTsuzwhVGnKM5hH5+ZbjI9yfr/Q
|
--- KQncWqjni5UN+D3I1OSWdKxVf5/UiLBUNBwFMG/1+mg
|
||||||
ûK¿;n},ijæ6uîȧMŤ'ülÿÓÂò–?ÜÜå'„MtÙš.¢ßšžô.I‹Qó³öy)&|=¨½@1ãmSRD"&&áWUgið}'ˆ¹¶¥�
|
Š�öôó\ÄXã±zÐÿÌ®œ¬Û\~.¼©ûfå•w@÷␍IµÂÂÀqvE²‡µïÓÕæÄ÷žÇiMÇ›ûE×€„fçÒ9Çj< ÇY¾`œÔÎ<ÿ ã›]e
|
||||||
+8
-8
@@ -1,9 +1,9 @@
|
|||||||
age-encryption.org/v1
|
age-encryption.org/v1
|
||||||
-> ssh-ed25519 osOCZA /DM/QULRCY6cCkuqi0h+PVXW+lFcvUwTaNI4vVHoG3w
|
-> ssh-ed25519 osOCZA UCilO75OGmYABIVNjQ7JKdObSSC882S6blT35S8XXBE
|
||||||
f/U+q+Y6zoIcg0ttN+JgFc0GqXKWV08nmWUeeeC+b5k
|
CEhIzJeE0Xa9jY1uboPVM1uK+U8N77e6pGsK3SCNBqY
|
||||||
-> ssh-ed25519 DFiohQ kgrVGGqMrBn8W1trnR116ODUHTeDR60BLnWfqANr/QA
|
-> ssh-ed25519 DFiohQ aKyq1IPwKvgQniPEDt9tIw6KLeQePctFPY8wnSmcREk
|
||||||
ys8CLK1XHkOQvp3xlcQkQ3me2a2/Nu94o5L9swWhsAs
|
3OQH5iOGFbnJaUXn8JmMDItPfPjXUmuO1OFYw31ftGA
|
||||||
-> ssh-ed25519 PT7ffg 3jg8xsvEsjZ8Gzap1Q0T5cZpi/lmk8/DuE1Zsk7ByGY
|
-> ssh-ed25519 PT7ffg 1XzR+XsNLJgf0itst++aN4GWpiUjPvlCPR/KcZzLNT4
|
||||||
gfrFh5M2GWWa+5IaO7yL8LRlMThRWt/XdZIYDXxo8zU
|
Na1BKsjIoFgFbQI4LVppxW53yAQgxQfz5r31HrTNRLI
|
||||||
--- U2Rbi2kHkOIW8AErxfFCUwf2HayX+J4QjKVz/4aGn9k
|
--- U8B89Fp3JNPBX18P5p3lVLYIz5CsbxldkvnjZ2Cx4+8
|
||||||
¤c Ý“øÉ�‘ôºœ2kž7ˆÿ)ýí,h~Ó1!¦ÓÁjaÍé`ÊVz Æ-␍n}<ÎØºKä+Rrw¿’Wð†Æ®3éCRªòV©yÙ{
|
º“¨3z®#<›Kî*ŽwÁMs!Ó2¼Ê�bqz>‰&€~u«¹‡Êi𧾘tYv§¾ÁþítÂøÒvÀ¬2Ïd)gfÇÃÙÔ�
|
||||||
Binary file not shown.
@@ -1,9 +0,0 @@
|
|||||||
age-encryption.org/v1
|
|
||||||
-> ssh-ed25519 osOCZA q6tH0FVLeK0IAAlBAQfpQJScvePOK3+TSZ38NAfQHWo
|
|
||||||
63rJwFLyI3TRkjR8j7Ek4ELE4Se+alHCl1tO5ujmzMA
|
|
||||||
-> ssh-ed25519 DFiohQ fc1aSau4DbXn8XzqgAFYbgw1T3U3IMva/RpUTqlBR2Y
|
|
||||||
G+qY9wIDOvyOWmQnNmnaasdlFVUGR6cBWpK2Zce5LHM
|
|
||||||
-> ssh-ed25519 hRPDBg FT7EbwtMJF9hjfcCc2PqWmRoYYFWkCvQ06DbVfpOg1Y
|
|
||||||
8j8Rwfmu+tckffMKMQ0h3/un09mUCi7/hymgqjSNz8U
|
|
||||||
--- C03Wle22666W4khdQVHeC9eP+ZJ8AcP3dB+jfCHpeP0
|
|
||||||
öÚ†µi•lÎ|G~¸DUô2í¶�åÐóä"׆ÎÁ¸}j,Ì\|w,°–0ÜO5�ζ¹?~•"ÊÃÖy»fl¢nËõÍÆT
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
age-encryption.org/v1
|
|
||||||
-> ssh-ed25519 osOCZA ylc8vGyAOgdlAWSFGDQ7rjIoPCXtCgTdjyq7jiJLtF4
|
|
||||||
HG7y2EyaHKB9664gfH3Q+YxL2ArAEKIIlwtC8YKtHh8
|
|
||||||
-> ssh-ed25519 DFiohQ XPjQk3y/Dmsh8mHqUD3oLdRR9drDB29sCRjoaYlbGkw
|
|
||||||
BfGPDs5n1IoYgAZzEgRnmvhxqvY+BrYLVASJOR/szeI
|
|
||||||
-> ssh-ed25519 hRPDBg s1AkS3NGpW8yAQwXqx9UgHc2SaiA+AcE4vCbYlHENH0
|
|
||||||
e9bhxPNYUct/dQEVirOkX1A+ulPiE1PHGh8M6qrM67k
|
|
||||||
--- Ov/CzZKVSYIKK4Z4W5duNQd/Bu9tkMwXhPXhzW9KOjk
|
|
||||||
žã«1ÿkVyôG||8ÊÚD„dÙÓòk*ŽÅhɽ~{»Ð1T�,¾?ht%Þ!ƃ§°ýÎÜbÍ·µË¼�ßtÚ
|
|
||||||
ø5ÀH{ÿä
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
age-encryption.org/v1
|
|
||||||
-> ssh-ed25519 osOCZA KJyp6aDTicchEwD0n3htOSM/F7YD/Fn3pVG71Gi3/0s
|
|
||||||
kcfL1a3T0dfsJ2/kdHjRf1fDuTaQbcMpcYPAs5BcPQA
|
|
||||||
-> ssh-ed25519 DFiohQ PTdr7UeBsPa/M9ygKNxIBrno/a62AgDrn+jV8pWA7UE
|
|
||||||
V1YGZVaxnarJmtBIFPuB1l/GYApJlrE2fVERJEH+yng
|
|
||||||
-> ssh-ed25519 hRPDBg HhuQcUjwuqeRGuHxEL+/TLjEnwmQsXo8oaBvRYgLES8
|
|
||||||
t9KoYWdtMugNwCRETs72y2PR5xvN9Vi3wyj0lndd0vE
|
|
||||||
--- GlYbPI0bPreYxtImgTl/lZPOY+0DKDruuqZ/jTmWhgA
|
|
||||||
ÍÓt³öH5 ¢>Â*'únæÂ0x£·2A Є=%¶_´ó+”0zØÃI ©qŘo=>Üã␍¬æžM �‹¾e!ÙÜ‚j<žÁak
|
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,9 +1,9 @@
|
|||||||
age-encryption.org/v1
|
age-encryption.org/v1
|
||||||
-> ssh-ed25519 osOCZA X80qdaNMdIz7sy+tLmTYlati/SKucc03gW5FXE9/xlM
|
-> ssh-ed25519 osOCZA Y7ieflWxf67Jik747FnOu5aKcDjh6hd8OWE88oX7gWc
|
||||||
5ZvMFxd3MOl4SyBEYR9xawjREi8UwEmW9DyuCC+E8jk
|
aQavFEvcW30cer2DutpkfSkKk1NqGzPiP2bYL2MCv+Q
|
||||||
-> ssh-ed25519 DFiohQ NPghTlceQTcZh4VGASPeFZy8G2VLURxykc0JjvAdW2s
|
-> ssh-ed25519 DFiohQ J5HO9n4KTQ6cSBGHxrBHORvmYp4WYJZiATekbZvhuEU
|
||||||
MkIN+sOI3yD8UzSmHOabeVnFHfXDDWMSWwFIuHGJGFE
|
QGAhwn6P5ByiNY8bMUI07hHvnA8diVI56Vg+6kVEJb0
|
||||||
-> ssh-ed25519 PT7ffg mc6TmlrgBEBQeOECY6SzHaLhpi4SrYc/9m9nml/2GGY
|
-> ssh-ed25519 PT7ffg RpcpdzFtX5VyodX3lXKvjMRxMlpasRqqpjqCacXDcy4
|
||||||
VDNZtQSSGG1+/84Y5TcyiJytoAoPCyj9PlYV7WZiKE8
|
VJQuB8YfhB0DQ4TjwzBhdtl35eyFVLLW8O87mCuH828
|
||||||
--- GPLaSoO3+AZXfY9gIweM+dDAb+hBSadz++DqYEkR2ug
|
--- qJnfQgOyw0CTBhXNSyIw3O33av2cjPxTntLsOW/bevA
|
||||||
;ܬl‘öj…Ãå˜æ?Ã§ÚøÆQ²}©„NÁìû/ªº)à·[xØö%‹F!¼0µ†ª
|
-åž␍8^$-IŒ4EJæ…5R�²<ÃI_²–Ò,Ý#í¹S¡FY¥�Éò¯ì2gðuïË
|
||||||
@@ -1,9 +1,10 @@
|
|||||||
age-encryption.org/v1
|
age-encryption.org/v1
|
||||||
-> ssh-ed25519 osOCZA t6j6GnCmxIYZKfYZ+dthOAaJqjHtbsIZSOYaonDvDxo
|
-> ssh-ed25519 osOCZA y1DrZWN4jrO6xg7TkFqrjTT/k4L49IAZVf/NLw3jMiM
|
||||||
KWhOeO95ExUHgcHtm5BYwZnG4EOMZpcKB7h/UM7yxZ8
|
BB+Aq/80SFQpLJfZxRKFPBw9ylHb+Y6LLuZQAqG3WdQ
|
||||||
-> ssh-ed25519 DFiohQ XlRVp8vYYbCbrCGIYpXLsewZkceQJRqUlegTnAp4gSo
|
-> ssh-ed25519 DFiohQ jPPM8ATBvlXn0F/9pDCsZB15yW+af0b2FQqV2gI8YyQ
|
||||||
4yu21bl6xEodz/lVBV/imCresblG89uyz8fzWhSjBJ8
|
8yJjKEvEGKDwtvBWP/yjiDql+HA6l5q+RfEgP8cAIGM
|
||||||
-> ssh-ed25519 PT7ffg UN3WxuZZrVq4xG/fOYdfyMBBvqO5aKLF+0DZ+8SYjFk
|
-> ssh-ed25519 PT7ffg +R5KlrnH2gGW2Qc3V+RgYKtAXerbRB5j898+b41/VAU
|
||||||
DSGXD5GeYKIwT/GLG2n16660x3R4JASnzPI0vBvi1kw
|
7Lq3GE9CMGeXAmsf9WgSiJf/o+HyssF4xz94JNjbEgM
|
||||||
--- mobBb6GglqgnpHy9IaTX6FwjIuv+g31FpC0V4tKJ9mg
|
--- k2xNMbWr5tntlbOOYLbt6DsWsW2mT/P3pg3IgXYcM58
|
||||||
ábwøysqéõfØÖ½Çøâ÷ùE§KvYt&²1ÿ�WzÙì«ÊZ+ɳ×rœ¼ ”o·‹ƒŠG
|
|ìÍѰm7Æy‰z¡¥R©ÔíYê+qî§ü—1>ž‡Y?hHÖžÌÌÈ|ù7àÞþ̃
|
||||||
|
N¥zÚöØäëøð
|
||||||
+10
-8
@@ -1,9 +1,11 @@
|
|||||||
age-encryption.org/v1
|
age-encryption.org/v1
|
||||||
-> ssh-ed25519 osOCZA OKLTpDP78xbkZ2uxMaJtOIVCd3/jbAV98E9yFGg0SHk
|
-> ssh-ed25519 osOCZA 77EDoJKwYm80SsOFMaTeLVKyb60mpVafClV0oDfhnm0
|
||||||
1RwoL09ZccJhYWJxy/JgMxJSHc74RsKD22L3c13BARo
|
gZ4EtoDbmI2iggyAooqWtd0Yhh5kebvR8vnHGz0Er6Y
|
||||||
-> ssh-ed25519 DFiohQ us/uB+O7gBPPJ3ewzn1Vpkc59obSQcwSORFIhPsw0Wo
|
-> ssh-ed25519 DFiohQ dYdiyYPtjCH8YFpj4bAwTDSEB+0JjzDIz1jwRPDjAFg
|
||||||
dW/6eaPCKI0Z59PPtoNiOWoaNPNFqy1WPhI2y+AXXok
|
NAdMoMfREupERqZ8t2lu6++Qjgq7LVIo5wsXabXKWU8
|
||||||
-> ssh-ed25519 PT7ffg Shf65NGS/njVgmsKYzH0G+R3Lbul24ov1YX0D8yCVlE
|
-> ssh-ed25519 PT7ffg 6NFOq021rnHwv7BFIw9in6uQR39hgh5qVuPNvxtCd3k
|
||||||
+V8lgVMK4tIIvjWXDRSK15Ko4lf9dBbd3BVnLm4zl7Y
|
4UK6DCVbSi4yugpY9nLBSoZ7SHLkF3MMIeHVKYH3bkQ
|
||||||
--- YLhHK7p69aBksuFUcoRIfh9xej8HsQcDgrCUEyw4x64
|
--- 1BNBapkorRybzDSfmHCQjfkN0ss0GXnZGv99Ni/O/VM
|
||||||
#\)V×¹ç{÷ËÆ½OøHK1lNæ(ˆß*ÒQ ^\aÉ�z(Ì?A»ý©·(#$Õè3Q” ·„¸£�K ã…p"I‘s)¼‡‹;½É1%
|
wp3IvÄ"ßc}úÀ™W,0VÀÄæÂ�mØû±ù´˜ƒv¹„¯j’ªP#„”$Ž”›f¹ÜÌ
|
||||||
|
v9x["ú¶c ‡y²
|
||||||
|
~ .€[†/ko'z
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+9
-9
@@ -1,10 +1,10 @@
|
|||||||
age-encryption.org/v1
|
age-encryption.org/v1
|
||||||
-> ssh-ed25519 osOCZA RXa84TEMDWGN5boMxdkSqkSYADuFtdOLiPRG7l4NfQ4
|
-> ssh-ed25519 osOCZA 8VhILVP+borbW+8iFTyZTPlRAsdmoIUVa8DUWjrz/RI
|
||||||
5ZKIoAUvvXDQmQBqIlD8Hk/jU447XHzZr681Yz/Vpqw
|
E9SbMJ+Tz9p4O2z33AOKhpKVla8u+O39nO35hclR6oo
|
||||||
-> ssh-ed25519 DFiohQ cgSbRK5wdZQc3Ns4czyUgvir5oQXbdAP6dzpbp0v2nI
|
-> ssh-ed25519 DFiohQ +JeoeaQ62VmfBNN2wDEDGINspEK+e4YWNfjR7eJ7RAg
|
||||||
iTujuKOCwK8ajM6AMyXx1TC/jCHFByG0ts9wSxDDNMw
|
KM4/0TfoOk1+CqXkx55uUbaylCFgNwTHRaTiPdCYmCI
|
||||||
-> ssh-ed25519 PT7ffg hOQSv4qpPf2UYAmoJse4oIco+Pehe2IBJsfmNM3jWgQ
|
-> ssh-ed25519 PT7ffg AcIAWujVGx4ndNfqbp1wtDsHp5rHTTnz2gy+LLSzxEE
|
||||||
yC2/4stjYH5mqklHiSL/oDzx3JJ+9f98PaWFFtODJ2s
|
wDsg4PytTFf2wY71BCaDYMpaHxo3UqjXC93SSqmBhlY
|
||||||
--- pCBHnbEEEgTwsGfiD21wq83WAjdw4T335TIJwuiqVIs
|
--- U6Gn7J2Lh8Z83lYmHUEOgbbyDDEJqxSr/Kw2dcwhVgo
|
||||||
^�±¥·Ì[ÐZImV ␍÷§_åÇÜþzÉËù@‚\•1;©üdŸ]ñiø¬d ƒ?Ýnb¶AVœj#ân¶ä¿åG—LÍQdŸ
|
wqø(0÷™Êºz†¤Œ:J(qè¨=ݨ©xô},øÏrG´•�Ët‡•uâ‘…“Ε"d„6Ù¢²
|
||||||
½}§O§�ö.°¤Ž–�ïÛv›©-¤¦
|
¥.U€øÑ¥bg'ÌzZ»8¯í«öšAZv–Bø‡.3sX[`#\ùÅ.;J
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
age-encryption.org/v1
|
|
||||||
-> ssh-ed25519 osOCZA MFFVCO90Gx4QdHymLlxU4yjRdJr3JQR1Bvvjz57+D34
|
|
||||||
xgGfp/ECiRGiWyp8sAjriHvy9b7GsiHQMCKYVHl1CSA
|
|
||||||
-> ssh-ed25519 DFiohQ A5iL8qI5AcaXUmpplE5J0xQIWAryxM1E82VwWc2DhGo
|
|
||||||
1dv2EqTkz6Z4HswQOYU5Q79Be0GZvc4HUyyqHWthtPk
|
|
||||||
-> ssh-ed25519 wU682A qPkbSVvvQKJI0wcw7syZfKl7ZxTQpZdCwyT4CwjnbFw
|
|
||||||
eT/8Z3Ei3/1oi0MEuqiBN/e9Rg8r3zRlUm3vtigl8cE
|
|
||||||
--- 3hc/9smt2J3sIJLMDC/QByjE8bqlcNjR7FE9NBgOQp8
|
|
||||||
¾>IÑY
|
|
||||||
7wgö¼
|
|
||||||
;ÏÇyî,øpå43@B3F‚;k(¼\ÊN&àÔÿÑÇý4Q•8Iúž‡MÿÁg!ÃîÜÚ£³
|
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,9 +1,9 @@
|
|||||||
age-encryption.org/v1
|
age-encryption.org/v1
|
||||||
-> ssh-ed25519 osOCZA xdLLgxnXF6YwP6PA158Jk5PLd9DqMdD7iWzgxcig5ks
|
-> ssh-ed25519 osOCZA eWLRpIyRno4qtjjwpXxlwsW4I5a59h+c8W4mJpb7rmg
|
||||||
mmjqlmU/6MyTaLUD63nyubvr+62SWik0a5wnlhuLQXY
|
i1LmqRoWZ8wB1EYxNvtqoMSr1lqGbcHHqyAPK1Ldy3Y
|
||||||
-> ssh-ed25519 DFiohQ BbxdetgH/z1ZUmzdKYLYFbSPx9y6vhqwZmMwlwRuCWM
|
-> ssh-ed25519 DFiohQ Wn2NMzQBdv6KsZnBUj82FGo3FdOcyZqd1A+KkQy5G1w
|
||||||
IpNVv3/EtML29RT9Ca8zrWM8gnGfk2iweW5RnXrTm94
|
ZCrFCEeikNUmG1pO/f0wy7GzTzwCYoNhQBTeofmo98g
|
||||||
-> ssh-ed25519 PT7ffg f2BPj7TYxAnf1Os/VgMmxQ8g7DIhMdohUUe5y97WK1A
|
-> ssh-ed25519 PT7ffg RnEdUTw4G7dVL/YWr5vls5IEf1BbrdBCjgk+ZTABlQo
|
||||||
zmNAbvgrV3tYQ8ZylZZ2P8v5FW7kklQt7XIkPqeIY9k
|
G2PEFcmClmcd8Ap6L4VEipULRZuGj3izzeB0l/cI6FU
|
||||||
--- 5RXgn5pqVJqKRAiZ9aA5uVL83P7fJHAs98R/iUC3tLs
|
--- +Jmqn5CDZN3jaexEWZzZvuKvxjZfXfEdyUW3cQIIsnQ
|
||||||
Ÿòb÷q§ÇA‰3ÇÒDâ”&kâK±Lï‰,û§|¤(�Àn8·žH‚'Q-µùJ$”ãÐZ°Ò�ïÓµ␍²�ûÀ¾ b;ˆ¾ññ Ñt®ç?Eÿ.±Ï=š�’E`åy£ÀcOËAc�ÍUt¨A.Úºýsº57¢²—CÖMÖemš‡÷˜ö ô
|
ƒ`‡ù€Úô�ŒÅ¬8\ÃRØYoç"¾Øó2ö/˜<[ÿ>ÈuÀyÿÉW¥ÝÞ�”`Ç® <¨™V¨Ø-úÌdÇBª*“ï±)n�(‘oB7sñÕs—À¢à’)aE)ÆÐ…4‹½ŠÎ(îÂmQ¹Fy·xj'„:õ�wX,QµV”ìÈö
|
||||||
@@ -1,10 +1,9 @@
|
|||||||
age-encryption.org/v1
|
age-encryption.org/v1
|
||||||
-> ssh-ed25519 osOCZA kxEAkm22oi+LCgW2w7T1GtuqBxz4m9aU4ceOf+0KInw
|
-> ssh-ed25519 osOCZA dkgMfjgrKalX7uGrncrep3rtVZFXUHeqwbPix7ngyFY
|
||||||
oJ43lhfBnq03g4OWyIclsoyUjJM4IRXQcN+H8xUfOzs
|
a9jzF29C7Ltg7tn7Rcoi95847kRhWePylmMU7PGOkdo
|
||||||
-> ssh-ed25519 DFiohQ UcmlGnwNYUjALCKI/90wC96JaeBU5NwX742yZ4JBpXQ
|
-> ssh-ed25519 DFiohQ CeZgWwo/TDb89fUVx2ueTArKGPuBjdp2sklqTpkgoj4
|
||||||
l8Wwh7zdA6Ww/CSqI0UffuuL6/oD3nW5/ZcDm62a2b8
|
7/H9QMGzIBXcSYTnzXfJwlvlKLI4B1miPU+LXzmiHtE
|
||||||
-> ssh-ed25519 PT7ffg ViIZQL9IEiMKxsYkZSewoQlEtHCS2K66HWs22dCtsHM
|
-> ssh-ed25519 PT7ffg 456boso/C85lpir1PYUYD1pzb70vQvTrAN3gKy15s1s
|
||||||
LkZwHCQWPP11iacPOC2HtGq6qpp+5owzSulTzGpDhzg
|
Sv2hsM/Yx1hUeGWih5zMYXzJaapm767IDzC/4wmKulU
|
||||||
--- gsZQ6BVOUwRjzTAED6JZHNo/Vz+m0wgN1VrCbJwS3Vo
|
--- /iFmcxXywCLhEOLKLjzrKx/QW93++yzI7tXvn/asMUQ
|
||||||
·‘ù
|
¯`©òˆ:…*ŸÞ¥·œê')»{;tɤ`E¨`ŠØþ„♇ •Ï‘„šUà‹::›¿’h�Ó<A±É¢”%äZgsE¼ol„¿cƒãÈj¾�Äuð‚ÔÔÌBÈ-¬³"{iëÒ…Iпíe8NÃæ6§çºV�÷SðFã$m¸Æ¥æ¼–oÈ
|
||||||
›{V-ó/Ûñ]Ã"•)œ¹×ÕA'YÔ6>ؾíUý␍eÎp³¦<¬�±úɺ@ ŽpèwHÒ©eß蟖ñNןótsæq[Cí-£°õ{‘p 2ޝ“Ä»7ÀFÇZÔWYŠ1ïéû¯ž•£9j�QS�/9'A£8•�f×þuª
|
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,9 +0,0 @@
|
|||||||
age-encryption.org/v1
|
|
||||||
-> ssh-ed25519 osOCZA M9L73b+MHSMCFYxR4e46y8BC6nId6H60+CFSRitUuDU
|
|
||||||
6Al0NVlujTdIrujXBWrjXRq06WhEtxCBwrxLpDmqUcY
|
|
||||||
-> ssh-ed25519 DFiohQ 8LsAZ+hNANvGQxoSD0Dxix4H7blGLruwUG3WiTLyaGw
|
|
||||||
0AyCxIEa8WcwThEt7IIrl3OEo9DdcqwR1BK4HxB4xBQ
|
|
||||||
-> ssh-ed25519 PT7ffg 2CZY6UYI+8SWwXAAtcLJQM4vNmTnKuLWUqxduk3KhgM
|
|
||||||
bKqTlHfSSSoLJRve93osz/aiJQ0dYqdLnDuAR8Hxcqs
|
|
||||||
--- PyiJQa1AZlZ1ukWSykQ07tn4wDZ2pmGr5EKRwCQ46cA
|
|
||||||
–àè�W(^>‡æ$:3‡o†+d¨Qdјò…k´"1LW³ËÚSGí•/û¥qf÷òš+E_À�ÑAÄ,”'qœ]ÕÚZw“Á
|
|
||||||
+1
-10
@@ -9,7 +9,7 @@ let
|
|||||||
apu = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICAZkIuXtpP9a9bHkBl+MJI//q3ClMqzx03Rd/Xe4rjc";
|
apu = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICAZkIuXtpP9a9bHkBl+MJI//q3ClMqzx03Rd/Xe4rjc";
|
||||||
freun-dev = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEvCSjIjipog1Xf9mPc683r5VSGSjVc8v1UZg5VrbbxM";
|
freun-dev = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEvCSjIjipog1Xf9mPc683r5VSGSjVc8v1UZg5VrbbxM";
|
||||||
radish = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIQ0fy4n3yyD64+g55eZazeI5g9FurJnlC6fRiOXbbks";
|
radish = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIQ0fy4n3yyD64+g55eZazeI5g9FurJnlC6fRiOXbbks";
|
||||||
turny = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIM3To9DeTGzwiQN+uqNWZXKzGG1BJnLvYRgLm0/iI92Y";
|
turny = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIODeWhDvzDGyTGkCoxay80NtgU2OVPL37qXjbhJP7oPJ";
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
"gotosocial.age".publicKeys = users ++ [ freun-dev ];
|
"gotosocial.age".publicKeys = users ++ [ freun-dev ];
|
||||||
@@ -44,13 +44,4 @@ in
|
|||||||
"context7.age".publicKeys = users ++ [ radish ];
|
"context7.age".publicKeys = users ++ [ radish ];
|
||||||
"gitea.age".publicKeys = users ++ [ freun-dev ];
|
"gitea.age".publicKeys = users ++ [ freun-dev ];
|
||||||
"gitea-actions-runner.age".publicKeys = users ++ [ freun-dev ];
|
"gitea-actions-runner.age".publicKeys = users ++ [ freun-dev ];
|
||||||
"invidious-companion.age".publicKeys = users ++ [ apu ];
|
|
||||||
"invidious.age".publicKeys = users ++ [ freun-dev ];
|
|
||||||
"open-webui.age".publicKeys = users ++ [ freun-dev ];
|
|
||||||
"open-terminal-api-key.age".publicKeys = users ++ [ freun-dev ];
|
|
||||||
"searx.age".publicKeys = users ++ [ freun-dev ];
|
|
||||||
"grafana/secret-key.age".publicKeys = users ++ [ freun-dev ];
|
|
||||||
"everii-vpn/de1.key.age".publicKeys = users ++ [ radish ];
|
|
||||||
"everii-vpn/ch1.key.age".publicKeys = users ++ [ radish ];
|
|
||||||
"everii-vpn/fi1.key.age".publicKeys = users ++ [ radish ];
|
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
@@ -1,10 +1,9 @@
|
|||||||
age-encryption.org/v1
|
age-encryption.org/v1
|
||||||
-> ssh-ed25519 osOCZA PQtiqZbb169B6Ovwy86AExmBbrVteTQOFbjnDGJq3UM
|
-> ssh-ed25519 osOCZA Mq3JCuYFasHg3oSpWgGEjs5+cleWPog/eJCx1SqqXVQ
|
||||||
LFCbg+9gFnt4p5xHGHrfJXMJRjSGPPmpXd4pEIFtp2A
|
SiJ8vHCSZoyhvbQQ3AUOGk54oOQZAqyNNvjRjKHm4Fg
|
||||||
-> ssh-ed25519 DFiohQ RqRirsYRaJ3RRLzvQIOohbF3uX6/5axomxhrO/SAjzA
|
-> ssh-ed25519 DFiohQ xEJMTBuU1uMm3goNGUln63rUbddy96+SZR8K+/LkMB4
|
||||||
jUMEf1YYetb1JWYzeWT4cyT81O36KNm0fN8I5rmuRrE
|
hFWZe+w2h6sxitweJ+dRNb6HY7YFt1k+/XDVNoDVw0Y
|
||||||
-> ssh-ed25519 PT7ffg ISuGZoBIQVdZv/17EKV+MNQHvqSTcj4ChLGvGew4KGQ
|
-> ssh-ed25519 PT7ffg ErmwKHYuFoIS8rjMeq/5G6SWKaUhHG/N1Uy/KK7yZXU
|
||||||
QzjajsmaW+6CQc28cYAOuCLCwFY3c9qP0/uSa4z9yPM
|
/Iz1mQp8mq7xH+kjP4S5m36GjTpzVRv40mcKPT+5rcc
|
||||||
--- aDY1WIHOhshB9Yfv6tZ9Xip7Ms+88v4D++tGiSSI1A4
|
--- 82a4oeNSWQhUtTJvV+ErC9nmT3YrATezQzY7m8G2JFU
|
||||||
mÎÔr¼&³A@j×
|
aë‡*1¾êr¤ÏDwzŸ¨*ŸÓ‡ê=®^¨ERÕÞ?Ž�b¡ô&`i¸˜¼`^lØ6N÷–á4¦Œ S½î-‰þÑ¥ Ù
|
||||||
B¯_W½r»1°½RúÆôv|Ð|ú|·Õd-TD¤VÝ&ÎãƒÍêɧˆøá3sÓŸ(S#*åF¾
|
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,497 +0,0 @@
|
|||||||
# Abdominal pain and tingling diagnosis
|
|
||||||
|
|
||||||
**Session ID:** ses_0fd155dc9ffew6VgVF3JAGjbxc
|
|
||||||
**Created:** 6/26/2026, 10:52:39 AM
|
|
||||||
**Updated:** 6/26/2026, 11:08:58 AM
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## User
|
|
||||||
|
|
||||||
I describe a health issue and maybe you can try to make educated guesses what it might be. Research online if it helps.
|
|
||||||
|
|
||||||
Long enduring (months) very mild pain in the abdomen area. Feels a bit like the pain would be in the muscles. It's usually localized to one area but the area might be different on another day. At a later stage the symptoms included a feeling of stuffedness even though the person hasn't eaten much. Stool consistency and color normal. Latest symptom: A tingling feeling in the penis, a bit like it would be numb after being cut off circulation. Blood test results:
|
|
||||||
|
|
||||||
P-Kreatiniini: 75 umol/l
|
|
||||||
P-Alaniiniaminotransferaasi: 31 U/l
|
|
||||||
P-C-reaktiivinen proteiini: <4 mg/l
|
|
||||||
B-Leukosyytit: 6.0 E9/l
|
|
||||||
B-Erytrosyytit: 5.37 E12/l
|
|
||||||
B-Hemoglobiini: 156 g/l
|
|
||||||
E-Erytrosyytit, keskitilavuus: 86 fl
|
|
||||||
E-Punasolujen kokojakauma: 12%
|
|
||||||
E-Hemoglobiini, keskimassa: 29 pg
|
|
||||||
E-Hemoglobiini, keskimassakonsentraatio: 339 g/l
|
|
||||||
B-Trombosyytit: 267 E9/l
|
|
||||||
Pt-Glomerulussuodatusnopeus, estimoitu: 111 ml/min/1.7
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Assistant
|
|
||||||
|
|
||||||
I'll research some of these symptoms to give you better-informed guesses. Let me look up a few things.
|
|
||||||
|
|
||||||
**Important disclaimer:** I'm a coding assistant, not a doctor. This is for informational purposes only — please see a healthcare professional.
|
|
||||||
|
|
||||||
Let me analyze the blood work first and research the symptom combination.
|
|
||||||
|
|
||||||
**Tool: webfetch**
|
|
||||||
|
|
||||||
**Tool: webfetch**
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Assistant
|
|
||||||
|
|
||||||
Let me try some other sources for research.
|
|
||||||
|
|
||||||
**Tool: webfetch**
|
|
||||||
|
|
||||||
**Tool: webfetch**
|
|
||||||
|
|
||||||
**Tool: webfetch**
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Assistant
|
|
||||||
|
|
||||||
Let me search for a couple more conditions to round out my analysis.
|
|
||||||
|
|
||||||
**Tool: webfetch**
|
|
||||||
|
|
||||||
**Tool: webfetch**
|
|
||||||
|
|
||||||
**Tool: webfetch**
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Assistant
|
|
||||||
|
|
||||||
Now let me search for one more condition that might be relevant.
|
|
||||||
|
|
||||||
**Tool: webfetch**
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Assistant
|
|
||||||
|
|
||||||
Here's my comprehensive analysis. **Disclaimer: I'm not a doctor — this is for informational purposes only. Please consult a healthcare professional.**
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Blood Work Analysis
|
|
||||||
|
|
||||||
**All values are within normal ranges:**
|
|
||||||
|
|
||||||
| Test | Value | Reference (male) | Status |
|
|
||||||
|------|-------|-------------------|--------|
|
|
||||||
| Creatinine | 75 µmol/l | 60-100 | Normal |
|
|
||||||
| ALT | 31 U/l | <45 | Normal |
|
|
||||||
| CRP | <4 mg/l | <5 | Normal (no inflammation) |
|
|
||||||
| WBC | 6.0 E9/l | 3.4-8.5 | Normal (no infection) |
|
|
||||||
| RBC | 5.37 E12/l | 4.2-5.7 | Normal |
|
|
||||||
| Hemoglobin | 156 g/l | 134-167 | Normal (no anemia) |
|
|
||||||
| MCV | 86 fl | 82-98 | Normal |
|
|
||||||
| RDW | 12% | 11.5-14.5% | Normal |
|
|
||||||
| MCH | 29 pg | 27-33 | Normal |
|
|
||||||
| MCHC | 339 g/l | 320-360 | Normal |
|
|
||||||
| Platelets | 267 E9/l | 150-360 | Normal |
|
|
||||||
| eGFR | 111 ml/min/1.73m² | >90 | Excellent |
|
|
||||||
|
|
||||||
The completely normal panel **rules out** systemic infection, significant inflammation, anemia, liver dysfunction, and kidney dysfunction. This actually narrows things down considerably — it points toward functional, neurological, or musculoskeletal causes rather than inflammatory/infectious ones.
|
|
||||||
|
|
||||||
**Notably NOT tested** (worth requesting):
|
|
||||||
- **Fasting glucose / HbA1c** — diabetes/pre-diabetes can cause neuropathy *and* gastroparesis (early satiety)
|
|
||||||
- **PSA** — prostate issues
|
|
||||||
- **TSH** — thyroid dysfunction
|
|
||||||
- **Vitamin B12** — deficiency causes neuropathy
|
|
||||||
- **Urinalysis** — urinary tract issues
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Ranked Differential Diagnoses
|
|
||||||
|
|
||||||
### 1. Chronic Pelvic Pain Syndrome (CP/CPPS) — *Best overall fit*
|
|
||||||
|
|
||||||
This is my top candidate. Also called chronic nonbacterial prostatitis (NIH Category III). Affects ~2-6% of men, peak age 35-45.
|
|
||||||
|
|
||||||
**Why it fits almost everything:**
|
|
||||||
- **Mild, migrating abdominal pain** — Wikipedia lists "abdominal pain" as a symptom; pain can be in perineum, testicles, tip of penis, pubic/bladder area, and can radiate/wax and wane
|
|
||||||
- **"Feels muscular"** — A key mechanism is **pelvic floor dysfunction with myofascial trigger points** — chronic unconscious contraction of pelvic floor muscles. The pain literally *is* muscular
|
|
||||||
- **Migrating location** — Different trigger points in different muscle groups on different days explains why the pain area changes
|
|
||||||
- **Penile tingling/numbness** — Wikipedia explicitly mentions "pain can be present in... tip of penis" and "constant burning pain in the penis"; the UPOINT classification system includes a Neurologic category
|
|
||||||
- **Early satiety** — CP/CPPS is strongly associated with **IBS** (gut-bladder-brain axis); the MAPP Research Network specifically groups these conditions together
|
|
||||||
- **Normal stool** — CP/CPPS itself doesn't typically affect stool consistency
|
|
||||||
- **Normal blood work** — Characteristic; no infection found, no systemic inflammation
|
|
||||||
- **Progressive symptoms** — The condition can worsen/expand over time, which matches the progression you describe (abdomen → fullness → penile tingling)
|
|
||||||
|
|
||||||
**Key quote from Wikipedia:** *"Symptoms may wax and wane. Pain can range from mild to debilitating. Pain may radiate to the back and rectum... Pain can be present in the perineum, testicles, tip of penis, pubic or bladder area... abdominal pain, constant burning pain in the penis..."*
|
|
||||||
|
|
||||||
**Treatment direction:** Pelvic floor physical therapy (trigger point release), stress management, possibly alpha-blockers or quercetin. Not antibiotics (unless clear infection evidence).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 2. Pudendal Neuralgia / Nerve Entrapment — *Best fit for penile symptoms*
|
|
||||||
|
|
||||||
Compression or irritation of the pudendal nerve (S2-S4 nerve roots). Relatively uncommon but often underdiagnosed.
|
|
||||||
|
|
||||||
**Why it fits:**
|
|
||||||
- **Penile tingling/numbness** — Wikipedia explicitly states: *"numbness of the penis"* and *"paresthesia (tingling / pins and needles)"* are characteristic symptoms. The dorsal nerve of the penis is a terminal branch of the pudendal nerve
|
|
||||||
- **Lower abdominal pain** — Pain can be perceived in the perineum, genitals, suprapubic region, and lower abdomen
|
|
||||||
- **Gradual onset** — *"The onset of pain symptoms is usually gradual without any single causative event"*
|
|
||||||
- **Normal blood work** — No systemic markers expected
|
|
||||||
- **Risk factor: prolonged sitting** — Wikipedia specifically lists **"programmers, office workers... drivers"** as at-risk. If you sit at a computer for long periods, this is highly relevant
|
|
||||||
|
|
||||||
**Why it's not #1:**
|
|
||||||
- Pain is typically **worsened by sitting and relieved by standing/toilet seat** — you didn't mention this pattern (but also didn't say it's absent)
|
|
||||||
- The **migrating** nature of the pain is somewhat atypical (pudendal pain is usually more fixed to one side/area)
|
|
||||||
- **Early satiety** is not well explained by pudendal nerve issues alone
|
|
||||||
- Pain character is usually more neuropathic (burning, electric shock-like) rather than "muscular"
|
|
||||||
|
|
||||||
**Diagnostic criteria (Nantes):** Pain in pudendal nerve territory, worsened by sitting, relieved by standing/lying, no objective sensory loss, positive pudendal nerve block.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 3. Pelvic Floor Dysfunction / Myofascial Pain Syndrome — *Best fit for "muscular" feeling*
|
|
||||||
|
|
||||||
This is actually a key mechanism *behind* CP/CPPS, but can also exist independently.
|
|
||||||
|
|
||||||
**Why it fits:**
|
|
||||||
- **"Feels like muscle pain"** — This *is* muscle pain; chronic tension in pelvic floor muscles (levator ani, coccygeus, piriformis) creates trigger points
|
|
||||||
- **Migrating pain** — Different muscles/trigger points on different days
|
|
||||||
- **Referred pain to abdomen and genitals** — Pelvic floor trigger points refer pain to the lower abdomen, groin, and penis
|
|
||||||
- **Fullness/stuffedness** — Pelvic floor tension can affect bowel function and create a sensation of pelvic/abdominal fullness
|
|
||||||
- **Penile tingling** — Nerve compression from tense muscles can cause paresthesia
|
|
||||||
- **Normal blood work** — Expected
|
|
||||||
|
|
||||||
This often co-occurs with CP/CPPS and pudendal neuralgia — it may be the common underlying mechanism.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 4. Irritable Bowel Syndrome (IBS) + Overlapping Pelvic Pain
|
|
||||||
|
|
||||||
**Why it partially fits:**
|
|
||||||
- **Migrating abdominal pain** — Classic IBS symptom
|
|
||||||
- **Early satiety/bloating** — Common in IBS
|
|
||||||
- **Normal blood work** — Characteristic
|
|
||||||
- **Comorbid with CP/CPPS** — Wikipedia on CP/CPPS: *"Men with CP/CPPS are more likely than the general population to have... irritable bowel syndrome (IBS)"*
|
|
||||||
|
|
||||||
**Why it's not higher:**
|
|
||||||
- **Normal stool** — Atypical for IBS, which usually involves changes in bowel habits (diarrhea/constipation/mixed). You specifically noted stool consistency and color are normal
|
|
||||||
- **Doesn't explain penile tingling** — IBS alone cannot cause this. IBS Wikipedia article mentions ~1/3 of IBS patients report sexual dysfunction (reduced libido), but not genital numbness/tingling
|
|
||||||
- The "muscular" quality of pain is less typical for IBS (which is usually described as crampy/visceral)
|
|
||||||
|
|
||||||
However, IBS could be a **co-existing condition** alongside a pelvic floor/nerve issue, explaining the GI symptoms while the pelvic floor issue explains the penile symptoms.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 5. Functional Dyspepsia (Postprandial Distress Syndrome subtype)
|
|
||||||
|
|
||||||
**Why it partially fits:**
|
|
||||||
- **Early satiety** — This is the **hallmark symptom** of the PDS subtype. Wikipedia: *"troublesome early satiation"* is a diagnostic criterion
|
|
||||||
- **Epigastric pain/discomfort** — Matches abdominal discomfort
|
|
||||||
- **Normal blood work** — Characteristic
|
|
||||||
- **Chronic, sporadic** — Matches the months-long duration with varying intensity
|
|
||||||
|
|
||||||
**Why it's not higher:**
|
|
||||||
- Doesn't explain **migrating** pain (FD pain is typically epigastric)
|
|
||||||
- Doesn't explain **penile tingling** at all
|
|
||||||
- Doesn't explain the **"muscular"** quality
|
|
||||||
|
|
||||||
This could be a **separate co-existing condition** — one condition explaining the GI symptoms (functional dyspepsia) and another explaining the pelvic/penile symptoms (CP/CPPS or pudendal neuralgia).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 6. Small Fiber Neuropathy — *Interesting unifying possibility*
|
|
||||||
|
|
||||||
Damage to small unmyelinated nerve fibers. Can be "patchy" and migrating.
|
|
||||||
|
|
||||||
**Why it's interesting:**
|
|
||||||
- **Tingling/numbness** — Characteristic symptom (penile tingling fits)
|
|
||||||
- **Migrating/patchy pain** — Non-length-dependent variant can affect many areas sporadically
|
|
||||||
- **Autonomic involvement** — If autonomic fibers are affected, could explain early satiety (gastroparesis-like) and possibly bowel/bladder issues
|
|
||||||
- **Normal routine blood work** — Standard panels don't detect it; requires skin biopsy for diagnosis
|
|
||||||
|
|
||||||
**Why it's lower:**
|
|
||||||
- Pain is usually described as **burning/stabbing**, not "mild muscular pain"
|
|
||||||
- Usually starts in **feet/hands** (length-dependent pattern), though non-length-dependent variants exist
|
|
||||||
- Common causes include **diabetes/pre-diabetes** — and glucose was **not tested** in this panel
|
|
||||||
- Would be a less common explanation for this symptom cluster
|
|
||||||
|
|
||||||
**If suspected:** Request fasting glucose, HbA1c, vitamin B12, and possibly a skin punch biopsy for intraepidermal nerve fiber density.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 7. Lumbosacral Radiculopathy (Nerve Root Irritation)
|
|
||||||
|
|
||||||
**Why it's possible:**
|
|
||||||
- **Migrating pain** — Different nerve roots irritated on different days
|
|
||||||
- **Penile tingling** — Pudendal nerve roots (S2-S4) in the sacral spine
|
|
||||||
- **Abdominal wall pain** — Referred pain from thoracic/lumbar nerve roots
|
|
||||||
- **Normal blood work** — Expected
|
|
||||||
|
|
||||||
**Why it's lower:**
|
|
||||||
- Usually accompanied by **back pain** (not mentioned)
|
|
||||||
- Early satiety not well explained
|
|
||||||
- Would need MRI of lumbar/sacral spine to diagnose
|
|
||||||
- The "muscular" quality is less typical (radicular pain is usually sharp/shooting)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 8. Abdominal Cutaneous Nerve Entrapment Syndrome (ACNES)
|
|
||||||
|
|
||||||
**Why it partially fits:**
|
|
||||||
- **Localized abdominal wall pain** that feels superficial/muscular
|
|
||||||
- Can change locations (different nerve branches)
|
|
||||||
- Normal blood work
|
|
||||||
- Often missed by doctors
|
|
||||||
|
|
||||||
**Why it's low:**
|
|
||||||
- Doesn't explain **penile tingling**
|
|
||||||
- Doesn't explain **early satiety**
|
|
||||||
- Only explains the abdominal pain component
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 9. Occult Inguinal Hernia
|
|
||||||
|
|
||||||
**Why it's possible:**
|
|
||||||
- Localized pain that can vary
|
|
||||||
- Can affect nerves (genital branch of genitofemoral nerve) → penile tingling
|
|
||||||
- May cause fullness sensation
|
|
||||||
- Normal blood work
|
|
||||||
|
|
||||||
**Why it's low:**
|
|
||||||
- Migrating pain is atypical
|
|
||||||
- Usually more groin-specific pain
|
|
||||||
- Early satiety not well explained
|
|
||||||
- Would need physical exam/ultrasound to detect
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Summary & Synthesis
|
|
||||||
|
|
||||||
The most coherent single explanation is **CP/CPPS with pelvic floor dysfunction**, possibly with overlapping **IBS or functional dyspepsia** explaining the early satiety. The symptom progression (abdominal pain → fullness → penile tingling) fits a pelvic floor/neurological condition that's gradually affecting more areas.
|
|
||||||
|
|
||||||
The **completely normal blood work** is reassuring — it rules out infection, inflammation, and organ dysfunction. This strongly points toward a **functional/neurological/musculoskeletal** cause rather than a structural or inflammatory disease.
|
|
||||||
|
|
||||||
**Key questions to consider:**
|
|
||||||
1. Is the pain **worsened by sitting** and relieved by standing? (→ pudendal neuralgia)
|
|
||||||
2. Do you sit for prolonged periods? (→ both pudendal and pelvic floor issues)
|
|
||||||
3. Has blood glucose ever been checked? (→ neuropathy/gastroparesis possibility)
|
|
||||||
4. Any urinary symptoms? (frequency, urgency, pain with urination?) (→ CP/CPPS)
|
|
||||||
5. Any lower back pain? (→ radiculopathy)
|
|
||||||
|
|
||||||
**Suggested next steps with a doctor:**
|
|
||||||
- **Urology referral** — for prostate/pelvic floor evaluation (CP/CPPS assessment)
|
|
||||||
- **Fasting glucose / HbA1c** — rule out diabetes/pre-diabetes
|
|
||||||
- **Pelvic floor physical therapy evaluation** — assess for trigger points and muscle tension
|
|
||||||
- Consider **abdominal/pelvic ultrasound** to rule out structural causes
|
|
||||||
- **PSA** if age-appropriate
|
|
||||||
- **Vitamin B12** — deficiency can cause neuropathy
|
|
||||||
|
|
||||||
The good news is that the normal blood work and absence of red flag symptoms (no weight loss, no bleeding, no anemia, normal stool) make serious organic disease unlikely.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## User
|
|
||||||
|
|
||||||
The patient rides a bike 60 minutes a day at least and reports of numbness in the penis after unmounting the bicycle some times if the saddle is in a bad position. Could this be a contributing factor or change your hypothesis?
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Assistant
|
|
||||||
|
|
||||||
This is a **major** piece of information that significantly strengthens the pudendal neuralgia/entrapment hypothesis and likely makes it the primary diagnosis.
|
|
||||||
|
|
||||||
## Why This Changes Things
|
|
||||||
|
|
||||||
The Wikipedia article on pudendal nerve entrapment literally has a section called **"In cyclists"** and lists the condition's alternate name as **"cyclist syndrome"**:
|
|
||||||
|
|
||||||
> *"In male competitive cyclists, it is often called 'cyclist syndrome'. This is a rare condition in which recurrent numbness of the penis and scrotum develops after prolonged cycling."*
|
|
||||||
|
|
||||||
> *"Nerve entrapment syndromes, presenting as genital numbness, are amongst the most common bicycling associated urogenital problems."*
|
|
||||||
|
|
||||||
> *"Pudendal nerve entrapment can develop in cyclists; likely due to both the compression and stretching of the pudendal nerve for prolonged time."*
|
|
||||||
|
|
||||||
The patient is **already experiencing the acute version** of this — temporary penile numbness after cycling with a bad saddle position. That's the pudendal nerve being compressed against the saddle/ischial rami through Alcock's canal. Over months of daily 60-minute rides, this repeated compression can progress to **chronic pudendal neuralgia** — which explains the symptom progression.
|
|
||||||
|
|
||||||
## Revised Hypothesis
|
|
||||||
|
|
||||||
**Pudendal nerve compression/neuralgia from cycling** is now the leading diagnosis, with **secondary pelvic floor dysfunction** explaining the muscular-quality migrating abdominal pain:
|
|
||||||
|
|
||||||
| Symptom | Explanation via Pudendal Neuralgia |
|
|
||||||
|---------|-------------------------------------|
|
|
||||||
| Penile tingling/numbness | **Direct** — dorsal nerve of penis is terminal branch of pudendal nerve; already demonstrably compressed by cycling |
|
|
||||||
| Migrating "muscular" abdominal pain | **Secondary** — chronic nerve irritation → pelvic floor muscle tension → trigger points in different muscle groups on different days |
|
|
||||||
| Early satiety/stuffedness | Pelvic floor tension affecting bowel function; pudendal nerve has autonomic fibers; possible overlap with IBS (common comorbidity) |
|
|
||||||
| Progressive course | Cumulative nerve damage from repeated daily compression |
|
|
||||||
| Normal blood work | Expected — nerve compression doesn't show in standard panels |
|
|
||||||
| Migrating location | Different nerve branches/muscle groups irritated on different days |
|
|
||||||
|
|
||||||
## What Doesn't Quite Fit Pudendal Neuralgia Alone
|
|
||||||
|
|
||||||
- **Migrating pain** — pudendal pain is usually more fixed (perineum/genitals/suprapubic), though central sensitization can spread it
|
|
||||||
- **Early satiety** — not a classic pudendal symptom, though pelvic floor secondary effects could explain it
|
|
||||||
- **Mild pain** — pudendal neuralgia is usually more severe/burning, but early-stage or mild compression could present this way
|
|
||||||
|
|
||||||
This is likely **pudendal nerve compression (from cycling) + secondary pelvic floor dysfunction**, possibly with an overlapping functional GI component (IBS/functional dyspepsia) explaining the satiety — the MAPP Network research groups all these as related urologic chronic pelvic pain syndromes (UCPPS).
|
|
||||||
|
|
||||||
## Practical Recommendations
|
|
||||||
|
|
||||||
**Immediate (before seeing a doctor):**
|
|
||||||
- **Saddle change** — get a saddle with a perineal cutout or a no-nose saddle (removes pressure from the pudendal canal area)
|
|
||||||
- **Saddle tilt** — tilt the nose slightly down to shift weight to the sit bones (ischial tuberosities) instead of the perineum
|
|
||||||
- **Saddle height/position** — have a bike shop do a proper fit; too high = more perineal pressure
|
|
||||||
- **Stand regularly** — get out of the saddle every 10-15 minutes during rides
|
|
||||||
- **Consider temporarily reducing** cycling frequency to see if symptoms improve
|
|
||||||
|
|
||||||
**Medical next steps:**
|
|
||||||
- **Urologist or pelvic pain specialist** — specifically one familiar with pudendal neuralgia / cyclist syndrome
|
|
||||||
- Mention the cycling and the post-ride numbness explicitly — this is the key diagnostic clue
|
|
||||||
- A **diagnostic pudendal nerve block** (local anesthetic injection) can confirm the diagnosis — if pain temporarily resolves, it's pudendal
|
|
||||||
- **Pelvic floor physical therapy** — to address secondary muscle tension/trigger points
|
|
||||||
- Still worth checking **fasting glucose/HbA1c** to rule out diabetic neuropathy as a cofactor
|
|
||||||
|
|
||||||
The encouraging part: if caught before permanent nerve damage, pudendal neuralgia from cycling often improves with conservative measures (saddle change, positioning, PT). The fact that the numbness is already position-dependent suggests the nerve isn't permanently damaged yet — it's still a dynamic compression issue.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## User
|
|
||||||
|
|
||||||
Any questions you'd ask the patient?
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Assistant
|
|
||||||
|
|
||||||
Here are the questions I'd ask, grouped by what they'd help distinguish:
|
|
||||||
|
|
||||||
## Pudendal Neuralgia vs. CP/CPPS vs. Other
|
|
||||||
|
|
||||||
1. **Is the pain worsened by sitting, and relieved by standing up or sitting on a toilet seat?** — The single most diagnostic question for pudendal nerve entrapment (Nantes criteria). If yes → strongly pudendal. If no → more likely CP/CPPS or pelvic floor dysfunction.
|
|
||||||
|
|
||||||
2. **Is the pain always on the same side, or does it switch sides?** — Pudendal neuralgia is usually unilateral or bilateral but fixed. Migrating sides suggests pelvic floor trigger points or a more systemic cause.
|
|
||||||
|
|
||||||
3. **Does the pain wake you up at night?** — Pudendal neuralgia typically does *not* wake patients at night (pain is position-dependent). Night pain that disrupts sleep would suggest other causes (inflammatory, neoplastic).
|
|
||||||
|
|
||||||
## Prostate / Urinary (CP/CPPS Assessment)
|
|
||||||
|
|
||||||
4. **Any urinary symptoms?** — Frequency, urgency, hesitancy, weak stream, pain with urination, or waking to urinate at night? These would point toward CP/CPPS or BPH.
|
|
||||||
|
|
||||||
5. **Any pain during or after ejaculation?** — A hallmark of CP/CPPS. Wikipedia: *"Post-ejaculatory pain is a hallmark of the condition."*
|
|
||||||
|
|
||||||
6. **Any pain in the perineum (area between scrotum and anus), testicles, or rectum?** — Core CP/CPPS and pudendal territories.
|
|
||||||
|
|
||||||
## Cycling Details
|
|
||||||
|
|
||||||
7. **How long has the post-ride numbness been happening, and has it gotten worse or more frequent over time?** — Progression from temporary numbness to persistent tingling would confirm cumulative nerve damage.
|
|
||||||
|
|
||||||
8. **Does the penile tingling persist even on days you don't cycle?** — If yes → the nerve irritation has become chronic/independent of cycling. If it only happens after riding → still purely positional/compression-related.
|
|
||||||
|
|
||||||
9. **What type of saddle, and have you tried different ones?** — Narrow racing saddles cause more perineal pressure than wider/cutout saddles.
|
|
||||||
|
|
||||||
10. **Do you use padded cycling shorts?** — No chamois = more direct pressure.
|
|
||||||
|
|
||||||
## GI / Functional Dyspepsia / IBS
|
|
||||||
|
|
||||||
11. **Is the "stuffedness" related to meals, or is it constant regardless of eating?** — Meal-related early satiety → functional dyspepsia. Constant fullness → pelvic floor tension or IBS overlap.
|
|
||||||
|
|
||||||
12. **Any nausea, heartburn, or bloating?** — Would support functional dyspepsia or gastroparesis.
|
|
||||||
|
|
||||||
13. **Has there been any change in bowel habits at all — even subtle?** — Frequency changes, urgency, or alternating constipation/diarrhea would suggest IBS. The patient says stool is normal, but frequency/urgency weren't specified.
|
|
||||||
|
|
||||||
14. **Any unintentional weight loss?** — Red flag for organic disease (malignancy, IBD). The normal blood work makes this unlikely but it's important to confirm.
|
|
||||||
|
|
||||||
## Neuropathy / Systemic
|
|
||||||
|
|
||||||
15. **Any tingling or numbness anywhere else — feet, hands, legs?** — Would suggest a more generalized small fiber neuropathy (diabetes, B12 deficiency) rather than isolated pudendal compression.
|
|
||||||
|
|
||||||
16. **Has blood sugar ever been checked? Any family history of diabetes?** — Pre-diabetes/diabetes is the most common cause of small fiber neuropathy and could also cause gastroparesis (early satiety).
|
|
||||||
|
|
||||||
17. **Any history of lower back injury, herniated disc, or sciatica?** — Could point to lumbosacral radiculopathy affecting S2-S4 roots (which include pudendal nerve roots).
|
|
||||||
|
|
||||||
## General / Red Flags
|
|
||||||
|
|
||||||
18. **How old is the patient?** — CP/CPPS peaks at 35-45. Pudendal neuralgia can occur at any age. Prostate issues (BPH, cancer) become more relevant >45-50.
|
|
||||||
|
|
||||||
19. **Any fever, night sweats, or fatigue?** — Would suggest infection or systemic illness (all absent from blood work, but worth confirming clinically).
|
|
||||||
|
|
||||||
20. **Has the patient taken any medications, especially PPIs (omeprazole etc.) or NSAIDs, long-term?** — PPIs can cause SIBO → IBS-like symptoms. Chronic NSAIDs can cause gastric issues → functional dyspepsia-like symptoms.
|
|
||||||
|
|
||||||
21. **Any prior abdominal surgeries or hernias?** — Could cause adhesions or nerve entrapment.
|
|
||||||
|
|
||||||
22. **Stress levels / anxiety?** — Both CP/CPPS and functional dyspepsia are strongly linked to the gut-brain axis and stress; Wikipedia on CP/CPPS: *"anxiety or stress results in chronic, unconscious contraction of the pelvic floor muscles."*
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
The **three highest-yield questions** would be #1 (sitting/standing pattern), #5 (ejaculatory pain), and #15 (tingling elsewhere) — those alone would substantially narrow the differential.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## User
|
|
||||||
|
|
||||||
1. Unconclusive, 2. not always on the same side. 3. No, 4. No, 5, No, 6. No, 7. Has been happening pretty much since the patient started bike commuting. 8. Since yesterday, yes. 9. It's a pretty comfortable one with suspension (commuter bike). 10. No, 11. Unconclusive, maybe unrelated to eating. 12. No. 13. Sometimes (rarely, maybe once every two weeks) diarrhea or very loose stool, no constipation. 14. No, 15. No, 16. No, but the patient has a blood sugar meter available and could check. 17. No, 18: 38, 19: No, 20: No, 21: No, 22. Very mild if at all.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Assistant
|
|
||||||
|
|
||||||
This is very informative. Let me reassess.
|
|
||||||
|
|
||||||
## What the Answers Rule Out or Weaken
|
|
||||||
|
|
||||||
**CP/CPPS is now significantly less likely.** The absence of its three hallmark features is telling:
|
|
||||||
- No urinary symptoms (common in CP/CPPS)
|
|
||||||
- No ejaculatory pain (Wikipedia calls this a *"hallmark of the condition"*)
|
|
||||||
- No perineal/testicular/rectal pain (core CP/CPPS territory)
|
|
||||||
- Low stress (stress is a key driver)
|
|
||||||
|
|
||||||
**Functional dyspepsia is weaker** — stuffedness may not be meal-related, and there's no nausea, heartburn, or bloating.
|
|
||||||
|
|
||||||
**Small fiber neuropathy is much less likely** — no tingling anywhere else, no known diabetes.
|
|
||||||
|
|
||||||
**Radiculopathy is unlikely** — no back pain, no other neurological symptoms.
|
|
||||||
|
|
||||||
## What Remains Strong
|
|
||||||
|
|
||||||
**The critical new finding is answer #8** — tingling now persists on non-cycling days (started yesterday). This suggests the nerve irritation is transitioning from **reversible positional compression** to **chronic neuralgia**. This is a turning point and arguably a reason to act soon.
|
|
||||||
|
|
||||||
The picture now looks like **two related cycling overuse problems**:
|
|
||||||
|
|
||||||
### Problem 1: Pudendal nerve compression (specifically dorsal nerve of penis)
|
|
||||||
- Penile numbness after cycling (confirmed, long-standing)
|
|
||||||
- Now tingling persisting off-bike (new development as of yesterday)
|
|
||||||
- The dorsal nerve of the penis is a terminal branch of the pudendal nerve that runs along the ischiopubic ramus — directly where a saddle presses
|
|
||||||
- This could be **Type IV entrapment** (terminal branch, 13% of cases in the Wikipedia article) which would explain why there's no perineal/rectal pain — only the penile branch is affected
|
|
||||||
- **No padded shorts** (answer #10) is a significant risk factor — more direct perineal pressure every ride
|
|
||||||
|
|
||||||
### Problem 2: Pelvic floor / hip flexor muscle tension from cycling
|
|
||||||
- Migrating "muscular" abdominal pain, not always same side
|
|
||||||
- Sensation of fullness/stuffedness unrelated to meals
|
|
||||||
- Cycling engages hip flexors, adductors, and pelvic floor muscles continuously; chronic tension in these muscles refers pain to the lower abdomen and can create a sensation of pelvic fullness
|
|
||||||
- Occasional loose stools (every ~2 weeks) could be explained by pelvic floor dysfunction affecting bowel function
|
|
||||||
|
|
||||||
These two problems may be **independent consequences of the same activity** rather than one causing the other — simpler and more parsimonious than my earlier CP/CPPS hypothesis.
|
|
||||||
|
|
||||||
## Immediate Actions I'd Recommend
|
|
||||||
|
|
||||||
**Today:**
|
|
||||||
1. **Check fasting blood sugar** with the available meter — quick, free, rules out diabetes as a neuropathy cofactor
|
|
||||||
2. **Stop or significantly reduce cycling** temporarily — the transition to persistent off-bike tingling is a warning sign. Give the nerve a chance to recover before it becomes permanent
|
|
||||||
|
|
||||||
**This week:**
|
|
||||||
3. **Get padded cycling shorts** if continuing to ride — this is the lowest-hanging fruit
|
|
||||||
4. **Try a no-nose or cutout saddle** — removes perineal pressure entirely
|
|
||||||
5. **Book a urology appointment** — mention cycling, penile numbness, and that tingling is now persisting off-bike. This is the key clinical detail that should get their attention
|
|
||||||
|
|
||||||
**If symptoms don't improve within 2-4 weeks of stopping cycling:**
|
|
||||||
6. **Pelvic floor physical therapy** — for the muscular abdominal pain and fullness
|
|
||||||
7. **Consider a pudendal nerve block** (diagnostic) if a specialist suspects neuralgia
|
|
||||||
|
|
||||||
## One More Question
|
|
||||||
|
|
||||||
**Has the patient ever tried completely stopping cycling for a week or more, and if so, did the symptoms improve?** — This would be the natural experiment that confirms or refutes cycling as the cause. If the abdominal pain and fullness also improve with stopping, it strongly confirms that both problems are cycling-related.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
Reference in New Issue
Block a user