1
0
mirror of https://github.com/kmein/niveum synced 2026-03-16 10:11:08 +01:00

55 Commits

Author SHA1 Message Date
b9a222855e pi: update plugins 2026-03-04 17:59:51 +01:00
f8bb3c04bf pdf-ocr: fix 2026-03-04 17:59:51 +01:00
dce42c7e80 mp3player-write: do not check disk space 2026-03-04 17:59:51 +01:00
5bedd897c9 switch manakish and kabsa 2026-03-04 17:59:51 +01:00
d952ecf17a meteora: host on ful 2026-03-04 17:59:51 +01:00
df36954fed kpaste: reenable 2026-03-04 17:59:51 +01:00
697100f85f cyberlocker-tools: reenable 2026-03-04 17:59:51 +01:00
8eccb752dc getty: always enable autologinOnce 2026-03-04 17:59:51 +01:00
6bb16ff6ed Revert "cuda: build ollama"
This reverts commit fb86f8c7f7.
2026-03-04 17:59:51 +01:00
8fe9e80522 panoptikon: use from new repo 2026-03-04 17:59:51 +01:00
314848d877 update secrets 2026-02-21 16:26:55 +01:00
377b338979 disable zram 2026-02-21 16:26:48 +01:00
26231912cf opencrow: use new agent 2026-02-21 16:26:36 +01:00
a5532f1e9d panoptikon: use from new repo 2026-02-21 16:26:19 +01:00
d878b73ab5 hyprland: use more niphas tools 2026-02-21 16:25:21 +01:00
3216cd19f5 openclaw: rip 2026-02-18 18:19:20 +01:00
14494ca1f7 wip: fix makanek deploy 2026-02-18 18:18:08 +01:00
201e9e5f60 opencrow 2026-02-18 18:17:45 +01:00
2b59d2a68e opencrow: open up 2026-02-17 23:08:26 +01:00
722b1e9d14 add opencrow Matrix bot on ful
Uses upstream NixOS module (systemd-nspawn container with sandboxing).

- User: @fable:4d2.org on matrix.4d2.org
- Provider: GitHub Copilot (OAuth, one-time interactive login)
- pkgs.pi added via extraPackages (available on PATH inside container)
- Secrets via agenix: matrix token as environmentFile,
  soul bind-mounted into the container
- Sessions + pi-agent state in /var/lib/opencrow, backed up via restic

To complete setup:
1. Create secrets/opencrow-matrix-token.age (OPENCROW_MATRIX_ACCESS_TOKEN=...)
2. Create secrets/opencrow-soul.age (SOUL.md content)
3. One-time Copilot login inside the container:
   sudo nixos-container root-login opencrow
   PI_CODING_AGENT_DIR=/var/lib/opencrow/pi-agent pi
   # Run /login, select GitHub Copilot, complete OAuth flow
2026-02-17 22:37:16 +01:00
bfbe2da850 openclaw: soften the hardening 2026-02-17 22:30:38 +01:00
c34647a800 pi llm 2026-02-17 22:30:38 +01:00
e5c86818d9 nextcloud: upgrade 2026-02-17 22:28:09 +01:00
e885753c7f weechat: run in screen 2026-02-17 22:28:09 +01:00
87e39cc30b aerc: use correct cock port 2026-02-17 22:28:09 +01:00
b46f06a462 add opencrow Matrix bot on ful
Uses upstream NixOS module (systemd-nspawn container with sandboxing).

- User: @fable:4d2.org on matrix.4d2.org
- Provider: GitHub Copilot (OAuth, one-time interactive login)
- Secrets via agenix: matrix token as environmentFile,
  soul bind-mounted into the container
- Sessions + pi-agent state in /var/lib/opencrow, backed up via restic

To complete setup:
1. Create secrets/opencrow-matrix-token.age (OPENCROW_MATRIX_ACCESS_TOKEN=...)
2. Create secrets/opencrow-soul.age (SOUL.md content)
3. One-time Copilot login inside the container:
   sudo nixos-container root-login opencrow
   PI_CODING_AGENT_DIR=/var/lib/opencrow/pi-agent pi
   # Run /login, select GitHub Copilot, complete OAuth flow
2026-02-17 22:25:42 +01:00
9041e4cb11 restore secrets.txt
Accidentally emptied by a previous 'git add -A' in a working tree
where the secrets submodule was not initialized.
2026-02-17 21:55:24 +01:00
6d2acd2e3c fix zaatar build: move printer groups to configs/default.nix
printing.nix can't conditionally set users.users.me.extraGroups —
any mention of users.users.me in the module system creates the user
entry and triggers the isNormalUser/isSystemUser assertion on machines
without a 'me' user (like zaatar).

Move lp/scanner groups to configs/default.nix where users.users.me is
defined. On zaatar, root gets lp/scanner groups directly.
2026-02-17 21:50:21 +01:00
ca6157a4f0 remove dead modules/retiolum.nix
This was an old copy of the krebs retiolum module with NIX_PATH lookups
(<retiolum/hosts>, <system-secrets/...>). It was never imported — the
upstream retiolum.nixosModules.retiolum from the flake input is used
instead (in profiles.default), with configs/retiolum.nix overriding
the key paths to agenix secrets.
2026-02-17 21:39:11 +01:00
6550c1c077 replace niveum-* aliases with direct niphas-* references
- configs/i3.nix: niveum-terminal → niphas-terminal, etc.
- configs/graphical/home-manager.nix: same
- configs/default.nix: same
- flake.nix: remove the three overlay aliases
2026-02-17 21:37:47 +01:00
6fe970ae3a update AGENTS.md with current state
- Remove resolved issues (broken refs, orphaned packages, dead code)
- Add coding conventions section (lib.getExe, writer patterns)
- Fix machine table (kibbeh/zaatar/tabula/tahina profiles are correct now)
- Trim improvement ideas to remaining items only
2026-02-17 21:36:09 +01:00
e67d6d7df2 use lib.getExe and lib.getExe' in packaged scripts
Replace all ${pkg}/bin/name patterns with:
- lib.getExe pkg (for main executables: curl, jq, gnused, ffmpeg, etc.)
- lib.getExe' pkg "name" (for specific binaries: coreutils, util-linux, etc.)
2026-02-17 21:35:28 +01:00
4fc29ff0fe package .bin/ scripts as proper nix packages, delete .bin/
Packaged 14 scripts from .bin/ into packages/ with proper dependency
declarations (writers.writeDashBin/writeBashBin/writePython3Bin):
- 256color → two56color (terminal color chart)
- avesta.sed → avesta (Avestan transliteration)
- bvg.sh → bvg (Berlin transit disruptions)
- unicode → charinfo (Unicode character info)
- chunk-pdf → chunk-pdf (split PDFs by page count)
- csv2json → csv2json (CSV to JSON converter)
- fix-sd.sh → fix-sd (exFAT SD card recovery, improved output handling)
- json2csv → json2csv (JSON to CSV converter)
- mp3player-write → mp3player-write (audio conversion for MP3 players)
- mushakkil.sh → mushakkil (Arabic diacritization)
- nix-haddock-index → nix-haddock-index (GHC Haddock index generator)
- pdf-ocr.sh → pdf-ocr (OCR PDFs via tesseract)
- prospekte.sh → prospekte (German supermarket flyer browser)
- readme → readme (GitHub README as man page)

All added to overlay and packages output. .bin/ directory removed.
2026-02-17 21:32:10 +01:00
dbbad1e146 delete obsolete scripts from .bin/
Removed 27 scripts that are dead, obsolete, or no longer relevant:
- work-specific: elm-publish-private, watson2fdf.sh
- hardcoded creds/sessions: libib.sh, ttrss-unread
- broken/bitrotted: proxies.sh, dummy-alert, playlist_entries.sh, screencap.sh
- trivial/unused: toposort.nix, tuesday-1800, mud.sh, load.sh, calendars.sh
- no longer used: anki-poem.sh, browser, candyman, horoscope.sh, lieferando.sh,
  space.py, notetags.sh, sample-pdf.sh, lit.awk, countdown, json2csv.jq
- dead mail tooling: mail-current-part, mail-current-query-find-part-by-*

Remaining scripts will be packaged properly.
2026-02-17 21:25:40 +01:00
36132b0454 fix broken references to deleted files, remove dead code
Build-breaking fixes:
- kibbeh: remove imports of deleted admin-essentials, nix, zsh configs
  (all provided by niphas via profiles.default + profiles.desktop)
- tabula, tahina: remove imports of deleted admin-essentials, nix configs
  (provided by niphas via profiles.default)
- zaatar: remove import of deleted tmux config
  (provided by niphas via profiles.default)

Dead code removal:
- flake.nix: remove nixosModules.zsh-kmein (referenced deleted config/zsh.nix)
- flake.nix: remove unused wallpapers input
- packages/gpt.nix, hora.nix, k-lock.nix: orphaned, not in overlay or referenced
- configs/hledger.nix: remove hora reference (package deleted)
2026-02-17 21:10:27 +01:00
3bebe25adb remove duplicates already provided by niphas
- Delete configs/bash.nix (identical to niphas, was dead code)
- Delete configs/direnv.nix (dead code, niphas provides direnv)
- Delete packages/vimv.nix (identical to niphas)
- Remove vimv from overlay and packages output
- Alias niveum-terminal/browser/filemanager to niphas equivalents
- Remove 17 duplicate packages from configs/packages.nix
- Remove nil from configs/editor.nix (niphas provides it)
- Remove dead dmenu packageOverride from configs/default.nix
2026-02-17 20:53:52 +01:00
21029d3bbc openclaw: add backups 2026-02-15 22:39:47 +01:00
d8bad81090 openclaw: give it a browser to play with 2026-02-15 22:36:29 +01:00
f12beaa69e picoclaw 2026-02-15 22:14:20 +01:00
a94dacb64c openclaw 2026-02-15 22:13:26 +01:00
fb86f8c7f7 cuda: build ollama 2026-02-15 16:47:29 +01:00
86b5e4da9f radio-news: use newer gemini model 2026-02-15 16:47:29 +01:00
15ab8ac8a8 autologin: fix module use 2026-02-15 16:47:29 +01:00
f202be220b manakish: fix imports 2026-02-15 16:47:29 +01:00
267124dfd1 format 2026-02-15 16:47:29 +01:00
ca05785b26 yt-dlp-master 2026-02-15 16:47:29 +01:00
f1610d08dc mpv: configure with wrappers 2026-02-15 16:47:29 +01:00
9aaaaff724 lix: try out 2026-02-15 16:47:29 +01:00
c6a11c1d79 hold overlays correctly 2026-02-10 22:02:34 +01:00
8c6363881d niphas: update 2026-02-07 16:40:45 +01:00
01019fffac printing: enable scanning 2026-02-07 16:40:35 +01:00
84f1a2688f ensure ashell from unstable 2026-02-06 17:13:14 +01:00
d4cfb63a11 editor: enable copilot and colorscheme 2026-02-06 17:12:57 +01:00
b038278af3 use shared config from niphas 2026-02-04 17:07:52 +01:00
e264f13885 zsh: export config as module 2026-02-03 16:42:47 +01:00
109 changed files with 1482 additions and 2490 deletions

View File

@@ -1,38 +0,0 @@
#! /bin/sh
set -euf
pl() {
for i in $(seq $1 $(expr $2 - 1)); do
printf '\e[38;5;%sm%03i\e[m ' $i $i
done
printf '\e[38;5;%sm%03i\e[m\n' $2 $2
}
p() {
printf '\e[38;5;%sm%03i\e[m ' $1 $1
}
pn() {
printf '\e[38;5;%sm%03i\e[m\n' $1 $1
}
p6x6() {
for i in $(seq 0 5); do
for j in $(seq 0 5); do
p $(expr $1 + $i + $j \* 6)
done
echo
done
}
pl 0 7
pl 8 15
p6x6 16
p6x6 52
p6x6 88
p6x6 124
p6x6 160
p6x6 196
pl 232 243
pl 244 255

View File

@@ -1,29 +0,0 @@
#!/bin/sh
file="${1?please supply a poetry file}"
[ -f "$file" ] || {
echo "'$file' is no file"
exit 1
}
poem="$(mktemp)"
clean () {
rm "$poem"
}
trap clean EXIT
sed '/^$/d' "$file" > "$poem"
htmlize() {
awk 'ORS="<br/>"' \
| head -c -5 # remove final <br/> characters
}
for line_number in $(seq 1 "$(wc -l "$poem" | cut -d' ' -f1)"); do
if [ "$line_number" -gt 3 ] && [ "$line_number" -gt 1 ]; then
sed -n "$((line_number - 3)),$((line_number - 1))p" "$poem"
else
sed -n "1,$((line_number - 1))p" "$poem"
fi | htmlize
printf '\t'
sed -n "${line_number},+1p" "$poem" | htmlize
printf '\n'
done

View File

@@ -1,54 +0,0 @@
#!/usr/bin/env -S sed -f
s/ā̊/𐬃/g
s//𐬝/g
s/ṣ̌/𐬴/g
s/š́/𐬳/g
s/ą̄/𐬅/g
s/ŋᵛ/𐬤/g
s/ə̄/𐬇/g
s/ŋ́/𐬣/g
s//𐬒/g
s/xᵛ/𐬓/g
s/a/𐬀/g
s/ā/𐬁/g
s/å/𐬂/g
s/ą/𐬄/g
s/ə/𐬆/g
s/e/𐬈/g
s/ē/𐬉/g
s/o/𐬊/g
s/ō/𐬋/g
s/i/𐬌/g
s/ī/𐬍/g
s/u/𐬎/g
s/ū/𐬏/g
s/k/𐬐/g
s/x/𐬑/g
s/g/𐬔/g
s/ġ/𐬕/g
s/γ/𐬖/g
s/c/𐬗/g
s/j/𐬘/g
s/t/𐬙/g
s/θ/𐬚/g
s/d/𐬛/g
s/δ/𐬜/g
s/p/𐬞/g
s/f/𐬟/g
s/b/𐬠/g
s/β/𐬡/g
s/ŋ/𐬢/g
s/n/𐬥/g
s/ń/𐬦/g
s//𐬧/g
s/m/𐬨/g
s//𐬩/g
s//𐬫/g
s/y/𐬪/g
s/v/𐬬/g
s/r/𐬭/g
s/s/𐬯/g
s/z/𐬰/g
s/š/𐬱/g
s/ž/𐬲/g
s/h/𐬵/g

View File

@@ -1,24 +0,0 @@
#!/bin/sh -e
#
# Usage: browser
# pipe html to a browser
# e.g.
# $ echo '<h1>hi mom!</h1>' | browser
# $ ron -5 man/rip.5.ron | browser
if [ -t 0 ]; then
if [ -n "$1" ]; then
open $1
else
cat <<usage
Usage: browser
pipe html to a browser
$ echo '<h1>hi mom!</h1>' | browser
$ ron -5 man/rip.5.ron | browser
usage
fi
else
f="/tmp/browser.$RANDOM.html"
cat /dev/stdin > $f
xdg-open $f
fi

View File

@@ -1,46 +0,0 @@
#!/bin/sh
interesting="U6 N6 140 M46 184 N84"
curl -sSL 'https://www.bvg.de/disruption-reports/q' \
--data-raw '{"variables":{},"query":"{
allDisruptions {
disruptions {
meldungsId
linie
verkehrsmittel
__typename
... on Traffic {
datum
gueltigVonDatum
gueltigVonZeit
gueltigBisDatum
gueltigBisZeit
richtungName
richtungHafasId
beginnAbschnittName
beginnAbschnittHafasId
endeAbschnittName
endeAbschnittHafasId
textIntUrsache
sev
textIntAuswirkung
umfahrung
textWAPSMSUrsache
textWAPSMSAuswirkung
prioritaet
__typename
}
}
__typename
}
}"}' \
| jq --arg interesting "$interesting" '
.data.allDisruptions.disruptions
| map(select(
(.linie as $linie
| $interesting
| split(" ")
| index($linie))
and (.["__typename"] == "Traffic")
))
'

View File

@@ -1,19 +0,0 @@
#!/bin/sh
directory="$(mktemp -d)"
trap clean EXIT
clean() {
rm -rf "$directory"
}
year=$(date +%Y)
output=/tmp/$year.pdf
for month in $(seq 1 12); do
printf "\r%d" "$month" 1>&2
astrolog -zN Berlin -qm "$month" "$year" -X -K -XA -Xr -Xm -Xb -Xo "$(printf "%s/%02d.bmp" "$directory" "$month")" -Xw 1080 720 2>/dev/null
done
printf "\r"
convert "$directory/*.bmp" "$output"
echo "$output"

View File

@@ -1,25 +0,0 @@
#!/bin/sh
set -efu
usage() {
echo >&2 "$0 add-{reddit,telegram,youtube,twitch,twitter} NAME"
exit 1
}
candyman() {
curl -fsSv http://news.r/api -H content-type:application/json -d "$(jq -n "
{
command: \"PRIVMSG\",
params: [\"#all\", \"candyman: $1 $2\"]
}
")"
}
[ $# -ge 2 ] || usage
case "$1" in
add-reddit|add-telegram|add-youtube|add-twitter|add-twitch)
candyman "$@"
;;
*) usage;;
esac

View File

@@ -1,23 +0,0 @@
#! /usr/bin/env nix-shell
#! nix-shell -i bash -p pdftk gnugrep
set -efu
INPUT_FILE="${2:?Pass the PDF path as second argument.}"
PAGES_PER_REPORT="${1:?Pass the chunk size as first argument.}"
if [ ! -f "$INPUT_FILE" ]; then
echo >&2 "File $INPUT_FILE does not exist."
exit 1
fi
TOTAL_PAGES="$(pdftk "$INPUT_FILE" dump_data | grep NumberOfPages | cut -f2 -d' ')"
RUNS=$((TOTAL_PAGES/PAGES_PER_REPORT))
for run in $(seq 0 "$((RUNS-1))"); do
start_page=$((run*PAGES_PER_REPORT+1))
end_page=$(((run+1)*PAGES_PER_REPORT))
output_file="chunk_$((run+1)).pdf"
echo "splitting $INPUT_FILE from $start_page to $end_page into $output_file"
pdftk "$INPUT_FILE" cat "$start_page-$end_page" output "$output_file"
done

View File

@@ -1,13 +0,0 @@
#!/usr/bin/env -S awk -f
function z() {
getline < "/proc/uptime"
close("/proc/uptime")
return $0
}
BEGIN {
x = z()
while (1) {
y = z()
printf "%02d:%05.2f\r", (y - x) / 60, (y - x) % 60
}
}

View File

@@ -1,14 +0,0 @@
#!/usr/bin/env python3
import csv
import json
import sys
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("--delimiter", "-d", default=",", help="CSV field separator")
args = parser.parse_args()
if __name__ == "__main__":
json.dump(list(csv.DictReader(sys.stdin, delimiter=args.delimiter)), sys.stdout)

View File

@@ -1,43 +0,0 @@
#!/bin/bash
name=$RANDOM
url='http://localhost:9093/api/v1/alerts'
echo "firing up alert $name"
# change url o
curl -XPOST $url -d "[{
\"status\": \"firing\",
\"labels\": {
\"alertname\": \"$name\",
\"service\": \"my-service\",
\"severity\":\"warning\",
\"instance\": \"$name.example.net\"
},
\"annotations\": {
\"summary\": \"High latency is high!\"
},
\"generatorURL\": \"http://prometheus.int.example.net/<generating_expression>\"
}]"
echo ""
echo "press enter to resolve alert"
read
echo "sending resolve"
curl -XPOST $url -d "[{
\"status\": \"resolved\",
\"labels\": {
\"alertname\": \"$name\",
\"service\": \"my-service\",
\"severity\":\"warning\",
\"instance\": \"$name.example.net\"
},
\"annotations\": {
\"summary\": \"High latency is high!\"
},
\"generatorURL\": \"http://prometheus.int.example.net/<generating_expression>\"
}]"
echo ""

View File

@@ -1,7 +0,0 @@
#! /usr/bin/env nix-shell
#! nix-shell -p "(import <nixpkgs> { overlays = [ (import ~/work/fysiweb/engiadina-pwa/devops/pkgs) ]; }).elm-publish-private"
#! nix-shell -i bash
set -efux
exec elm-publish-private "$@"

View File

@@ -1,21 +0,0 @@
#!/bin/sh
set -xfu
drive="$1"
mountpoint="/media/sd-card-$(date +%s)"
backup_directory="$(pwd)"
trap clean EXIT
clean() {
umount "$mountpoint"
rmdir "$mountpoint"
fsck.exfat "$drive"
}
filenames="$(fsck.exfat "$drive" 2>&1 | sed -nE "s/.* file '(.*?)' is not allocated.*/\1/p")"
mkdir "$mountpoint"
mount "$drive" "$mountpoint"
echo "$filenames" | while read -r filename; do
find "$mountpoint" -type f -name "$filename" -exec mv {} "$backup_directory" \;
done

View File

@@ -1,34 +0,0 @@
#!/bin/sh
set -efu
# Berlin: -d lodeg=13 -d lomin=22 -d losec=41 -d lodir=E -d ladeg=52 -d lamin=27 -d lasec=42 -d ladir=N -d usecoords=1 \
# Kassel: -d lodeg=9 -d lomin=32 -d losec=5 -d lodir=E -d ladeg=51 -d lamin=18 -d lasec=17 -d ladir=N -d usecoords=1 \
[ $# -eq 1 ] || {
echo >&2 Usage: "$0" TIMESTAMP
exit 1
}
export TZ=UTC
chart_path="$(mktemp /tmp/chart_XXX.pdf)"
timestamp="$1"
year="$(date -d "@$timestamp" +%Y)"
month="$(date -d "@$timestamp" +%m)"
day="$(date -d "@$timestamp" +%d)"
hour="$(date -d "@$timestamp" +%H)"
minute="$(date -d "@$timestamp" +%M)"
curl -sSL 'https://edifyingfellowship.org/astro/' \
-d lodeg=9 -d lomin=32 -d losec=5 -d lodir=E -d ladeg=51 -d lamin=18 -d lasec=17 -d ladir=N -d usecoords=1 \
-d ybyr="$year" -d ybmo="$month" -d ybdy="$day" -d ybhr="$hour" -d ybmi="$minute" -d ybsc=0 -d ybtz="$TZ" \
-d currenttime=0 \
-d title="$timestamp" \
-d options[]=VancouverWheel -d options[]=Arrow -d options[]=XBold -d options[]=HouseLabels -d options[]=Placidus \
-d options[]=Sun -d options[]=Moon -d options[]=Mercury -d options[]=Venus -d options[]=Mars -d options[]=Jupiter -d options[]=Saturn -d options[]=Uranus -d options[]=Neptune -d options[]=Pluto -d options[]=Ascendant -d options[]=MC -d options[]=Lilith -d options[]=MeanNode -d options[]=TrueNode \
-d aspectpct=100 -d format=PDF -d Submit= -o "$chart_path"
zathura "$chart_path"

View File

@@ -1,24 +0,0 @@
#!/usr/bin/env python3
import csv
import json
import sys
if __name__ == "__main__":
json_list = json.load(sys.stdin)
if not isinstance(json_list, list):
print("JSON object is not a list.", file=sys.stderr)
sys.exit(1)
if len(json_list) == 0:
print("JSON list is empty.", file=sys.stderr)
sys.exit(1)
keys = set()
for element in json_list:
if isinstance(element, dict):
keys |= element.keys()
else:
print("Non-dict element:", element, file=sys.stderr)
sys.exit(1)
writer = csv.DictWriter(sys.stdout, fieldnames=list(keys))
writer.writeheader()
for element in json_list:
writer.writerow(element)

View File

@@ -1,5 +0,0 @@
#!/usr/bin/env -S jq -r -f
(map(keys) | add | unique) as $cols
| map(. as $row | $cols | map($row[.])) as $rows
| $cols, $rows[]
| @csv

View File

@@ -1,15 +0,0 @@
#!/usr/bin/env bash
session_id=7b638c194d9bda74f80043045018cc9e
declare -A libraries
libraries["Literatur"]=344428
libraries["Sprache"]=344160
libraries["Miscellanea"]=344427
libraries["Wissenschaft"]=344429
libraries["Relicta"]=565920
for library in ${!libraries[@]}
do
curl -sSL 'https://www.libib.com/library/functions/csv-export.php' -H "Cookie: PHPSESSID=$session_id" -d export="${libraries[$library]}" > "$library.csv"
done

View File

@@ -1,81 +0,0 @@
#!/bin/sh
set -efu
if echo "$1" | grep -Eq '[[:digit:]]{5}'; then
PLZ="$1"
else
echo >&2 "Usage: $0 PLZ"
exit 1
fi
lieferando_dir=/tmp/lieferando
mkdir -p "$lieferando_dir/$PLZ"
fetch_restaurants() {
cache_path="$lieferando_dir/$PLZ.json"
if [ -r "$cache_path" ]; then
cat "$cache_path"
else
w3m -dump_source "http://www.lieferando.de/$PLZ" \
| gunzip \
| sed -n '/var restaurants/,/];$/p' \
| sed 's/var restaurants =//;$s/;$//' \
| prettier --parser=json \
| jq '
map({
name: .[30] | .name,
category: .[30] |.categories | split(", "),
url: "http://lieferando.de\(.[30] | .url)",
minutes: .[19],
minimum: .[10],
delivery: .[14]
})' \
| tee "$cache_path"
fi
}
fetch_menu() {
[ $# -eq 1 ] || exit 1
slug="$(echo "$1" | sed 's!.*/!!')"
cache_path="$lieferando_dir/$PLZ/$slug.json"
if [ -r "$cache_path" ]; then
cat "$cache_path"
else
w3m -dump_source "$1" \
| gunzip \
| sed -n '/var MenucardProducts/,/\];/p' \
| sed 's/var MenucardProducts =//;s/;$//' \
| jq -r '
unique_by(.productId)
| group_by(.categoryId)
| flatten
' \
| tee "$cache_path"
fi
}
data="$(fetch_restaurants)"
# echo "$data" | jq -c '.[]' | while read -r restaurant; do
# fetch_menu "$(echo "$restaurant" | jq -r .url)"
# done
selected_categories="$(echo "$data" | jq -r 'map(.category) | flatten | unique | .[]' | fzf -m)"
selected_restaurant_url="$(echo "$selected_categories" | jq --argjson restaurants "$data" -sRr '
split("\n")[:-1] as $categories
| $restaurants[]
| select(.category - $categories != .category)
| "\(.name) [🚴\(.minutes)min 💰\(.minimum)€ + \(.delivery)€] (\(.url))"
' \
| fzf \
| sed 's/.*(//;s/)$//'
)"
fetch_menu "$selected_restaurant_url" \
| jq -r '.[] | "\(.price)\t\(.name)"' \
| fzf -m \
| awk '{print $0; sum += $1} END {print "-----"; print sum}'

View File

@@ -1,17 +0,0 @@
BEGIN {
if (!comment) comment = "--";
if (!begin) begin = "\\begin{code}";
if (!end) end = "\\end{code}";
}
{
if ($0 == begin) {
code = 1;
print comment, $0;
} else if ($0 == end) {
code = 0;
print comment, $0;
} else {
if (code) print $0;
else print comment, $0;
}
}

View File

@@ -1,2 +0,0 @@
#!/bin/sh
uptime | sed 's/.*load average: \(.*\), \(.*\), \(.*\)/\1 \2 \3/'

View File

@@ -1,3 +0,0 @@
#! /bin/sh
set -efu
exec curl -fSs --unix-socket /tmp/much.api.sock http://localhost/current/part

View File

@@ -1,27 +0,0 @@
#! /bin/sh
# usage: mail-current-query-find-part-by-name NAME
set -efu
name=$1
query=$(mail-current-query)
result=$(notmuch show --entire-thread=false --format=json "$query")
part_id=$(printf %s "$result" | jq --arg name "$name" '
[
recurse |
select(type == "object") |
{ id, name: .filename } |
select(.id != null and .name != null)
] |
map(select(.name == $name))[0].id
')
if test "$part_id" = null; then
printf 'error: could not find part with name %s\n' \
"$name" \
>&2
exit 1
fi
exec notmuch show --part="$part_id" "$query"

View File

@@ -1,39 +0,0 @@
#! /bin/sh
# usage: mail-current-query-find-part-by-type TYPE
set -efu
type=$1
query=$(mail-current-query)
result=$(notmuch show --entire-thread=false --format=json "$query")
part_id=$(printf %s "$result" | jq --arg type "$type" '
#flatten|map(select(.!=null))[0].body[0] |
#
#if .["content-type"] == $type then
# .id
#elif .["content-type"] | test("^multipart/") then
# .content|map(select(.["content-type"]==$type))[0].id
#else
# null
#end
[
recurse |
select(type == "object") |
{ id, type: .["content-type"] } |
select(.id != null and .type != null)
] |
map(select(.type == $type))[0].id
')
if test "$part_id" = null; then
printf 'error: could not find part with type %s\n' \
"$type" \
>&2
exit 1
fi
exec notmuch show --part="$part_id" "$query"

View File

@@ -1,117 +0,0 @@
#!/usr/bin/env bash
# Usage:
# ./mp3_transfer.sh -s 1.3 /mnt/mp3player file1.m4a file2.m4a ...
set -e
# Default speed
SPEED=1.0
# Parse options
while getopts ":s:" opt; do
case $opt in
s)
SPEED=$OPTARG
;;
\?)
echo "Invalid option: -$OPTARG" >&2
exit 1
;;
:)
echo "Option -$OPTARG requires a value." >&2
exit 1
;;
esac
done
# Shift past the options
shift $((OPTIND -1))
# Check arguments
if [ "$#" -lt 2 ]; then
echo "Usage: $0 [-s speed] MOUNT_POINT FILE1 [FILE2 ...]"
exit 1
fi
MOUNT_POINT=$1
shift
FILES=("$@")
# Check mount point exists
if [ ! -d "$MOUNT_POINT" ]; then
echo "Error: Mount point '$MOUNT_POINT' does not exist."
exit 1
fi
# Estimate required space
TOTAL_SIZE=0
for f in "${FILES[@]}"; do
if [ ! -f "$f" ]; then
echo "Warning: File '$f' does not exist, skipping."
continue
fi
# Get file size in bytes
FILE_SIZE=$(stat --printf="%s" "$f")
# Estimate mp3 output size: roughly 1/2 of original m4a (adjust if needed)
TOTAL_SIZE=$((TOTAL_SIZE + FILE_SIZE / 2))
done
# Get available space in bytes
AVAILABLE=$(df --output=avail "$MOUNT_POINT" | tail -n 1)
AVAILABLE=$((AVAILABLE * 1024)) # df reports in KB
if [ "$TOTAL_SIZE" -gt "$AVAILABLE" ]; then
echo "Error: Not enough space on device. Required: $TOTAL_SIZE bytes, Available: $AVAILABLE bytes"
exit 1
fi
echo "Enough space available. Starting conversion..."
sanitize_filename() {
local name="$1"
# Remove path, keep only base name
name=$(basename "$name")
# Remove any extension
name=${name%.*}
# Replace spaces and special chars with underscore
name=$(echo "$name" | tr ' ' '_' | tr -cd '[:alnum:]_-')
# Truncate to max 50 chars
echo "${name:0:50}"
}
# Convert and copy files
for f in "${FILES[@]}"; do
if [ ! -f "$f" ]; then
continue
fi
# Determine the next prefix
existing_prefixes=$(ls "$MOUNT_POINT" | grep -E '^[0-9].*\.mp3$' | sed -E 's/^([0-9]).*/\1/' | sort -n | uniq)
for i in {0..9}; do
if ! echo "$existing_prefixes" | grep -q "^$i$"; then
PREFIX=$i
break
fi
done
echo "Using prefix: $PREFIX"
BASENAME=$(sanitize_filename "$f")
OUT_PATTERN="$MOUNT_POINT/${PREFIX}_%03d_${BASENAME}.mp3"
echo "Converting '$f' to '$OUT_PATTERN' at speed $SPEED..."
ffmpeg -nostdin -i "$f" \
-filter:a "atempo=$SPEED" \
-ar 22050 -ac 1 -c:a libmp3lame -b:a 32k \
-f segment -segment_time 300 \
"$OUT_PATTERN"
# Update prefix for next file
# Count how many segments were created
SEG_COUNT=$(ls "$MOUNT_POINT" | grep -E "^${PREFIX}[0-9]{2}_" | wc -l)
PREFIX=$((PREFIX + SEG_COUNT))
done
echo "All files processed successfully."

View File

@@ -1 +0,0 @@
ssh mud@hotdog.r -t "MUD_NICKNAME=$LOGNAME mud"

View File

@@ -1,6 +0,0 @@
#!/bin/sh
curl -sSL 'https://diac.alsharekh.org/Diac/DiacText' \
-H "Content-Type: application/json" \
--data-raw "$(jq --raw-input '{word: ., type: 1}')" \
--compressed \
| jq -r .diacWord

View File

@@ -1,93 +0,0 @@
#! /usr/bin/env nix-shell
#! nix-shell -i bash -p coreutils gnugrep gnused graphviz
#
# usage: nix-haddock-index
#
# Run this script in an environment where either NIX_GHC is set, or the ghc
# executable exists, to generate an HTML index file pointing to all Haddock
# files accessible to the respective ghc version.
#
# Additionally, an SVG dependency graph of all packages is linked at the
# bottom of the index file.
#
# Note: all files will be generated in /tmp, and won't be deleted automatically
#
set -efux
if test -z "${NIX_GHC-}"; then
NIX_GHC=$(readlink -f "$(type -P ghc)")
fi
if ! echo $NIX_GHC | grep -q '^/nix/store/'; then
printf '%s: error: unsupported GHC executable path (not in Nix store): %q\n' \
"$0" \
"$NIX_GHC" \
>&2
exit -1
fi
NIX_GHC_PREFIX=$(dirname "$(dirname "$NIX_GHC")")
NIX_GHC_DOCDIR=$NIX_GHC_PREFIX/share/doc/ghc/html
main() {
hash=$(echo $NIX_GHC_PREFIX | sed -n 's|^/nix/store/\([a-z0-9]\+\).*|\1|p')
title="Haddock index for $NIX_GHC_PREFIX"
header=$(
printf 'Haddock index for <a href="%s">%s</a>\n' \
$NIX_GHC_PREFIX \
$NIX_GHC_PREFIX \
)
suffix=${hash:+-$hash}
index_file=/tmp/haddock$suffix-index.html
svg_file=/tmp/haddock$suffix.svg
#if ! test -e $index_file; then
eval "$(
echo 'gen_index() {'
echo ' html_head'
"$NIX_GHC_PREFIX"/bin/ghc-pkg dump | sed -n '
s/^---$/ reset/p
s/^\(name\|version\):\s*\([-A-Za-z0-9_.]\+\)$/ \1=\2/p
s/^haddock-html:\s*\([-A-Za-z0-9_./]\+\)$/ haddock_html \1/p
'
echo ' html_foot'
echo '}'
)"
gen_index > $index_file
#fi
#if ! test -e $svg_file; then
"$NIX_GHC_PREFIX"/bin/ghc-pkg dot | tred | dot -Tsvg | sed '
s/<svg width="[0-9]\+pt" height="[0-9]\+pt"/<svg width="3600px" height="100%"/
' > $svg_file
#fi
echo $index_file
}
reset() {
unset name version
}
haddock_html() {
printf '<li>'
printf '<a href="%s/index.html">%s</a>' "$1" "$name-$version"
printf '</li>\n'
}
html_head() {
printf '<!doctype html>\n'
printf '<title>%s</title>\n' "$title"
printf '<link href="%s" rel="stylesheet" type="text/css">\n' \
"$NIX_GHC_DOCDIR/libraries/ocean.css"
printf '<h1>%s</h1>\n' "$header"
printf '<ul>\n'
}
html_foot() {
printf '</ul>\n'
printf '<a href="%s">graph</a>\n' "$svg_file"
}
main "$@"

View File

@@ -1,15 +0,0 @@
#!/bin/sh
# inspired by https://github.com/connermcd/bin/blob/1d38cb98812906d8b95dc6e51e1149e29261617d/notetags
cd "$HOME/notes/" || exit
[ -f tags ] && rm tags
grep -r 'tags:' ./* | while read -r line; do
file=$(echo "$line" | cut -d: -f1)
unparsed_tags=$(echo "$line" | cut -d: -f3) #
tags=$(echo "$unparsed_tags" | sed -e 's/tags: *//g' -e 's/[][,]//g')
for tag in $tags; do
echo "$tag $file /^$unparsed_tags$/;" >> tags
done
done

View File

@@ -1,23 +0,0 @@
#!/usr/bin/env nix-shell
#! nix-shell -i bash -p poppler_utils tesseract4
set -eu
pdf_path="$(realpath "$1")"
[ -f "$pdf_path" ] || {
echo "Usage: $0 FILE.pdf" >&2
exit 1
}
tmpdir="$(mktemp -d)"
trap 'rm -rf $tmpdir' EXIT
cd "$tmpdir"
pdftoppm -png "$pdf_path" pdf-ocr
for png in pdf-ocr*.png; do
tesseract "$png" "$png.txt" 2>/dev/null
done
cat pdf-ocr-*.txt

View File

@@ -1,2 +0,0 @@
#!/bin/sh
youtube-dl -ij "$*" | jq -sr '.[] | .webpage_url'

View File

@@ -1,65 +0,0 @@
#!/bin/sh
lidl() {
echo LIDL
curl -sSL 'https://endpoints.lidl-flyer.com/v3/region-overview/lidl/de-DE/0.json' \
| jq -r '
.categories
| map(select(.name == "Filial-Angebote") | .subcategories | map(.flyers))
| flatten
| flatten
| .[]
| .pdfUrl
'
}
aldi_nord() {
echo ALDI nord
echo 'https://magazine.aldi-nord.de/aldi-nord/aldi-aktuell/GetPDF.ashx'
echo 'https://magazine.aldi-nord.de/aldi-nord/aldi-vorschau/GetPDF.ashx'
}
rewe_berlin() {(
store_id=662366923
publisher_id=1062
echo REWE
curl -sSL 'https://www.bonialserviceswidget.de/de/stores/'$store_id'/brochures?storeId='$store_id'&publisherId='$publisher_id | while read -r brochure_id; do
curl -sSL 'https://www.bonialserviceswidget.de/de/v5/brochureDetails/'"$brochure_id"'?publisherId='$publisher_id | jq -r .pdfUrl
done
)}
kaufland() {(
region_code=8920
echo KAUFLAND
curl -sSL https://filiale.kaufland.de/prospekte.html | htmlq --attribute href '.flyer a' | grep -Eo 'DE_de_KDZ[^/]*' | sed "s/_3000_/_${region_code}_/" | while read -r flyer_id; do
curl -sSL "https://endpoints.leaflets.kaufland.com/v3/$flyer_id/flyer.json?regionCode=$region_code" | jq -r .flyer.pdfUrl
done
)}
netto_schwarz() {
echo 'NETTO (schwarz)'
curl -sSL 'https://squid-api.tjek.com/v2/catalogs?dealer_ids=90f2VL&order_by=created' \
| jq -r '.[] | .id' \
| while read -r flyer_id; do
curl -sSL "https://squid-api.tjek.com/v2/catalogs/$flyer_id/download" \
| jq -r .pdf_url
done
}
dir="$(mktemp -d)"
trap clean EXIT
clean() {
rm -rf "$dir"
}
prospekt_url="$( (
lidl
aldi_nord
rewe_berlin
kaufland
netto_schwarz
) | fzf)"
curl -sSL "$prospekt_url" -o "$dir/prospekt.pdf"
zathura "$dir/prospekt.pdf"

View File

@@ -1,17 +0,0 @@
#!/bin/sh
curl -sSL https://www.netzwelt.de/proxy/index.html \
| pup ".tblc" \
| xml-to-json /dev/stdin \
| jq '
.div.table.tbody.tr
| map(
.td
| {
ip: .[0].a.value,
port: .[1],
country: .[2] | (if type == "string" then . else .a.value end),
security: .[3],
protocol: .[4]
}
)
'

View File

@@ -1,4 +0,0 @@
#!/bin/sh
curl -sSL "https://raw.githubusercontent.com/$*/master/README.md" \
| pandoc -f gfm -t man -s \
| man -l -

View File

@@ -1,6 +0,0 @@
#!/bin/sh
filepath="$(shuf --head-count=1)"
pages="$(pdfinfo "$filepath" | awk '/^Pages:/{print $2}')"
random_page="$(shuf --input-range="1-$pages" --head-count=1)"
zathura --page="$random_page" "$filepath"

View File

@@ -1,16 +0,0 @@
#! /usr/bin/env nix-shell
#! nix-shell -i sh -p coreutils byzanz xorg.xwininfo gnused
# shellcheck shell=sh
# ref https://gist.github.com/aforemny/0994cb7f06ea30d56c8b9681ff5d2054
set -eux
eval "$(xwininfo | \
sed -n -e 's/^ \+Absolute upper-left X: \+\([0-9]\+\).*/x=\1/p' \
-e 's/^ \+Absolute upper-left Y: \+\([0-9]\+\).*/y=\1/p' \
-e 's/^ \+Width: \+\([0-9]\+\).*/w=\1/p' \
-e 's/^ \+Height: \+\([0-9]\+\).*/h=\1/p')"
trap "pkill -f 'sleep 360d'" INT
byzanz-record -e "sleep 360d" -c -x $x -y $y -w $w -h $h "$@"

View File

@@ -1,49 +0,0 @@
import ephem
from datetime import datetime, date, timedelta
now = datetime.now()
limit = now + timedelta(days=365)
def events_until(limit):
initial_date = ephem.Date(datetime.now())
events = {}
now = initial_date
while ephem.localtime(now) <= limit:
now = ephem.next_full_moon(now)
events[now] = "🌕"
now = initial_date
while ephem.localtime(now) <= limit:
now = ephem.next_new_moon(now)
events[now] = "🌑"
now = initial_date
while ephem.localtime(now) <= limit:
now = ephem.next_vernal_equinox(now)
events[now] = "spring equinox"
now = initial_date
while ephem.localtime(now) <= limit:
now = ephem.next_autumnal_equinox(now)
events[now] = "fall equinox"
now = initial_date
while ephem.localtime(now) <= limit:
now = ephem.next_winter_solstice(now)
events[now] = "winter solstice"
now = initial_date
while ephem.localtime(now) <= limit:
now = ephem.next_summer_solstice(now)
events[now] = "summer solstice"
return events
events = events_until(limit)
for date, event in sorted(events.items(), key=lambda x: x[0]):
if ephem.localtime(date) < limit:
print(ephem.localtime(date), event)

View File

@@ -1,81 +0,0 @@
let
lib = import <nixpkgs/lib>;
in
rec {
inherit lib;
input = [
{
x = [
"pool"
"zfs"
];
y = [
"mdadm"
"raid1"
];
}
{
x = [
"pool"
"zfs"
];
y = [
"disk"
"sda"
];
}
{
x = [
"mdadm"
"raid1"
];
y = [
"disk"
"sdb"
];
}
{
x = [
"mdadm"
"raid1"
];
y = [
"disk"
"sdc"
];
}
];
outNodes = node: graph: lib.unique (builtins.map (e: e.y) (builtins.filter (v: v.x == node) graph));
vertices = graph: lib.unique (builtins.map (x: x.y) graph ++ builtins.map (x: x.x) graph);
deleteVertex = node: graph: (builtins.filter (v: v.x != node && v.y != node) graph);
findSink =
graph:
lib.findFirst (v: outNodes v graph == [ ]) (lib.trace graph (builtins.abort "No sink found")) (
vertices graph
);
topSort =
graph:
if graph == [ ] then
[ ]
else if builtins.length graph == 1 then
let
only = builtins.head graph;
in
[
only.y
only.x
]
else
let
sink = findSink graph;
in
[ sink ] ++ topSort (deleteVertex sink graph);
output = topSort input;
}

View File

@@ -1,18 +0,0 @@
#/usr/bin/env -S deno run -A:q
set -x
session_cache="$HOME/.cache/tt-rss.session"
ttrss_endpoint=https://feed.kmein.de/api/
ttrss_user=k
ttrss_password=$(pass shared/tt-rss/password)
login() {
if [ -f "$session_cache" ]; then
session_id="$(cat "$session_cache")"
else
session_id="$(curl -d '{"op":"login","user":"'"$ttrss_user"'","password":"'"$ttrss_password"'"}' "$ttrss_endpoint" | jq -r .content.session_id)"
echo "$session_id" > "$session_cache"
fi
}
login
curl -d '{"sid":"'"$session_id"'","op":"getUnread"}' "$ttrss_endpoint" | jq .content

View File

@@ -1,16 +0,0 @@
#!/bin/sh
set -efux
expected_max_results=1024 # the upper bound on the number of restaurants
radius=250
echo '[out:json];node(id:260050809)->.cbase;
(
node(around.cbase:'$radius')[amenity=fast_food];
node(around.cbase:'$radius')[amenity=restaurant];
);out;' \
| curl -sSL -d @- -X POST http://overpass-api.de/api/interpreter \
| jq --argjson random "$(shuf -i 0-$expected_max_results -n 1)" '
.elements
| length as $length
| .[$random % $length]
'

View File

@@ -1,8 +0,0 @@
import sys
import unicodedata
for index, character in enumerate(sys.stdin.read().strip()):
try:
print(index, character, hex(ord(character)), unicodedata.category(character), unicodedata.name(character))
except:
print(index, character, hex(ord(character)))

View File

@@ -1,26 +0,0 @@
project=Filli
year=2022
for month in Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec; do
from="$(date +%F -d "$month 1, $year")"
to="$(date +%F -d "$month 1, $year + 1 month")"
watson report --json --from "$from" --to "$to" --project "$project"
done | jq --slurp '
def in_array($arr):
. as $value | any($arr[]; . == $value);
map(
["engadin-app","fysiweb","val-muestair","mia-engiadina","ol"] as $official_projects
| (.timespan.from | .[0:7]) as $timespan
| .projects | .[0]
| .time as $total_time
| .tags
| select(. != null)
| map(select(.name | in_array($official_projects)))
| (map(.time)|add) as $official_time
| map({key:.name, value:.time}) | from_entries
| .other |= ($total_time - $official_time)
| map_values(. / (60*60) | ceil)
| .month |= $timespan
)
'

96
AGENTS.md Normal file
View File

@@ -0,0 +1,96 @@
# niveum — Agent Notes
## What This Is
A NixOS flake managing ~9 machines (desktops, servers, family laptops) for one user (kmein/kfm).
Levantine food-themed hostnames: fatteh, kabsa, kibbeh, makanek, manakish, tabula, tahina, zaatar, ful.
## Repository Structure
```
flake.nix # ~670 lines — inputs, overlay, nixosConfigurations, apps, packages output
configs/ # ~50 NixOS config fragments imported by systems
default.nix # 200+ line mega-module for desktop machines (user, shell, gnupg, i18n, etc.)
graphical/ # Hyprland + home-manager config (415 lines in home-manager.nix)
packages.nix # ~250 lines of environment.systemPackages
bots/ # Telegram/Mastodon/Matrix bot configs
keyboard/ # XKB layouts (Coptic, Avestan, Gothic, etc.)
configs/*.nix # Individual concerns: bluetooth, sound, printing, ssh, fonts, etc.
modules/ # Proper NixOS modules with options (retiolum, telegram-bot, passport, power-action, etc.)
packages/ # ~107 package files (scripts, wrappers, small tools)
systems/<name>/ # Per-machine: configuration.nix + hardware-configuration.nix + extras
lib/ # default.nix (niveum helpers), machines.nix (IP/key inventory), panoptikon.nix
secrets/ # agenix-encrypted .age files (empty dir in checkout, tracked via secrets.txt)
```
## Key Relationships
- **niphas** (input): Provides shared "how I like things" config — nixosModules (shell, editor, git, desktop, nix, udiskie) and overlay (niphas-* packages). Used in `profiles.default` and `profiles.desktop`.
- **configs/default.nix**: The "big desktop profile" — imported by fatteh, kabsa, manakish (the main desktop machines). NOT imported by servers or family laptops.
- **profiles** (in flake.nix): `profiles.default`, `profiles.desktop`, `profiles.server` — lists of modules composed per machine.
- **lib.niveum**: Custom lib injected via overlay (`pkgs.lib.niveum`) — used everywhere for machine addresses, SSH port, helper functions.
## Coding Conventions
- Packages use `writers.writeDashBin`, `writers.writeBashBin`, or `writers.writePython3Bin`
- Dependencies are referenced via `lib.getExe pkg` (main executable) or `lib.getExe' pkg "name"` (specific binary)
- For packages needing many commands via PATH, use `lib.makeBinPath` instead (see `packages/prospekte.nix`)
- Overlay entries use `prev.callPackage packages/foo.nix { }` pattern
- Packages are exported via `inherit (pkgs) ...` in the `packages` output
## Known Bugs / Broken References
All previously broken references have been fixed (see commits `36132b04`, `e67d6d7d`).
Remaining issues:
- `modules/retiolum.nix` uses `<retiolum/hosts>` and `<system-secrets/...>` NIX_PATH lookups — breaks flake purity but works with current `NIX_PATH` setup
## Architectural Issues
### 1. configs/default.nix is a grab-bag (200+ lines, ~15 inline anonymous modules)
It's a list of `imports` mixing inline `{ ... }` blocks with file imports. Hard to find what's defined where.
### 2. Retiolum secret boilerplate repeated 9 times
Every system has a near-identical block:
```nix
age.secrets.retiolum-rsa = { file = ../../secrets/${hostname}-retiolum-privateKey-rsa.age; mode = "400"; owner = "tinc-retiolum"; ... };
age.secrets.retiolum-ed25519 = { ... same ... };
```
Could be a function or module parameterized by hostname.
### 3. Nginx + ACME boilerplate duplicated
ful and makanek have identical nginx recommended settings + ACME config.
### 4. niveum-* overlay aliases
`niveum-terminal`, `niveum-browser`, `niveum-filemanager` are aliases to niphas equivalents. Could be removed by updating ~6 references in configs/ to use niphas-* names directly.
### 5. The `pkgs.lib.niveum` pattern
Custom lib injected via overlay into `pkgs.lib`. Unconventional — only available where overlay is applied. A `specialArgs` approach or standalone lib would be cleaner.
### 6. Restic backup config scattered
`services.restic.backups.niveum` is configured in configs/backup.nix, configs/applicative.nix, and extended in 5+ system files. Hard to see what a given machine backs up.
### 7. configs/ vs modules/ distinction blurry
`configs/` has both stateless config fragments (spacetime.nix = timezone) and stateful ones (backup.nix, cloud.nix). `modules/` has proper option-declaring modules. Some configs/ files import from modules/.
## Machines Overview
| Machine | Role | Profile | Arch | Notes |
|-----------|--------------|------------------|---------|---------------------------------------|
| fatteh | Desktop | default+desktop | x86_64 | ThinkPad T480, CUDA, main daily |
| kabsa | Desktop | default+desktop | x86_64 | ThinkPad X220, constrained (2 jobs) |
| manakish | Desktop | default+desktop | x86_64 | ThinkPad X230 |
| kibbeh | Desktop | default+desktop | x86_64 | Pantheon DE, travel laptop |
| ful | Server | default+server | aarch64 | Oracle/Hetzner, nginx, web services |
| makanek | Server | default+server | x86_64 | Hetzner, gitea, nextcloud, weechat |
| zaatar | Server/Home | default+server | x86_64 | Home assistant, backup server |
| tabula | Family laptop| default | x86_64 | LXQt, user "xenos" |
| tahina | Family laptop| default | x86_64 | Pantheon, user "xenos", German |
## Remaining Improvement Ideas
1. **Extract retiolum secret boilerplate** into a function/module
2. **Break up configs/default.nix** into proper named files
3. **Extract nginx+ACME server profile**
4. **Replace niveum-* aliases** with direct niphas-* references
5. **Fix modules/retiolum.nix** NIX_PATH usage for flake purity

View File

@@ -1,127 +0,0 @@
{
pkgs,
lib,
...
}:
let
darwin = lib.strings.hasSuffix "-darwin" pkgs.stdenv.hostPlatform.system;
in
{
environment.systemPackages = [
pkgs.htop
pkgs.w3m
pkgs.wget
# ARCHIVE TOOLS
pkgs.unzip
pkgs.unrar
pkgs.p7zip
pkgs.sshuttle
pkgs.zip
# MONITORS
pkgs.iftop # interface bandwidth monitor
pkgs.lsof # list open files
# SHELL
pkgs.sqlite
pkgs.fd # better find
pkgs.tree
pkgs.parallel # for parallel, since moreutils shadows task spooler
pkgs.ripgrep # better grep
pkgs.rlwrap
pkgs.progress # display progress bars for pipes
pkgs.file # determine file type
pkgs.gdu # ncurses disk usage (ncdu is broken)
pkgs.rmlint # remove duplicate files
pkgs.jq # json toolkit
pkgs.jless # less(1) for json
pkgs.fq # toolkit for yaml, xml and binaries
pkgs.bc # calculator
pkgs.pari # gp -- better calculator
pkgs.ts
pkgs.vimv
pkgs.vg
pkgs.fkill
pkgs.cyberlocker-tools
pkgs.untilport
pkgs.kpaste
# HARDWARE
pkgs.pciutils # for lspci
]
++ lib.optionals (!darwin) [
pkgs.usbutils # for lsusb
pkgs.lshw # for lshw
pkgs.iotop # I/O load monitor
pkgs.psmisc # for killall, pstree
];
security.wrappers = {
pmount = {
setuid = true;
owner = "root";
group = "root";
source = "${pkgs.pmount}/bin/pmount";
};
pumount = {
setuid = true;
owner = "root";
group = "root";
source = "${pkgs.pmount}/bin/pumount";
};
};
environment.interactiveShellInit = ''
# Use XDG_RUNTIME_DIR for temporary files if available
if [ -d "$XDG_RUNTIME_DIR" ]; then
export TMPDIR="$XDG_RUNTIME_DIR"
fi
'';
environment.shellAliases =
let
take = pkgs.writers.writeDash "take" ''
mkdir "$1" && cd "$1"
'';
cdt = pkgs.writers.writeDash "cdt" ''
cd $(mktemp -p "$XDG_RUNTIME_DIR" -d "cdt-XXXXXX")
pwd
'';
wcd = pkgs.writers.writeDash "wcd" ''
cd "$(readlink "$(${pkgs.which}/bin/which --skip-alias "$1")" | xargs dirname)/.."
'';
where = pkgs.writers.writeDash "where" ''
readlink "$(${pkgs.which}/bin/which --skip-alias "$1")" | xargs dirname
'';
in
{
nixi = "nix repl nixpkgs";
take = "source ${take}";
wcd = "source ${wcd}";
where = "source ${where}";
# temporary files and directories
cdt = "source ${cdt}";
vit = "$EDITOR $(mktemp)";
# file safety
mv = "${pkgs.coreutils}/bin/mv --interactive";
rm = "${pkgs.coreutils}/bin/rm --interactive";
cp = "${pkgs.coreutils}/bin/cp --interactive";
# colours
cat = "${pkgs.bat}/bin/bat --theme=ansi --style=plain";
l = "${pkgs.coreutils}/bin/ls --color=auto --time-style=long-iso --almost-all";
ls = "${pkgs.coreutils}/bin/ls --color=auto --time-style=long-iso";
ll = "${pkgs.coreutils}/bin/ls --color=auto --time-style=long-iso -l";
la = "${pkgs.coreutils}/bin/ls --color=auto --time-style=long-iso --almost-all -l";
}
// (
if darwin then
{ }
else
{
"ß" = "${pkgs.util-linux}/bin/setsid";
ip = "${pkgs.iproute2}/bin/ip -c";
# systemd
s = "${pkgs.systemd}/bin/systemctl";
us = "${pkgs.systemd}/bin/systemctl --user";
j = "${pkgs.systemd}/bin/journalctl";
uj = "${pkgs.systemd}/bin/journalctl --user";
}
);
}

View File

@@ -91,7 +91,7 @@
imap.host = mailhost;
imap.port = 993;
smtp.host = mailhost;
smtp.port = 25;
smtp.port = 587;
smtp.tls.useStartTls = true;
};
ical-ephemeris =

View File

@@ -1,35 +1,7 @@
{
pkgs,
lib,
...
}:
let
in
{
environment.variables.TERMINAL = "alacritty";
home-manager.users.me = {
programs.alacritty = {
enable = true;
settings = {
keyboard.bindings = [
{
key = "Plus";
mods = "Control";
action = "IncreaseFontSize";
}
{
key = "Minus";
mods = "Control";
action = "DecreaseFontSize";
}
{
key = "Key0";
mods = "Control";
action = "ResetFontSize";
}
];
};
};
};
}

View File

@@ -18,44 +18,13 @@
];
};
services.displayManager.autoLogin.enable = lib.mkForce false;
# if we have multiple users, they should be able to log in through a greeter
programs.regreet =
let
wallpaper =
pkgs.runCommand "textured-monochrome-wallpaper.png"
{
buildInputs = [ pkgs.imagemagick ];
}
''
magick -size 2560x1440 plasma:fractal \
-colorspace Gray \
-normalize \
-fill ${lib.escapeShellArg config.lib.stylix.colors.withHashtag.base00} -colorize 100% \
-attenuate 0.15 +noise Gaussian \
$out
'';
in
{
enable = true;
settings = {
background = {
path = wallpaper;
fit = "Fill";
};
appearance.greeting_msg = "";
widget.clock.format = "%F %H:%M";
nixpkgs.overlays = [
(final: prev: {
niphas-wallpaper = prev.callPackage ../packages/applicative-wallpaper.nix {
inherit (config.lib.stylix) colors;
};
font = {
inherit (config.stylix.fonts.sansSerif) name;
size = config.stylix.fonts.sizes.applications;
};
iconTheme = {
inherit (config.home-manager.users.me.gtk.iconTheme) package name;
};
};
})
];
# to run nspawn in nix sandbox
nix.settings = {
@@ -65,8 +34,6 @@
"auto-allocate-uids"
"cgroups"
];
extra-sandbox-paths = [ "/dev/net" ]; # needed for tests of VPNs
trusted-users = [ config.users.users.applicative.name ];
};
@@ -86,82 +53,4 @@
commands = [ "ALL" ];
}
];
home-manager.users.applicative =
let
mainGitConfig = (import ./git.nix {
inherit pkgs lib config;
}).home-manager.users.me.programs.git;
mainAlacrittyConfig = (import ./alacritty.nix {
inherit pkgs lib config;
}).home-manager.users.me.programs.alacritty;
in
lib.recursiveUpdate
(import ./graphical/home-manager.nix {
inherit lib pkgs config;
})
{
xdg.enable = true;
home.stateVersion = "25.11";
programs.git = {
enable = true;
settings = {
inherit (mainGitConfig.settings) alias;
};
};
programs.alacritty = mainAlacrittyConfig;
services.hyprpaper =
let
# asgWallpaper = pkgs.fetchurl {
# url = "http://c.krebsco.de/asg-wallpaper.png";
# hash = "sha256-XLp8XcUgFgDKW5Qae0//0Xw9lhribcevQTVSQvClEB4=";
# };
backgroundColor = config.lib.stylix.colors.withHashtag.base06;
foregroundColor = config.lib.stylix.colors.withHashtag.base01;
width = 1920;
height = 1080;
svgUrl = "https://applicative.systems/_astro/logo-full.D8zRvqBZ.svg";
logoSvg = pkgs.fetchurl {
url = svgUrl;
hash = "sha256-qXDIEZsAPn4eUJ3kb5U6L3PMUCtWGYqhqyIaBt7FntE=";
};
asgWallpaper =
pkgs.runCommand "applicative-wallpaper.png"
{
nativeBuildInputs = [ pkgs.imagemagick ];
}
''
# 1. We use -background to set the canvas color
# 2. We use -fuzz and -opaque to replace the logo's internal colors
# 3. We use -gravity and -extent to center it on a wallpaper-sized canvas
convert \
-background none \
-density 300 \
"${logoSvg}" \
-fuzz 100% -fill "${foregroundColor}" -opaque black \
-resize 800x800 \
-gravity center \
-background "${backgroundColor}" \
-extent ${toString width}x${toString height} \
$out
'';
in
{
enable = true;
settings = {
ipc = false;
splash = true;
preload = [ "${asgWallpaper}" ];
wallpaper = [ ",${asgWallpaper}" ];
};
};
};
}

View File

@@ -1,10 +0,0 @@
{ pkgs, ... }:
{
programs.bash = {
promptInit = ''PS1="$(${pkgs.ncurses}/bin/tput bold)\w \$([[ \$? == 0 ]] && echo \"\[\033[1;32m\]\" || echo \"\[\033[1;31m\]\")\$$(${pkgs.ncurses}/bin/tput sgr0) "'';
interactiveShellInit = ''
set -o vi
'';
completion.enable = true;
};
}

View File

@@ -69,7 +69,7 @@ in
wantedBy = [ "multi-user.target" ];
description = "Telegram reverse bot";
path = [ pkgs.ffmpeg ];
enable = true;
enable = false;
script = ''
TELEGRAM_BOT_TOKEN="$(cat "$CREDENTIALS_DIRECTORY/token")" ${pkgs.telebots}/bin/telegram-reverse
'';
@@ -81,7 +81,7 @@ in
systemd.services.telegram-streaming-link = {
wantedBy = [ "multi-user.target" ];
description = "Telegram bot converting YouTube Music <-> Spotify";
enable = true;
enable = false;
script = ''
TELEGRAM_BOT_TOKEN="$(cat "$CREDENTIALS_DIRECTORY/token")" ${pkgs.telebots}/bin/telegram-streaming-link
'';
@@ -92,7 +92,7 @@ in
systemd.services.telegram-betacode = {
wantedBy = [ "multi-user.target" ];
description = "Telegram beta code bot";
enable = true;
enable = false;
script = ''
TELEGRAM_BOT_TOKEN="$(cat "$CREDENTIALS_DIRECTORY/token")" ${pkgs.telebots}/bin/telegram-betacode
'';
@@ -103,7 +103,7 @@ in
systemd.services.telegram-proverb = {
wantedBy = [ "multi-user.target" ];
description = "Telegram proverb bot";
enable = true;
enable = false;
script = ''
TELEGRAM_BOT_TOKEN="$(cat "$CREDENTIALS_DIRECTORY/token")" ${pkgs.telebots}/bin/telegram-proverb
'';

View File

@@ -16,9 +16,6 @@ in
nixpkgs = {
config = {
allowUnfree = true;
packageOverrides = pkgs: {
dmenu = pkgs.writers.writeDashBin "dmenu" ''exec ${pkgs.rofi}/bin/rofi -dmenu "$@"'';
};
permittedInsecurePackages = [
];
};
@@ -69,6 +66,8 @@ in
extraGroups = [
"pipewire"
"audio"
"lp"
"scanner"
];
};
@@ -87,14 +86,9 @@ in
swallow = command: "${pkgs.swallow}/bin/swallow ${command}";
in
{
o = "${pkgs.xdg-utils}/bin/xdg-open";
ns = "nix-shell --run zsh";
pbcopy = "${pkgs.wl-clipboard}/bin/wl-copy";
pbpaste = "${pkgs.wl-clipboard}/bin/wl-paste";
tmux = "${pkgs.tmux}/bin/tmux -2";
sxiv = swallow "${pkgs.nsxiv}/bin/nsxiv";
zathura = swallow "${pkgs.zathura}/bin/zathura";
im = "${pkgs.openssh}/bin/ssh weechat@makanek -t tmux attach-session -t IM";
im = "${pkgs.openssh}/bin/ssh weechat@makanek -t screen -x weechat";
yt = "${pkgs.yt-dlp}/bin/yt-dlp --add-metadata -ic"; # Download video link
yta = "${pkgs.yt-dlp}/bin/yt-dlp --add-metadata --audio-format mp3 --audio-quality 0 -xic"; # Download with audio
};
@@ -153,21 +147,12 @@ in
home-manager.backupFileExtension = "bak";
}
{
systemd.user.services.udiskie = {
after = [ "udisks2.service" ];
wants = [ "udisks2.service" ];
wantedBy = [ "graphical-session.target" ];
serviceConfig = {
ExecStart = "${pkgs.udiskie}/bin/udiskie --verbose --no-config --notify";
};
};
services.udisks2.enable = true;
programs.dconf.enable = true;
home-manager.users.me = {
dconf.enable = true;
dconf.settings = {
# Change the default terminal for Nemo
"org/cinnamon/desktop/applications/terminal".exec = lib.getExe pkgs.niveum-terminal;
"org/cinnamon/desktop/applications/terminal".exec = lib.getExe pkgs.niphas-terminal;
};
};
}
@@ -175,31 +160,26 @@ in
./stylix.nix
./alacritty.nix
./backup.nix
./bash.nix
./bluetooth.nix
./aerc.nix
./khal.nix
./browser.nix
./clipboard.nix
./cloud.nix
./direnv.nix
./fonts.nix
./fzf.nix
./git.nix
./hledger.nix
./htop.nix
./lix.nix
./uni.nix
# ./i3.nix
./graphical
./i3status-rust.nix
./keyboard
./kdeconnect.nix
{ services.upower.enable = true; }
./lb.nix
./mpv.nix
./mime.nix
./neovim.nix
./newsboat.nix
./editor.nix
./flameshot.nix
./packages.nix
./virtualization.nix
@@ -211,17 +191,19 @@ in
./ssh.nix
./sound.nix
./sudo.nix
./tmux.nix
./unclutter.nix
./vscode.nix
./wallpaper.nix
./zsh.nix
{
home-manager.users.me.home.file.".zshrc".text = ''
# nothing to see here
'';
}
{
programs.zsh.interactiveShellInit = ''
hash -d nixos=/etc/nixos niveum=${config.users.users.me.home}/sync/src/niveum
'';
home-manager.users.me = {
xdg.userDirs =
let

View File

@@ -1,42 +0,0 @@
{ pkgs, ... }:
let
nixify = pkgs.writers.writeDashBin "nixify" ''
set -efuC
if [ ! -e ./.envrc ]; then
echo use_nix > .envrc
direnv allow
fi
if [ ! -e shell.nix ]; then
cat > shell.nix <<'EOF'
{ pkgs ? import <nixpkgs> {} }:
pkgs.mkShell {
packages = [];
}
EOF
''${EDITOR:-vim} shell.nix
fi
'';
in
{
environment.systemPackages = [
pkgs.direnv
nixify
];
home-manager.users.me.programs.direnv = {
enable = true;
stdlib = builtins.readFile "${
pkgs.fetchFromGitHub {
owner = "Mic92";
repo = "dotfiles";
rev = "a0a9b7e358fa70a85cd468f8ca1fbb02ae0a91df";
sha256 = "1y9h5s1lf59sczsm0ksq2x1yhl98ba9lwk5yil3q53rg7n4574pg";
}
}/home/.direnvrc";
};
programs.zsh.interactiveShellInit = ''
eval "$(${pkgs.direnv}/bin/direnv hook zsh)"
'';
}

View File

@@ -4,20 +4,7 @@
config,
...
}:
let
vim-kmein = (
pkgs.vim-kmein.override {
# stylixColors = config.lib.stylix.colors;
colorscheme = "base16-gruvbox-dark-medium";
}
);
in
{
environment.variables.EDITOR = lib.getExe vim-kmein;
environment.shellAliases.vi = "nvim";
environment.shellAliases.vim = "nvim";
environment.shellAliases.view = "nvim -R";
home-manager.users.me = {
editorconfig = {
enable = true;
@@ -46,13 +33,11 @@ in
environment.systemPackages = [
pkgs.vim-typewriter
pkgs.dawn-editor
vim-kmein
# language servers
pkgs.pyright
pkgs.haskellPackages.haskell-language-server
pkgs.texlab
pkgs.nil
pkgs.gopls
pkgs.nixfmt-rfc-style
pkgs.rust-analyzer

View File

@@ -1,27 +0,0 @@
{ pkgs, ... }:
{
programs.fzf = {
fuzzyCompletion = true;
keybindings = true;
};
home-manager.users.me = {
programs.fzf =
let
defaultCommand = "${pkgs.fd}/bin/fd --type f --strip-cwd-prefix --follow --no-ignore-vcs --exclude .git";
in
{
enable = true;
defaultCommand = defaultCommand;
defaultOptions = [ "--height=40%" ];
changeDirWidgetCommand = "${pkgs.fd}/bin/fd --type d";
changeDirWidgetOptions = [
"--preview='${pkgs.tree}/bin/tree -L 1 {}'"
"--bind=space:toggle-preview"
"--preview-window=hidden"
];
fileWidgetCommand = defaultCommand;
fileWidgetOptions = [ "--preview='head -$LINES {}'" ];
};
};
}

View File

@@ -1,53 +0,0 @@
{
pkgs,
lib,
...
}:
{
environment.systemPackages = [
pkgs.mr
pkgs.gitFull
pkgs.git-crypt
pkgs.gitflow
pkgs.gh
pkgs.git-extras
# pkgs.git-trim
pkgs.git-absorb
pkgs.gitstats
pkgs.patch
pkgs.patchutils
];
environment.shellAliases = {
gf = "git-flow";
g = "git";
};
home-manager.users.me = {
programs.git = {
enable = true;
package = pkgs.gitFull;
settings.alias = {
br = "branch";
co = "checkout";
ci = "commit";
cm = "commit -m";
amend = "commit --amend";
st = "status -s";
unstage = "reset HEAD --";
diffs = "diff --staged";
pushf = "push --force-with-lease";
last = "log -1 HEAD";
logs = "log --pretty=oneline";
graph = "log --graph --abbrev-commit --decorate --date=relative --format=format:'%C(bold blue)%h%C(reset) - %C(bold green)(%ar)%C(reset) %C(white)%s%C(reset) %C(dim white)- %an%C(reset)%C(bold yellow)%d%C(reset)' --all";
};
ignores = pkgs.lib.niveum.ignorePaths;
settings.user.name = pkgs.lib.niveum.kieran.name;
settings.user.email = pkgs.lib.niveum.kieran.email;
settings.pull.ff = "only";
settings.rebase.autoStash = true;
settings.merge.autoStash = true;
settings.push.autoSetupRemove = true;
};
};
}

View File

@@ -4,9 +4,6 @@
config,
...
}:
let
stylixColors = config.lib.stylix.colors;
in
{
programs.hyprland = {
enable = true;
@@ -38,10 +35,8 @@ in
pkgs.xdg-desktop-portal-hyprland
];
services.displayManager.autoLogin = {
enable = true;
user = config.users.users.me.name;
};
services.getty.autologinOnce = true;
services.getty.autologinUser = config.users.users.me.name;
home-manager.users.me = import ./home-manager.nix {
inherit lib pkgs config;

View File

@@ -134,8 +134,6 @@ in
settings.default-timeout = 10 * 1000;
};
services.hyprsunset.enable = true;
programs.ashell = {
enable = true;
settings = {
@@ -197,15 +195,6 @@ in
};
};
services.hyprpaper = {
enable = true;
settings = {
ipc = "on";
splash = false;
preload = [ "${config.users.users.me.home}/.cache/wallpaper/wallpaper" ];
};
};
services.hypridle = {
enable = true;
settings = {
@@ -263,10 +252,8 @@ in
"QT_QPA_PLATFORM=wayland"
"GDK_BACKEND=wayland"
"NIXOS_OZONE_WL=1"
"HYPRSHOT_DIR=${config.home-manager.users.me.xdg.userDirs.download}/screenshots"
];
permission = [
"${lib.getExe pkgs.hyprshot}, screencopy, allow"
"${pkgs.xdg-desktop-portal-hyprland}/libexec/.xdg-desktop-portal-hyprland-wrapped, screencopy, allow"
];
monitor = [
@@ -278,9 +265,9 @@ in
exec-once = [
(lib.getExe pkgs.ashell)
"hyprctl dispatch exec \"[workspace special:${language.obsidian} silent] obsidian\""
"${lib.getExe' pkgs.wl-clipboard "wl-paste"} -t text --watch ${lib.getExe pkgs.clipman} store"
# (lib.getExe pkgs.hyprsunset)
# (lib.getExe pkgs.hyprpaper)
(lib.getExe pkgs.niphas-clipboard-watcher)
(lib.getExe pkgs.niphas-redshift)
(lib.getExe pkgs.niphas-set-wallpaper)
];
device = [
@@ -354,8 +341,7 @@ in
",XF86AudioMicMute, exec, wpctl set-mute @DEFAULT_AUDIO_SOURCE@ toggle"
",XF86MonBrightnessUp, exec, brightnessctl -e4 -n2 set 5%+"
",XF86MonBrightnessDown, exec, brightnessctl -e4 -n2 set 5%-"
", Print, exec, ${lib.getExe pkgs.hyprshot} -m region --clipboard-only"
"${mod}, Print, exec, ${lib.getExe pkgs.hyprshot} -m region"
", Print, exec, ${lib.getExe pkgs.niphas-screenshot}"
];
bindl = [
", XF86AudioNext, exec, playerctl next"
@@ -364,12 +350,12 @@ in
", XF86AudioPrev, exec, playerctl previous"
];
bind = [
"${mod}, Return, exec, ${lib.getExe pkgs.niveum-terminal}"
"${mod}, Return, exec, ${lib.getExe pkgs.niphas-terminal}"
"${mod} SHIFT, Q, killactive,"
"${mod} SHIFT, R, exit,"
"${mod}, t, exec, ${lib.getExe pkgs.niveum-filemanager}"
"${mod}, Y, exec, ${lib.getExe pkgs.niveum-browser}"
"${mod}, Q, exec, ${lib.getExe pkgs.clipman} pick --tool=rofi"
"${mod}, t, exec, ${lib.getExe pkgs.niphas-file-browser}"
"${mod}, Y, exec, ${lib.getExe pkgs.niphas-web-browser}"
"${mod}, Q, exec, ${lib.getExe pkgs.niphas-clipman}"
"${mod}, u, exec, ${lib.getExe pkgs.unicodmenu}"
"${mod}, p, exec, ${lib.getExe pkgs.rofi-pass-wayland}"
"${mod} SHIFT, Z, togglefloating,"
@@ -382,8 +368,6 @@ in
"${mod}, l, movefocus, r"
"${mod}, k, movefocus, u"
"${mod}, j, movefocus, d"
"${mod}, F9, exec, hyprctl hyprsunset temperature -1000"
"${mod}, F10, exec, hyprctl hyprsunset temperature +1000" # reset color temperature
"${mod}, F12, exec, ${klem}/bin/klem"
"${mod} SHIFT, W, exec, hyprlock"

View File

@@ -4,7 +4,6 @@
}:
let
ledgerDirectory = "/home/kfm/sync/src/ledger";
hora = pkgs.callPackage ../packages/hora.nix { timeLedger = "${ledgerDirectory}/time.timeclock"; };
in
{
environment.systemPackages =
@@ -12,7 +11,6 @@ in
git = "${pkgs.git}/bin/git -C ${ledgerDirectory}";
in
[
hora
pkgs.hledger
(pkgs.writers.writeDashBin "hledger-git" ''
if [ "$1" = entry ]; then

View File

@@ -243,9 +243,9 @@ in
"${modifier}+w" = "layout tabbed";
"${modifier}+q" = "exec ${config.services.clipmenu.package}/bin/clipmenu";
"${modifier}+Return" = "exec ${lib.getExe pkgs.niveum-terminal}";
"${modifier}+t" = "exec ${lib.getExe pkgs.niveum-filemanager}";
"${modifier}+y" = "exec ${lib.getExe pkgs.niveum-browser}";
"${modifier}+Return" = "exec ${lib.getExe pkgs.niphas-terminal}";
"${modifier}+t" = "exec ${lib.getExe pkgs.niphas-file-browser}";
"${modifier}+y" = "exec ${lib.getExe pkgs.niphas-web-browser}";
"${modifier}+d" =
"exec ${pkgs.writers.writeDash "run" ''exec rofi -modi run,ssh,window -show run''}";

View File

@@ -1,85 +0,0 @@
{
pkgs,
config,
...
}:
{
age.secrets = {
miniflux-api-token = {
file = ../secrets/miniflux-api-token.age;
owner = config.users.users.me.name;
group = config.users.users.me.group;
mode = "400";
};
};
home-manager.users.me = {
programs.i3status-rust = {
enable = true;
bars.bottom = {
icons = "awesome6";
settings = {
theme.overrides =
let
colours = config.lib.stylix.colors.withHashtag;
in
{
idle_bg = colours.base00;
idle_fg = colours.base05;
good_bg = colours.base00;
good_fg = colours.base0B;
warning_bg = colours.base00;
warning_fg = colours.base0A;
critical_bg = colours.base00;
critical_fg = colours.base09;
info_bg = colours.base00;
info_fg = colours.base04;
separator_bg = colours.base00;
separator = " ";
};
};
blocks = [
{
block = "music";
format = "{$icon $combo $play |}";
separator = " ";
}
{
block = "net";
format = " $icon HU";
missing_format = "";
device = "ppp0";
}
{
block = "net";
format = " $icon FU";
missing_format = "";
device = "tun0";
}
{
block = "battery";
format = "$icon $percentage $time";
device = "DisplayDevice";
driver = "upower";
}
{
block = "sound";
}
{
block = "disk_space";
format = "$icon $available";
}
{
block = "memory";
format = "$icon $mem_used.eng(prefix:G)";
}
{ block = "load"; }
{
block = "time";
format = "$icon $timestamp.datetime(f:'%Y-%m-%d (%W %a) %H:%M', l:de_DE)";
}
];
};
};
};
}

View File

@@ -1,13 +1,11 @@
{
pkgs,
lib,
...
}:
{
nixpkgs = {
config.allowUnfree = true;
};
nix = {
package = pkgs.nixVersions.stable;
package = lib.mkForce pkgs.lix;
settings.experimental-features = [ "nix-command" "flakes" ];
};
}

View File

@@ -1,49 +1,42 @@
{
pkgs,
lib,
self,
config,
...
}:
let
swallow = command: "${pkgs.swallow}/bin/swallow ${command}";
myMpv =
pkgs:
self.inputs.wrappers.wrapperModules.mpv.apply {
inherit pkgs;
scripts = [
pkgs.mpvScripts.visualizer
];
"mpv.conf".content = "";
"mpv.input".content = ''
Alt+- add video-zoom -0.25
Alt+= add video-zoom 0.25
Alt+LEFT add video-rotate -90
Alt+RIGHT add video-rotate 90
Alt+h add video-pan-x 0.05
Alt+j add video-pan-y -0.05
Alt+k add video-pan-y 0.05
Alt+l add video-pan-x -0.05
'';
};
in
{
environment.shellAliases.smpv = swallow "mpv";
nixpkgs.overlays = [
(self: super: {
mpv = config.home-manager.users.me.programs.mpv.finalPackage;
(final: prev: {
mpv = (myMpv prev).wrapper;
})
];
home-manager.users.me = {
programs.mpv = {
enable = true;
config = {
ytdl-format = "bestvideo[height<=?720][fps<=?30][vcodec!=?vp9]+bestaudio/best";
ytdl-raw-options = lib.concatStringsSep "," [
''sub-lang="de,en"''
"write-sub="
"write-auto-sub="
];
screenshot-template = "%F-%wH%wM%wS-%#04n";
script-opts = "ytdl_hook-ytdl_path=${pkgs.yt-dlp}/bin/yt-dlp";
ao = "pulse"; # no pipewire for me :(
};
bindings = {
"Alt+RIGHT" = "add video-rotate 90";
"Alt+LEFT" = "add video-rotate -90";
"Alt+-" = "add video-zoom -0.25";
"Alt+=" = "add video-zoom 0.25";
"Alt+l" = "add video-pan-x -0.05";
"Alt+h" = "add video-pan-x 0.05";
"Alt+k" = "add video-pan-y 0.05";
"Alt+j" = "add video-pan-y -0.05";
};
scripts = [
# pkgs.mpvScripts.quality-menu
# pkgs.mpvScripts.visualizer
];
};
};
environment.systemPackages = [
((myMpv pkgs).wrapper)
];
}

View File

@@ -1,18 +0,0 @@
{
pkgs,
config,
...
}:
{
environment.systemPackages = [
(pkgs.writers.writeDashBin "miniflux-watch-later" ''
miniflux_api_token=$(cat ${config.age.secrets.miniflux-api-token.path})
random_feed_item=$(
${pkgs.curl}/bin/curl -u "$miniflux_api_token" --basic -s 'https://feed.kmein.de/v1/entries?starred=true&limit=0' \
| ${pkgs.jq}/bin/jq -r '.entries[].id' \
| ${pkgs.coreutils}/bin/shuf -n1
)
${pkgs.xdg-utils}/bin/xdg-open "https://feed.kmein.de/starred/entry/$random_feed_item"
'')
];
}

View File

@@ -70,23 +70,16 @@ in
GPODDER_DOWNLOAD_DIR=${config.users.users.me.home}/mobile/audio/Text/podcasts exec ${pkgs.gpodder}/bin/gpodder "$@"
'')
# INTERNET
aria2
telegram-desktop
whois
dnsutils
# FILE MANAGERS
lf
pcmanfm
# MEDIA
ffmpeg
simplescreenrecorder
imagemagick
exiftool
nsxiv
graphviz
# SHELL
bat # better cat
dos2unix
genpass # generate passwords
(pkgs.writers.writeDashBin "genpassphrase" ''${pkgs.genpass}/bin/genpass "$@" --passphrase | ${pkgs.gnused}/bin/sed 's/ /-/g;s/\(^\|-\)\([a-z]\)/\1\U\2/g;s/$/-'$(${pkgs.coreutils}/bin/date +%Y)'/' '')
gcc
@@ -94,16 +87,10 @@ in
pup # html toolkit
xan # csv toolkit
magic-wormhole-rs # file transfer
man-pages
man-pages-posix
exfat # to mount windows drives
# HARDWARE TOOLS
gnome-disk-utility
arandr # xrandr for noobs
wdisplays
libnotify # for notify-send
wl-clipboard # clipboard CLI
dragon-drop # drag and drop
portfolio # personal finance overview
audacity
calibre
@@ -132,6 +119,7 @@ in
pdfpc # presenter console for pdf slides
hc # print files as qr codes
yt-dlp
yt-dlp-master
espeak
rink # unit converter
auc
@@ -141,6 +129,7 @@ in
polyglot
qrpaste
ttspaste
pi # llm agent
new-mac # get a new mac address
scanned
default-gateway
@@ -176,14 +165,11 @@ in
di-fm-key-file = config.age.secrets.di-fm-key.path;
})
# kmein.slide
termdown
termdown # countdown timer in terminal
image-convert-tolino
rfc
tag
timer
nix-prefetch-git
nix-git
nixfmt-rfc-style
comma
par
qrencode
@@ -207,6 +193,8 @@ in
#krebs
pkgs.nur.repos.mic92.ircsink
cyberlocker-tools
kpaste
(haskellPackages.ghcWithHoogle (hs: [
hs.text
@@ -256,10 +244,6 @@ in
# proselint
asciidoctor
wordnet
tokei # count lines of code
gnumake
binutils # for strip, ld, ...
# nightly.rust
shellcheck
# photography

View File

@@ -1,6 +1,6 @@
{ pkgs, lib, ... }:
let
hp-driver = pkgs.hplip;
hp-driver = pkgs.hplipWithPlugin;
in
{
services.printing = {
@@ -8,12 +8,25 @@ in
drivers = [ hp-driver ];
};
hardware.sane = {
enable = true;
extraBackends = [ hp-driver ];
};
environment.systemPackages = [
pkgs.system-config-printer
pkgs.simple-scan
hp-driver
];
# allow connecting to .local printers
services.avahi.nssmdns4 = true;
services.avahi = {
nssmdns4 = true;
enable = true;
openFirewall = true;
};
# users.users.me.extraGroups is set in configs/default.nix which defines the "me" user
hardware.printers.ensurePrinters = [
{

View File

@@ -1,44 +0,0 @@
{ pkgs, ... }:
{
environment.systemPackages = [
pkgs.tmuxp
pkgs.reptyr # move programs over to a tmux session
];
programs.tmux = {
enable = true;
keyMode = "vi";
clock24 = true;
terminal = "screen-256color";
baseIndex = 1;
aggressiveResize = true;
escapeTime = 50;
historyLimit = 7000;
shortcut = "b";
extraConfig = ''
set -g mouse on
unbind *
bind * list-clients
# naVIgate
bind h select-pane -L
bind j select-pane -D
bind k select-pane -U
bind l select-pane -R
# Use C-h and C-l to cycle through panes
bind -r C-h select-window -t :-
bind -r C-l select-window -t :+
setw -g monitor-activity on
set -g visual-activity on
set -g status-interval 2
set -g status-left-length 32
set -g status-right-length 150
set -g status-position bottom
'';
};
}

View File

@@ -1,98 +0,0 @@
{
config,
pkgs,
...
}:
let
promptColours.success = "cyan";
promptColours.failure = "red";
in
{
programs.zsh =
let
zsh-completions = pkgs.fetchFromGitHub {
owner = "zsh-users";
repo = "zsh-completions";
rev = "cf565254e26bb7ce03f51889e9a29953b955b1fb";
sha256 = "1yf4rz99acdsiy0y1v3bm65xvs2m0sl92ysz0rnnrlbd5amn283l";
};
in
{
enable = true;
enableCompletion = true;
autosuggestions.enable = true;
syntaxHighlighting.enable = true;
syntaxHighlighting.highlighters = [
"main"
"brackets"
"pattern"
"line"
];
interactiveShellInit = ''
setopt INTERACTIVE_COMMENTS CORRECT
setopt MULTIOS
setopt AUTO_NAME_DIRS
setopt AUTOCD CDABLE_VARS
setopt HIST_IGNORE_ALL_DUPS
setopt VI
setopt AUTO_MENU
setopt COMPLETE_IN_WORD
setopt ALWAYS_TO_END
unsetopt NOMATCH
unsetopt MENU_COMPLETE
zstyle ':completion:*:*:*:*:*' menu select
zstyle ':completion:*' matcher-list 'm:{a-zA-Z-_}={A-Za-z_-}' 'r:|=*' 'l:|=* r:|=*'
zstyle ':completion:*' special-dirs true
zstyle ':completion:*' list-colors \'\'
zstyle ':completion:*:*:kill:*:processes' list-colors '=(#b) #([0-9]#) ([0-9a-z-]#)*=01;34=0=01'
zstyle ':completion:*:*:*:*:processes' command "ps -u $USER -o pid,user,comm -w -w"
zstyle ':completion:*:cd:*' tag-order local-directories directory-stack path-directories
export KEYTIMEOUT=1
hash -d nixos=/etc/nixos niveum=${config.users.users.me.home}/sync/src/niveum
autoload -U zmv run-help edit-command-line
fpath=(${zsh-completions}/src $fpath)
'';
promptInit = ''
autoload -Uz vcs_info
zstyle ':vcs_info:*' enable git
zstyle ':vcs_info:*' check-for-changes true
zstyle ':vcs_info:*' stagedstr '%F{green}+%f'
zstyle ':vcs_info:*' unstagedstr '%F{red}~%f'
zstyle ':vcs_info:*' use-prompt-escapes true
zstyle ':vcs_info:*' formats "%c%u%F{cyan}%b%f"
zstyle ':vcs_info:*' actionformats "(%a) %c%u%F{cyan}%b%f"
precmd () {
vcs_info
if [ -n "$SSH_CLIENT" ] || [ -n "$SSH_TTY" ] || [ -n "$SSH_CONNECTION" ]; then
RPROMPT="$(hostname)"
else
RPROMPT="$vcs_info_msg_0_"
fi
if [[ -n $IN_NIX_SHELL ]]; then
PROMPT='%B%~%b %(?.%F{${promptColours.success}}.%F{${promptColours.failure}})λ%f '
else
PROMPT='%B%~%b %(?.%F{${promptColours.success}}.%F{${promptColours.failure}})%#%f '
fi
print -Pn "\e]2;%n@%M:%~\a" # title bar prompt
}
zle-keymap-select zle-line-init () {
case $KEYMAP in
vicmd) print -n '\e]12;green\a';;
viins|main) print -n '\e]12;gray\a';;
esac
}
zle -N zle-line-init
zle -N zle-keymap-select
zle -N edit-command-line
bindkey -M vicmd v edit-command-line
'';
};
}

329
flake.lock generated
View File

@@ -113,6 +113,28 @@
"type": "github"
}
},
"blueprint": {
"inputs": {
"nixpkgs": [
"llm-agents",
"nixpkgs"
],
"systems": "systems_2"
},
"locked": {
"lastModified": 1769353768,
"narHash": "sha256-zI+7cbMI4wMIR57jMjDSEsVb3grapTnURDxxJPYFIW0=",
"owner": "numtide",
"repo": "blueprint",
"rev": "c7da5c70ad1c9b60b6f5d4f674fbe205d48d8f6c",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "blueprint",
"type": "github"
}
},
"buildbot-nix": {
"inputs": {
"flake-parts": "flake-parts_3",
@@ -121,7 +143,7 @@
"stockholm",
"nixpkgs"
],
"treefmt-nix": "treefmt-nix"
"treefmt-nix": "treefmt-nix_3"
},
"locked": {
"lastModified": 1768927382,
@@ -376,6 +398,26 @@
"type": "github"
}
},
"llm-agents": {
"inputs": {
"blueprint": "blueprint",
"nixpkgs": "nixpkgs",
"treefmt-nix": "treefmt-nix"
},
"locked": {
"lastModified": 1771167156,
"narHash": "sha256-hvlg7rTzAmfX2HW0GgrVGvbXoNioTK0bidbRv42QEhY=",
"owner": "numtide",
"repo": "llm-agents.nix",
"rev": "bbd22c02ac546b7ba07147eb14194128b44ff209",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "llm-agents.nix",
"type": "github"
}
},
"menstruation-backend": {
"inputs": {
"fenix": [
@@ -422,6 +464,24 @@
"type": "github"
}
},
"meteora": {
"inputs": {
"nixpkgs": "nixpkgs_2"
},
"locked": {
"lastModified": 1772641269,
"narHash": "sha256-o8G09GrqHjZO9BYbsCvYds+baGxLC0PhhlGTllkHC54=",
"ref": "refs/heads/master",
"rev": "0b2255c2e4003b208f19153e6fb7e484544c4399",
"revCount": 1377,
"type": "git",
"url": "ssh://git@github.com/kmein/meteora.git"
},
"original": {
"type": "git",
"url": "ssh://git@github.com/kmein/meteora.git"
}
},
"naersk": {
"inputs": {
"fenix": [
@@ -445,6 +505,28 @@
"type": "github"
}
},
"niphas": {
"inputs": {
"nixpkgs": [
"nixpkgs-unstable"
],
"treefmt-nix": "treefmt-nix_2",
"wrappers": "wrappers"
},
"locked": {
"lastModified": 1771601908,
"narHash": "sha256-lqscsSHms5xk8iOOEj0J6XtrIcZp7/TXN4iiQjNeXzM=",
"ref": "refs/heads/master",
"rev": "13ee868d5d297fbcfa1370cfff67e5c7f5e3d0aa",
"revCount": 42,
"type": "git",
"url": "https://code.kmein.de/kfm/niphas"
},
"original": {
"type": "git",
"url": "https://code.kmein.de/kfm/niphas"
}
},
"nix-index-database": {
"inputs": {
"nixpkgs": [
@@ -525,16 +607,16 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1769598131,
"narHash": "sha256-e7VO/kGLgRMbWtpBqdWl0uFg8Y2XWFMdz0uUJvlML8o=",
"lastModified": 1770843696,
"narHash": "sha256-LovWTGDwXhkfCOmbgLVA10bvsi/P8eDDpRudgk68HA8=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "fa83fd837f3098e3e678e6cf017b2b36102c7211",
"rev": "2343bbb58f99267223bc2aac4fc9ea301a155a16",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-25.11",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
@@ -570,6 +652,70 @@
"type": "github"
}
},
"nixpkgs-unstable": {
"locked": {
"lastModified": 1770197578,
"narHash": "sha256-AYqlWrX09+HvGs8zM6ebZ1pwUqjkfpnv8mewYwAo+iM=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "00c21e4c93d963c50d4c0c89bfa84ed6e0694df2",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_2": {
"locked": {
"lastModified": 1772479524,
"narHash": "sha256-u7nCaNiMjqvKpE+uZz9hE7pgXXTmm5yvdtFaqzSzUQI=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "4215e62dc2cd3bc705b0a423b9719ff6be378a43",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_3": {
"locked": {
"lastModified": 1769598131,
"narHash": "sha256-e7VO/kGLgRMbWtpBqdWl0uFg8Y2XWFMdz0uUJvlML8o=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "fa83fd837f3098e3e678e6cf017b2b36102c7211",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-25.11",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_4": {
"locked": {
"lastModified": 1771369470,
"narHash": "sha256-0NBlEBKkN3lufyvFegY4TYv5mCNHbi5OmBDrzihbBMQ=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "0182a361324364ae3f436a63005877674cf45efb",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nur": {
"inputs": {
"flake-parts": "flake-parts_2",
@@ -616,6 +762,47 @@
"type": "github"
}
},
"opencrow": {
"inputs": {
"nixpkgs": [
"nixpkgs"
],
"treefmt-nix": [
"treefmt-nix"
]
},
"locked": {
"lastModified": 1771622489,
"narHash": "sha256-CGAS5ISs+h6GNQwaOLycfbcFRkN0legi/hdDov4Obfk=",
"owner": "pinpox",
"repo": "opencrow",
"rev": "9ec2d17e6c9d45b22b9cca3174b6b1a75758d8f6",
"type": "github"
},
"original": {
"owner": "pinpox",
"repo": "opencrow",
"type": "github"
}
},
"panoptikon": {
"inputs": {
"nixpkgs": "nixpkgs_4"
},
"locked": {
"lastModified": 1771686951,
"narHash": "sha256-+s19xrH0kVVDT48TtpulmuWFMagi/ysjWL+PjKcmF/k=",
"ref": "refs/heads/main",
"rev": "8868eb8736c3395186fa410c040e0000abca56bd",
"revCount": 3,
"type": "git",
"url": "https://code.kmein.de/kfm/panoptikon"
},
"original": {
"type": "git",
"url": "https://code.kmein.de/kfm/panoptikon"
}
},
"retiolum": {
"locked": {
"lastModified": 1756302470,
@@ -637,25 +824,31 @@
"autorenkalender": "autorenkalender",
"fenix": "fenix",
"home-manager": "home-manager",
"llm-agents": "llm-agents",
"menstruation-backend": "menstruation-backend",
"menstruation-telegram": "menstruation-telegram",
"meteora": "meteora",
"naersk": "naersk",
"niphas": "niphas",
"nix-index-database": "nix-index-database",
"nix-topology": "nix-topology",
"nixos-hardware": "nixos-hardware",
"nixpkgs": "nixpkgs",
"nixpkgs": "nixpkgs_3",
"nixpkgs-old": "nixpkgs-old",
"nixpkgs-unstable": "nixpkgs-unstable",
"nur": "nur",
"opencrow": "opencrow",
"panoptikon": "panoptikon",
"retiolum": "retiolum",
"scripts": "scripts",
"stockholm": "stockholm",
"stylix": "stylix",
"telebots": "telebots",
"tinc-graph": "tinc-graph",
"treefmt-nix": "treefmt-nix_2",
"treefmt-nix": "treefmt-nix_4",
"voidrice": "voidrice",
"wallpapers": "wallpapers",
"wetter": "wetter"
"wetter": "wetter",
"wrappers": "wrappers_2"
}
},
"rust-analyzer-src": {
@@ -736,7 +929,7 @@
"nixpkgs"
],
"nur": "nur_2",
"systems": "systems_2",
"systems": "systems_3",
"tinted-foot": "tinted-foot",
"tinted-kitty": "tinted-kitty",
"tinted-schemes": "tinted-schemes",
@@ -788,6 +981,21 @@
"type": "github"
}
},
"systems_3": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
},
"telebots": {
"inputs": {
"nixpkgs": [
@@ -916,6 +1124,48 @@
}
},
"treefmt-nix": {
"inputs": {
"nixpkgs": [
"llm-agents",
"nixpkgs"
]
},
"locked": {
"lastModified": 1770228511,
"narHash": "sha256-wQ6NJSuFqAEmIg2VMnLdCnUc0b7vslUohqqGGD+Fyxk=",
"owner": "numtide",
"repo": "treefmt-nix",
"rev": "337a4fe074be1042a35086f15481d763b8ddc0e7",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "treefmt-nix",
"type": "github"
}
},
"treefmt-nix_2": {
"inputs": {
"nixpkgs": [
"niphas",
"nixpkgs"
]
},
"locked": {
"lastModified": 1769691507,
"narHash": "sha256-8aAYwyVzSSwIhP2glDhw/G0i5+wOrren3v6WmxkVonM=",
"owner": "numtide",
"repo": "treefmt-nix",
"rev": "28b19c5844cc6e2257801d43f2772a4b4c050a1b",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "treefmt-nix",
"type": "github"
}
},
"treefmt-nix_3": {
"inputs": {
"nixpkgs": [
"stockholm",
@@ -937,7 +1187,7 @@
"type": "github"
}
},
"treefmt-nix_2": {
"treefmt-nix_4": {
"inputs": {
"nixpkgs": [
"nixpkgs"
@@ -973,22 +1223,6 @@
"type": "github"
}
},
"wallpapers": {
"flake": false,
"locked": {
"lastModified": 1589319718,
"narHash": "sha256-2NruGq3z37vY3uAH8S4sLqHvFAGi8gaDJAgEzMIvM/4=",
"owner": "kmein",
"repo": "wallpapers",
"rev": "7c553bc6bd78afa6dbf2824691466bbad0d8e6e9",
"type": "github"
},
"original": {
"owner": "kmein",
"repo": "wallpapers",
"type": "github"
}
},
"wetter": {
"inputs": {
"nixpkgs": [
@@ -1008,6 +1242,47 @@
"repo": "wetter",
"type": "github"
}
},
"wrappers": {
"inputs": {
"nixpkgs": [
"niphas",
"nixpkgs"
]
},
"locked": {
"lastModified": 1770112394,
"narHash": "sha256-H8d0WplmDeuvOM4bsHNt77T6OWiSJMaTP6UG1XyMNxA=",
"owner": "Lassulus",
"repo": "wrappers",
"rev": "23625835eb91d925c498780f5a37442ead1ae8e5",
"type": "github"
},
"original": {
"owner": "Lassulus",
"repo": "wrappers",
"type": "github"
}
},
"wrappers_2": {
"inputs": {
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1770311206,
"narHash": "sha256-gzTvuaJZaymgxQC4rOZ9HlMRRWHVF2moEEaTnCG556A=",
"owner": "lassulus",
"repo": "wrappers",
"rev": "241f2f7dfcac0dbb2338105bdba7f03f412c5847",
"type": "github"
},
"original": {
"owner": "lassulus",
"repo": "wrappers",
"type": "github"
}
}
},
"root": "root",

140
flake.nix
View File

@@ -2,8 +2,6 @@
description = "niveum: packages, modules, systems";
inputs = {
self.submodules = true;
agenix.url = "github:ryantm/agenix";
autorenkalender.url = "github:kmein/autorenkalender";
home-manager.url = "github:nix-community/home-manager/release-25.11";
@@ -12,6 +10,9 @@
nix-index-database.url = "github:nix-community/nix-index-database";
nixpkgs-old.url = "github:NixOS/nixpkgs/50fc86b75d2744e1ab3837ef74b53f103a9b55a0";
nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.11";
nixpkgs-unstable.url = "github:NixOS/nixpkgs/nixos-unstable";
niphas.url = "git+https://code.kmein.de/kfm/niphas";
panoptikon.url = "git+https://code.kmein.de/kfm/panoptikon";
nixos-hardware.url = "github:NixOS/nixos-hardware";
nur.url = "github:nix-community/NUR";
retiolum.url = "github:krebs/retiolum";
@@ -22,12 +23,14 @@
tinc-graph.url = "github:kmein/tinc-graph";
treefmt-nix.url = "github:numtide/treefmt-nix";
voidrice.url = "github:Lukesmithxyz/voidrice";
wallpapers.url = "github:kmein/wallpapers";
nix-topology.url = "github:oddlama/nix-topology";
wetter.url = "github:4z3/wetter";
wrappers.url = "github:lassulus/wrappers";
llm-agents.url = "github:numtide/llm-agents.nix";
opencrow.url = "github:pinpox/opencrow";
meteora.url = "git+ssh://git@github.com/kmein/meteora.git";
voidrice.flake = false;
wallpapers.flake = false;
naersk.url = "github:nix-community/naersk";
fenix.url = "github:nix-community/fenix";
@@ -45,6 +48,8 @@
agenix.inputs.home-manager.follows = "home-manager";
opencrow.inputs.treefmt-nix.follows = "treefmt-nix";
agenix.inputs.nixpkgs.follows = "nixpkgs";
autorenkalender.inputs.nixpkgs.follows = "nixpkgs";
home-manager.inputs.nixpkgs.follows = "nixpkgs";
@@ -60,6 +65,9 @@
stylix.inputs.nixpkgs.follows = "nixpkgs";
tinc-graph.inputs.nixpkgs.follows = "nixpkgs";
wetter.inputs.nixpkgs.follows = "nixpkgs";
niphas.inputs.nixpkgs.follows = "nixpkgs-unstable";
wrappers.inputs.nixpkgs.follows = "nixpkgs";
opencrow.inputs.nixpkgs.follows = "nixpkgs";
};
outputs =
@@ -75,15 +83,21 @@
scripts,
tinc-graph,
nix-topology,
llm-agents,
opencrow,
nixpkgs-unstable,
nixos-hardware,
niphas,
treefmt-nix,
autorenkalender,
telebots,
stockholm,
panoptikon,
nix-index-database,
stylix,
voidrice,
wetter,
meteora,
...
}:
let
@@ -161,7 +175,7 @@
${pkgs.nixos-rebuild-ng}/bin/nixos-rebuild-ng switch \
--max-jobs 2 \
--log-format internal-json \
--flake .#${hostname} \
--flake .?submodules=1#${hostname} \
--use-substitutes \
--target-host "$target" \
${lib.optionalString (localSystem != machines.${hostname}.system) "--build-host $target"} \
@@ -183,7 +197,6 @@
nixosModules = {
moodle-dl = import modules/moodle-dl.nix;
passport = import modules/passport.nix;
panoptikon = import modules/panoptikon.nix;
power-action = import modules/power-action.nix;
system-dependent = import modules/system-dependent.nix;
telegram-bot = import modules/telegram-bot.nix;
@@ -191,9 +204,24 @@
};
overlays.default = final: prev: {
niveum-terminal = prev.alacritty;
niveum-browser = prev.firefox;
niveum-filemanager = prev.pcmanfm;
# packaged from .bin/
two56color = prev.callPackage packages/256color.nix { };
avesta = prev.callPackage packages/avesta.nix { };
bvg = prev.callPackage packages/bvg.nix { };
charinfo = prev.callPackage packages/charinfo.nix { };
chunk-pdf = prev.callPackage packages/chunk-pdf.nix { };
csv2json = prev.callPackage packages/csv2json.nix { };
fix-sd = prev.callPackage packages/fix-sd.nix { };
json2csv = prev.callPackage packages/json2csv.nix { };
mp3player-write = prev.callPackage packages/mp3player-write.nix { };
mushakkil = prev.callPackage packages/mushakkil.nix { };
nix-haddock-index = prev.callPackage packages/nix-haddock-index.nix { };
pdf-ocr = prev.callPackage packages/pdf-ocr.nix { };
prospekte = prev.callPackage packages/prospekte.nix { };
readme = prev.callPackage packages/readme.nix { };
ashell = nixpkgs-unstable.legacyPackages.${prev.system}.ashell;
# wrapped from upstream
wrapScript =
@@ -229,7 +257,7 @@
auc = prev.callPackage packages/auc.nix { };
cheat-sh = prev.callPackage packages/cheat-sh.nix { };
brassica = prev.callPackage packages/brassica.nix { }; # TODO upstream
dawn-editor = prev.callPackage packages/dawn.nix {};
dawn-editor = prev.callPackage packages/dawn.nix { };
text2pdf = prev.callPackage packages/text2pdf.nix { }; # TODO upstream
wttr = prev.callPackage packages/wttr.nix { }; # TODO upstream
jsesh = prev.callPackage packages/jsesh.nix { }; # TODO upstream
@@ -237,15 +265,10 @@
trans = prev.callPackage packages/trans.nix { }; # TODO upstream
go-webring = prev.callPackage packages/go-webring.nix { }; # TODO upstream
stag = prev.callPackage packages/stag.nix { }; # TODO upstream
mpv = prev.mpv.override {
scripts = [
# final.mpvScripts.visualizer
final.mpvScripts.mpris
];
};
morris = prev.callPackage packages/morris.nix { };
cro = prev.callPackage packages/cro.nix { };
exodus = prev.callPackage packages/exodus.nix { };
picoclaw = prev.callPackage packages/picoclaw.nix { };
dmenu = prev.writers.writeDashBin "dmenu" ''exec ${final.rofi}/bin/rofi -dmenu "$@"'';
weechatScripts = prev.weechatScripts // {
hotlist2extern = prev.callPackage packages/weechatScripts/hotlist2extern.nix { }; # TODO upstream
@@ -266,6 +289,7 @@
};
# packaged from inputs
opencrow = opencrow.packages.${prev.stdenv.hostPlatform.system}.opencrow;
wetter = wetter.packages.${prev.stdenv.hostPlatform.system}.wetter;
agenix = agenix.packages.${prev.stdenv.hostPlatform.system}.default;
pun-sort-api = scripts.packages.${prev.stdenv.hostPlatform.system}.pun-sort-api;
@@ -275,10 +299,12 @@
menstruation-backend =
menstruation-backend.packages.${prev.stdenv.hostPlatform.system}.menstruation-backend;
telebots = telebots.packages.${prev.stdenv.hostPlatform.system}.telebots;
pi-llm = llm-agents.packages.${prev.stdenv.hostPlatform.system}.pi;
hesychius = scripts.packages.${prev.stdenv.hostPlatform.system}.hesychius;
autorenkalender = autorenkalender.packages.${prev.stdenv.hostPlatform.system}.default;
onomap = scripts.packages.${prev.stdenv.hostPlatform.system}.onomap;
tinc-graph = tinc-graph.packages.${prev.stdenv.hostPlatform.system}.tinc-graph;
meteora-website = meteora.packages.${prev.stdenv.hostPlatform.system}.website;
# krebs
brainmelter = prev.callPackage packages/brainmelter.nix { };
@@ -288,6 +314,7 @@
radio-news = prev.callPackage packages/radio-news { };
untilport = prev.callPackage packages/untilport.nix { };
weechat-declarative = prev.callPackage packages/weechat-declarative.nix { };
pi = prev.callPackage packages/pi.nix { };
# my packages
betacode = prev.callPackage packages/betacode.nix { };
@@ -338,18 +365,14 @@
unicodmenu = prev.callPackage packages/unicodmenu.nix { };
vg = prev.callPackage packages/vg.nix { };
vim-kmein = prev.callPackage packages/vim-kmein { };
vimv = prev.callPackage packages/vimv.nix { };
klem = prev.callPackage packages/klem.nix { };
yt-dlp-master = prev.callPackage packages/yt-dlp-master.nix { };
lib = lib // {
niveum = import lib/default.nix {
inherit lib;
pkgs = final;
};
panoptikon = import lib/panoptikon.nix {
inherit lib;
pkgs = final;
};
};
};
@@ -357,7 +380,26 @@
let
profiles.default = [
{ nix.nixPath = [ "nixpkgs=${nixpkgs}" ]; }
{ nixpkgs.overlays = [ self.overlays.default ]; }
{
nixpkgs.overlays = [
self.overlays.default
niphas.overlays.default
panoptikon.overlays.default
(final: prev: {
niphas-git =
(prev.niphas-git.passthru.configuration.apply {
settings = {
user.name = prev.lib.niveum.kieran.name;
user.email = prev.lib.niveum.kieran.email;
};
}).wrapper;
niphas-editor = prev.niphas-editor.override {
withCopilot = true;
colorscheme = "base16-gruvbox-dark-medium";
};
})
];
}
{
system.autoUpgrade = {
enable = true;
@@ -372,15 +414,19 @@
agenix.nixosModules.default
retiolum.nixosModules.retiolum
nix-topology.nixosModules.default
niphas.nixosModules.nix
niphas.nixosModules.shell
configs/mycelium.nix
configs/tor.nix
configs/retiolum.nix
configs/spacetime.nix
configs/nix.nix
configs/sshd.nix
configs/admin-essentials.nix
];
profiles.desktop = [
niphas.nixosModules.editor
niphas.nixosModules.git
niphas.nixosModules.udiskie
niphas.nixosModules.desktop
home-manager.nixosModules.home-manager
nix-index-database.nixosModules.default
nur.modules.nixos.default
@@ -397,20 +443,28 @@
{
ful = nixpkgs.lib.nixosSystem {
system = "aarch64-linux";
specialArgs = { inherit self; };
modules =
profiles.default
++ profiles.server
++ [
systems/ful/configuration.nix
self.nixosModules.panoptikon
panoptikon.nixosModules.default
self.nixosModules.go-webring
stockholm.nixosModules.reaktor2
opencrow.nixosModules.default
nur.modules.nixos.default
{ nixpkgs.overlays = [ stockholm.overlays.default ]; }
{
nixpkgs.overlays = [
stockholm.overlays.default
llm-agents.overlays.default
];
}
];
};
zaatar = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
specialArgs = { inherit self; };
modules =
profiles.default
++ profiles.server
@@ -420,6 +474,7 @@
};
kibbeh = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
specialArgs = { inherit self; };
modules =
profiles.default
++ profiles.desktop
@@ -429,6 +484,7 @@
};
makanek = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
specialArgs = { inherit self; };
modules =
profiles.default
++ profiles.server
@@ -440,38 +496,43 @@
};
tahina = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
specialArgs = { inherit self; };
modules = profiles.default ++ [
systems/tahina/configuration.nix
];
};
tabula = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
specialArgs = { inherit self; };
modules = profiles.default ++ [
systems/tabula/configuration.nix
];
};
manakish = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
specialArgs = { inherit self; };
modules =
profiles.default
++ profiles.desktop
++ [
systems/manakish/configuration.nix
nixos-hardware.nixosModules.lenovo-thinkpad-x230
nixos-hardware.nixosModules.lenovo-thinkpad-x220
];
};
kabsa = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
specialArgs = { inherit self; };
modules =
profiles.default
++ profiles.desktop
++ [
systems/kabsa/configuration.nix
nixos-hardware.nixosModules.lenovo-thinkpad-x220
nixos-hardware.nixosModules.lenovo-thinkpad-x230
];
};
fatteh = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
specialArgs = { inherit self; };
modules =
profiles.default
++ profiles.desktop
@@ -508,14 +569,20 @@
];
};
inherit (pkgs)
two56color
avesta
auc
betacode
booksplit
brainmelter
brassica
bvg
charinfo
cheat-sh
chunk-pdf
closest
cro
csv2json
cyberlocker-tools
dawn-editor
default-gateway
@@ -526,6 +593,7 @@
emailmenu
exodus
fkill
fix-sd
fzfmenu
gfs-fonts
bring-out-the-gimp
@@ -536,6 +604,7 @@
image-convert-tolino
ipa
jsesh
json2csv
kirciuoklis
klem
kpaste
@@ -544,23 +613,31 @@
mansplain
manual-sort
morris
mp3player-write
mpv-iptv
mpv-radio
mpv-tuner
mpv-tv
mushakkil
new-mac
niveum-ssh
nix-git
nix-haddock-index
noise-waves
notemenu
obsidian-vim
opustags
pdf-ocr
pi
picoclaw
pls
polyglot
prospekte
q
qrpaste
radio-news
random-zeno
readme
rfc
scanned
stag
@@ -578,11 +655,16 @@
vim-kmein
vim-typewriter
vim-email
vimv
weechat-declarative
wttr
yt-dlp-master
;
}
);
};
nixConfig = {
extra-substituters = [ "https://cache.numtide.com" ];
extra-trusted-public-keys = [ "niks3.numtide.com-1:DTx8wZduET09hRmMtKdQDxNNthLQETkc/yaX7M4qK0g=" ];
};
}

View File

@@ -1,47 +0,0 @@
{
pkgs,
lib,
...
}:
{
# watcher scripts
url =
address:
pkgs.writers.writeDash "watch-url" ''
${pkgs.curl}/bin/curl -sSL ${lib.escapeShellArg address} \
| ${pkgs.python3Packages.html2text}/bin/html2text --decode-errors=ignore
'';
urlSelector =
selector: address:
pkgs.writers.writeDash "watch-url-selector" ''
${pkgs.curl}/bin/curl -sSL ${lib.escapeShellArg address} \
| ${pkgs.htmlq}/bin/htmlq ${lib.escapeShellArg selector} \
| ${pkgs.python3Packages.html2text}/bin/html2text
'';
urlJSON =
{
jqScript ? ".",
}:
address:
pkgs.writers.writeDash "watch-url-json" ''
${pkgs.curl}/bin/curl -sSL ${lib.escapeShellArg address} | ${pkgs.jq}/bin/jq -f ${pkgs.writeText "script.jq" jqScript}
'';
# reporter scripts
kpaste-irc =
{
target,
retiolumLink ? false,
server ? "irc.r",
messagePrefix ? "change detected: ",
nick ? ''"$PANOPTIKON_WATCHER"-watcher'',
}:
pkgs.writers.writeDash "kpaste-irc-reporter" ''
KPASTE_CONTENT_TYPE=text/plain ${pkgs.kpaste}/bin/kpaste \
| ${pkgs.gnused}/bin/sed -n "${if retiolumLink then "2" else "3"}s/^/${messagePrefix}/p" \
| ${pkgs.nur.repos.mic92.ircsink}/bin/ircsink \
--nick ${nick} \
--server ${server} \
--target ${target}
'';
}

View File

@@ -1,123 +0,0 @@
{
config,
lib,
pkgs,
...
}:
{
options.services.panoptikon = {
enable = lib.mkEnableOption "Generic command output / website watcher";
watchers = lib.mkOption {
type = lib.types.attrsOf (
lib.types.submodule (watcher: {
options = {
script = lib.mkOption {
type = lib.types.path;
description = ''
A script whose stdout is to be watched.
'';
example = ''
pkgs.writers.writeDash "github-meta" '''
''${pkgs.curl}/bin/curl -sSL https://api.github.com/meta | ''${pkgs.jq}/bin/jq
'''
'';
};
frequency = lib.mkOption {
type = lib.types.str;
description = ''
How often to run the script. See systemd.time(7) for more information about the format.
'';
example = "*:0/3";
default = "daily";
};
loadCredential = lib.mkOption {
type = lib.types.listOf lib.types.str;
description = ''
This can be used to pass secrets to the systemd service without adding them to the nix store.
'';
default = [ ];
};
reporters = lib.mkOption {
type = lib.types.listOf lib.types.path;
description = ''
A list of scripts that take the diff (if any) via stdin and report it (e.g. to IRC, Telegram or Prometheus). The name of the watcher will be in the $PANOPTIKON_WATCHER environment variable.
'';
example = ''
[
(pkgs.writers.writeDash "telegram-reporter" '''
''${pkgs.curl}/bin/curl -X POST https://api.telegram.org/bot''${TOKEN}/sendMessage \
-d chat_id=123456 \
-d text="$(cat)"
''')
(pkgs.writers.writeDash "notify" '''
''${pkgs.libnotify}/bin/notify-send "$PANOPTIKON_WATCHER has changed."
''')
]
'';
};
};
config = { };
})
);
};
};
config =
let
cfg = config.services.panoptikon;
in
lib.mkIf cfg.enable {
users.extraUsers.panoptikon = {
isSystemUser = true;
createHome = true;
home = "/var/lib/panoptikon";
group = "panoptikon";
};
users.extraGroups.panoptikon = { };
systemd.timers = lib.attrsets.mapAttrs' (
watcherName: _:
lib.nameValuePair "panoptikon-${watcherName}" {
timerConfig.RandomizedDelaySec = toString (60 * 60);
}
) cfg.watchers;
systemd.services = lib.attrsets.mapAttrs' (
watcherName: watcherOptions:
lib.nameValuePair "panoptikon-${watcherName}" {
enable = true;
startAt = watcherOptions.frequency;
serviceConfig = {
Type = "oneshot";
User = "panoptikon";
Group = "panoptikon";
WorkingDirectory = "/var/lib/panoptikon";
RestartSec = toString (60 * 60);
Restart = "on-failure";
LoadCredential = watcherOptions.loadCredential;
};
unitConfig = {
StartLimitIntervalSec = "300";
StartLimitBurst = "5";
};
environment.PANOPTIKON_WATCHER = watcherName;
wants = [ "network-online.target" ];
script = ''
set -fux
${watcherOptions.script} > ${lib.escapeShellArg watcherName}
diff_output=$(${pkgs.diffutils}/bin/diff --new-file ${
lib.escapeShellArg (watcherName + ".old")
} ${lib.escapeShellArg watcherName} || :)
if [ -n "$diff_output" ]
then
${lib.strings.concatMapStringsSep "\n" (
reporter: ''echo "$diff_output" | ${reporter} || :''
) watcherOptions.reporters}
fi
mv ${lib.escapeShellArg watcherName} ${lib.escapeShellArg (watcherName + ".old")}
'';
}
) cfg.watchers;
};
}

View File

@@ -1,73 +0,0 @@
{
config,
pkgs,
lib,
...
}:
with lib;
let
netname = "retiolum";
cfg = config.networking.retiolum;
in
{
options = {
networking.retiolum.ipv4 = mkOption {
type = types.str;
description = ''
own ipv4 address
'';
};
networking.retiolum.ipv6 = mkOption {
type = types.str;
description = ''
own ipv6 address
'';
};
networking.retiolum.nodename = mkOption {
type = types.str;
default = config.networking.hostName;
description = ''
tinc network name
'';
};
};
config = {
services.tinc.networks.${netname} = {
name = cfg.nodename;
hosts = builtins.mapAttrs (name: _: builtins.readFile "${<retiolum/hosts>}/${name}") (
builtins.readDir <retiolum/hosts>
);
rsaPrivateKeyFile = toString <system-secrets/retiolum.key>;
ed25519PrivateKeyFile = toString <system-secrets/retiolum.ed25519>;
extraConfig = ''
LocalDiscovery = yes
AutoConnect = yes
'';
};
networking.extraHosts = builtins.readFile (toString <retiolum/etc.hosts>);
environment.systemPackages = [ config.services.tinc.networks.${netname}.package ];
networking.firewall = {
allowedTCPPorts = [ 655 ];
allowedUDPPorts = [ 655 ];
};
#services.netdata.portcheck.checks.tinc.port = 655;
systemd.network = {
enable = true;
networks = {
"${netname}".extraConfig = ''
[Match]
Name = tinc.${netname}
[Network]
Address=${cfg.ipv4}/12
Address=${cfg.ipv6}/16
'';
};
};
};
}

37
packages/256color.nix Normal file
View File

@@ -0,0 +1,37 @@
{
writers,
}:
writers.writeDashBin "256color" ''
pl() {
for i in $(seq $1 $(expr $2 - 1)); do
printf '\e[38;5;%sm%03i\e[m ' $i $i
done
printf '\e[38;5;%sm%03i\e[m\n' $2 $2
}
p() {
printf '\e[38;5;%sm%03i\e[m ' $1 $1
}
p6x6() {
for i in $(seq 0 5); do
for j in $(seq 0 5); do
p $(expr $1 + $i + $j \* 6)
done
echo
done
}
pl 0 7
pl 8 15
p6x6 16
p6x6 52
p6x6 88
p6x6 124
p6x6 160
p6x6 196
pl 232 243
pl 244 255
''

View File

@@ -0,0 +1,38 @@
{
runCommand,
fetchurl,
imagemagick,
colors,
}:
let
backgroundColor = colors.withHashtag.base06;
foregroundColor = colors.withHashtag.base01;
width = 1920;
height = 1080;
svgUrl = "https://applicative.systems/_astro/logo-full.D8zRvqBZ.svg";
logoSvg = fetchurl {
url = svgUrl;
hash = "sha256-qXDIEZsAPn4eUJ3kb5U6L3PMUCtWGYqhqyIaBt7FntE=";
};
in
runCommand "applicative-wallpaper.png"
{
nativeBuildInputs = [ imagemagick ];
}
''
# 1. We use -background to set the canvas color
# 2. We use -fuzz and -opaque to replace the logo's internal colors
# 3. We use -gravity and -extent to center it on a wallpaper-sized canvas
convert \
-background none \
-density 300 \
"${logoSvg}" \
-fuzz 100% -fill "${foregroundColor}" -opaque black \
-resize 800x800 \
-gravity center \
-background "${backgroundColor}" \
-extent ${toString width}x${toString height} \
$out
''

66
packages/avesta.nix Normal file
View File

@@ -0,0 +1,66 @@
# Transliterate Latin-script Avestan to Avestan Unicode script
{
lib,
writers,
gnused,
}:
let
sedScript = builtins.toFile "avesta.sed" ''
s/ā̊/𐬃/g
s/t̰/𐬝/g
s/̌/𐬴/g
s/š́/𐬳/g
s/ą̄/𐬅/g
s/ŋ/𐬤/g
s/ə̄/𐬇/g
s/ŋ́/𐬣/g
s/x́/𐬒/g
s/x/𐬓/g
s/a/𐬀/g
s/ā/𐬁/g
s/å/𐬂/g
s/ą/𐬄/g
s/ə/𐬆/g
s/e/𐬈/g
s/ē/𐬉/g
s/o/𐬊/g
s/ō/𐬋/g
s/i/𐬌/g
s/ī/𐬍/g
s/u/𐬎/g
s/ū/𐬏/g
s/k/𐬐/g
s/x/𐬑/g
s/g/𐬔/g
s/ġ/𐬕/g
s/γ/𐬖/g
s/c/𐬗/g
s/j/𐬘/g
s/t/𐬙/g
s/θ/𐬚/g
s/d/𐬛/g
s/δ/𐬜/g
s/p/𐬞/g
s/f/𐬟/g
s/b/𐬠/g
s/β/𐬡/g
s/ŋ/𐬢/g
s/n/𐬥/g
s/ń/𐬦/g
s//𐬧/g
s/m/𐬨/g
s/m̨/𐬩/g
s//𐬫/g
s/y/𐬪/g
s/v/𐬬/g
s/r/𐬭/g
s/s/𐬯/g
s/z/𐬰/g
s/š/𐬱/g
s/ž/𐬲/g
s/h/𐬵/g
'';
in
writers.writeDashBin "avesta" ''
exec ${lib.getExe gnused} -f ${sedScript} "$@"
''

52
packages/bvg.nix Normal file
View File

@@ -0,0 +1,52 @@
# Berlin BVG transit disruption checker
{
lib,
writers,
curl,
jq,
}:
writers.writeDashBin "bvg" ''
${lib.getExe curl} -sSL 'https://www.bvg.de/disruption-reports/q' \
--data-raw '{"variables":{},"query":"{
allDisruptions {
disruptions {
meldungsId
linie
verkehrsmittel
__typename
... on Traffic {
datum
gueltigVonDatum
gueltigVonZeit
gueltigBisDatum
gueltigBisZeit
richtungName
richtungHafasId
beginnAbschnittName
beginnAbschnittHafasId
endeAbschnittName
endeAbschnittHafasId
textIntUrsache
sev
textIntAuswirkung
umfahrung
textWAPSMSUrsache
textWAPSMSAuswirkung
prioritaet
__typename
}
}
__typename
}
}"}' \
| ${lib.getExe jq} --arg interesting "$interesting" '
.data.allDisruptions.disruptions
| map(select(
(.linie as $linie
| $interesting
| split(" ")
| index($linie))
and (.["__typename"] == "Traffic")
))
'
''

17
packages/charinfo.nix Normal file
View File

@@ -0,0 +1,17 @@
# Print Unicode character info for each character on stdin
{
writers,
python3,
}:
writers.writePython3Bin "charinfo" {
flakeIgnore = [ "E501" "E722" ];
} ''
import sys
import unicodedata
for index, character in enumerate(sys.stdin.read().strip()):
try:
print(index, character, hex(ord(character)), unicodedata.category(character), unicodedata.name(character))
except Exception:
print(index, character, hex(ord(character)))
''

31
packages/chunk-pdf.nix Normal file
View File

@@ -0,0 +1,31 @@
# Split a PDF into chunks of N pages
{
lib,
writers,
pdftk,
gnugrep,
coreutils,
}:
writers.writeDashBin "chunk-pdf" ''
set -efu
INPUT_FILE="''${2:?Pass the PDF path as second argument.}"
PAGES_PER_REPORT="''${1:?Pass the chunk size as first argument.}"
if [ ! -f "$INPUT_FILE" ]; then
echo >&2 "File $INPUT_FILE does not exist."
exit 1
fi
TOTAL_PAGES="$(${lib.getExe pdftk} "$INPUT_FILE" dump_data | ${lib.getExe gnugrep} NumberOfPages | ${lib.getExe' coreutils "cut"} -f2 -d' ')"
RUNS=$((TOTAL_PAGES/PAGES_PER_REPORT))
for run in $(${lib.getExe' coreutils "seq"} 0 "$((RUNS-1))"); do
start_page=$((run*PAGES_PER_REPORT+1))
end_page=$(((run+1)*PAGES_PER_REPORT))
output_file="chunk_$((run+1)).pdf"
echo "splitting $INPUT_FILE from $start_page to $end_page into $output_file"
${lib.getExe pdftk} "$INPUT_FILE" cat "$start_page-$end_page" output "$output_file"
done
''

21
packages/csv2json.nix Normal file
View File

@@ -0,0 +1,21 @@
# Convert CSV to JSON
{
writers,
python3,
}:
writers.writePython3Bin "csv2json" {
flakeIgnore = [ "E501" ];
} ''
import csv
import json
import sys
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("--delimiter", "-d", default=",", help="CSV field separator")
args = parser.parse_args()
if __name__ == "__main__":
json.dump(list(csv.DictReader(sys.stdin, delimiter=args.delimiter)), sys.stdout)
''

35
packages/fix-sd.nix Normal file
View File

@@ -0,0 +1,35 @@
# Recover files from a corrupted exFAT SD card
{
lib,
writers,
exfatprogs,
util-linux,
coreutils,
findutils,
gnused,
}:
writers.writeDashBin "fix-sd" ''
set -efu
drive="''${1:?Usage: fix-sd /dev/sdX [output-dir]}"
output_dir="''${2:-$(${lib.getExe' coreutils "mktemp"} -d "''${TMPDIR:-/tmp}/fix-sd-XXXXXX")}"
mountpoint="$(${lib.getExe' coreutils "mktemp"} -d "''${TMPDIR:-/tmp}/fix-sd-mount-XXXXXX")"
trap clean EXIT
clean() {
${lib.getExe' util-linux "umount"} "$mountpoint" 2>/dev/null || true
${lib.getExe' coreutils "rmdir"} "$mountpoint" 2>/dev/null || true
}
filenames="$(${lib.getExe' exfatprogs "fsck.exfat"} "$drive" 2>&1 | ${lib.getExe gnused} -nE "s/.* file '(.*?)' is not allocated.*/\1/p")"
${lib.getExe' coreutils "mkdir"} -p "$mountpoint" "$output_dir"
${lib.getExe' util-linux "mount"} "$drive" "$mountpoint"
echo "$filenames" | while read -r filename; do
[ -n "$filename" ] || continue
${lib.getExe' findutils "find"} "$mountpoint" -type f -name "$filename" -exec ${lib.getExe' coreutils "cp"} {} "$output_dir" \;
done
echo "Recovered files saved to $output_dir"
${lib.getExe' exfatprogs "fsck.exfat"} "$drive"
''

View File

@@ -1,15 +0,0 @@
{
curl,
writers,
jq,
apiKeyCommand ? "pass api-keys/openai.com",
model ? "gpt-3.5-turbo",
}:
writers.writeDashBin "gpt" ''
json=$(jq --slurp --raw-input '{model:"${model}", messages: [{role: "user", content: .}]}')
${curl}/bin/curl -sSL https://api.openai.com/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $(${apiKeyCommand})" \
-d "$json" \
| ${jq}/bin/jq -r '.choices[] | .message.content'
''

View File

@@ -1,81 +0,0 @@
{
symlinkJoin,
hledger,
writers,
lib,
git,
coreutils,
gnugrep,
timeLedger,
...
}:
let
date = "${coreutils}/bin/date +'%Y-%m-%d %H:%M:%S'";
hora = writers.writeDashBin "hora" "${hledger}/bin/hledger -f ${lib.escapeShellArg timeLedger} \"$@\"";
hora-edit = writers.writeDashBin "hora-edit" "$EDITOR ${lib.escapeShellArg timeLedger}";
hora-status = writers.writeDashBin "hora-status" "${coreutils}/bin/tac ${lib.escapeShellArg timeLedger} | ${gnugrep}/bin/grep -m 1 .";
hora-start = writers.writeDashBin "hora-start" ''
last_nonempty_line=$(${hora-status}/bin/hora-status)
(echo $last_nonempty_line | ${gnugrep}/bin/grep -q "^o") || {
echo "Last activity must be closed: $last_nonempty_line" >/dev/stderr
exit 1
}
account=$1
(${hora}/bin/hora accounts | ${gnugrep}/bin/grep -q "^$account\$") || {
echo "The account '$account' is not known. Please add manually."
exit 1
}
message=$2
date=$(${date})
echo "i $date $account $message\n" >> "${timeLedger}"
echo "Started $account at $date" >/dev/stderr
'';
hora-stop = writers.writeDashBin "hora-stop" ''
last_nonempty_line=$(${hora-status}/bin/hora-status)
(echo $last_nonempty_line | ${gnugrep}/bin/grep "^i") || {
echo "Last activity cannot be closed: $last_nonempty_line" >/dev/stderr
exit 1
}
last_activity=$(echo "$last_nonempty_line" | ${coreutils}/bin/cut -d' ' -f 4)
date=$(${date})
echo "o $date\n" >> ${timeLedger}
echo "Stopped $last_activity at $date" >/dev/stderr
'';
hora-year = writers.writeDashBin "hora-year" ''
${hora}/bin/hora balance --tree --monthly --begin $(${coreutils}/bin/date +%Y) --depth 1
'';
hora-git = writers.writeDashBin "hora-git" ''
directory=$(${coreutils}/bin/dirname ${lib.escapeShellArg timeLedger})
if [ $# -gt 0 ]
then
${git}/bin/git -C "$directory" --all --message=$(${date})
else
${git}/bin/git -C "$directory" "$@"
fi
'';
hora-weekly = writers.writeDashBin "hora-weekly" ''
${hora}/bin/hora register -p weekly --depth 1 --empty
'';
in
symlinkJoin {
name = "hora";
paths = [
hora
hora-edit
hora-start
hora-status
hora-stop
hora-year
hora-git
hora-weekly
];
}

32
packages/json2csv.nix Normal file
View File

@@ -0,0 +1,32 @@
# Convert JSON array of objects to CSV
{
writers,
python3,
}:
writers.writePython3Bin "json2csv" {
flakeIgnore = [ "E501" ];
} ''
import csv
import json
import sys
if __name__ == "__main__":
json_list = json.load(sys.stdin)
if not isinstance(json_list, list):
print("JSON object is not a list.", file=sys.stderr)
sys.exit(1)
if len(json_list) == 0:
print("JSON list is empty.", file=sys.stderr)
sys.exit(1)
keys = set()
for element in json_list:
if isinstance(element, dict):
keys |= element.keys()
else:
print("Non-dict element:", element, file=sys.stderr)
sys.exit(1)
writer = csv.DictWriter(sys.stdout, fieldnames=list(keys))
writer.writeheader()
for element in json_list:
writer.writerow(element)
''

View File

@@ -1,30 +0,0 @@
{
writers,
lib,
xlockmore,
}:
let
xlockModes = lib.concatStringsSep "\\n" [
# "braid"
"galaxy"
# "lightning"
# "matrix"
"pyro2"
"space"
];
in
writers.writeDashBin "k-lock" ''
MODE=$(printf "${xlockModes}" | shuf -n 1)
${xlockmore}/bin/xlock \
-saturation 0.4 \
-erasemode no_fade \
+description \
-showdate \
-username " " \
-password " " \
-info " " \
-validate "..." \
-invalid "Computer says no." \
-mode "$MODE"
''

View File

@@ -0,0 +1,70 @@
# Convert and transfer audio files to an MP3 player
{
lib,
writers,
ffmpeg,
coreutils,
gnugrep,
gnused,
}:
writers.writeBashBin "mp3player-write" ''
set -e
SPEED=1.0
while getopts ":s:" opt; do
case $opt in
s) SPEED=$OPTARG ;;
\?) echo "Invalid option: -$OPTARG" >&2; exit 1 ;;
:) echo "Option -$OPTARG requires a value." >&2; exit 1 ;;
esac
done
shift $((OPTIND -1))
if [ "$#" -lt 2 ]; then
echo "Usage: mp3player-write [-s speed] MOUNT_POINT FILE1 [FILE2 ...]"
exit 1
fi
MOUNT_POINT=$1
shift
FILES=("$@")
if [ ! -d "$MOUNT_POINT" ]; then
echo "Error: Mount point '$MOUNT_POINT' does not exist."
exit 1
fi
sanitize_filename() {
local name
name=$(${lib.getExe' coreutils "basename"} "$1")
name=''${name%.*}
name=$(echo "$name" | ${lib.getExe' coreutils "tr"} ' ' '_' | ${lib.getExe' coreutils "tr"} -cd '[:alnum:]_-')
echo "''${name:0:50}"
}
for f in "''${FILES[@]}"; do
[ -f "$f" ] || continue
existing_prefixes=$(${lib.getExe' coreutils "ls"} "$MOUNT_POINT" | ${lib.getExe gnugrep} -E '^[0-9].*\.mp3$' | ${lib.getExe gnused} -E 's/^([0-9]).*/\1/' | ${lib.getExe' coreutils "sort"} -n | ${lib.getExe' coreutils "uniq"})
for i in {0..9}; do
if ! echo "$existing_prefixes" | ${lib.getExe gnugrep} -q "^$i$"; then
PREFIX=$i
break
fi
done
BASENAME=$(sanitize_filename "$f")
OUT_PATTERN="$MOUNT_POINT/''${PREFIX}_%03d_''${BASENAME}.mp3"
echo "Converting '$f' to '$OUT_PATTERN' at speed $SPEED..."
${lib.getExe ffmpeg} -nostdin -i "$f" \
-filter:a "atempo=$SPEED" \
-ar 22050 -ac 1 -c:a libmp3lame -b:a 32k \
-f segment -segment_time 300 \
"$OUT_PATTERN"
done
echo "All files processed successfully."
''

14
packages/mushakkil.nix Normal file
View File

@@ -0,0 +1,14 @@
# Add Arabic diacritics (tashkeel) to text via alsharekh.org
{
lib,
writers,
curl,
jq,
}:
writers.writeDashBin "mushakkil" ''
${lib.getExe curl} -sSL 'https://diac.alsharekh.org/Diac/DiacText' \
-H "Content-Type: application/json" \
--data-raw "$(${lib.getExe jq} --raw-input '{word: ., type: 1}')" \
--compressed \
| ${lib.getExe jq} -r .diacWord
''

View File

@@ -0,0 +1,84 @@
# Generate a Haddock index page for all packages visible to the current GHC
{
lib,
writers,
coreutils,
gnugrep,
gnused,
graphviz,
}:
writers.writeBashBin "nix-haddock-index" ''
set -efux
if test -z "''${NIX_GHC-}"; then
NIX_GHC=$(${lib.getExe' coreutils "readlink"} -f "$(type -P ghc)")
fi
if ! echo $NIX_GHC | ${lib.getExe gnugrep} -q '^/nix/store/'; then
printf '%s: error: unsupported GHC executable path (not in Nix store): %q\n' \
"$0" \
"$NIX_GHC" \
>&2
exit 1
fi
NIX_GHC_PREFIX=$(${lib.getExe' coreutils "dirname"} "$(${lib.getExe' coreutils "dirname"} "$NIX_GHC")")
NIX_GHC_DOCDIR=$NIX_GHC_PREFIX/share/doc/ghc/html
main() {
hash=$(echo $NIX_GHC_PREFIX | ${lib.getExe gnused} -n 's|^/nix/store/\([a-z0-9]\+\).*|\1|p')
title="Haddock index for $NIX_GHC_PREFIX"
header=$(
printf 'Haddock index for <a href="%s">%s</a>\n' \
$NIX_GHC_PREFIX \
$NIX_GHC_PREFIX \
)
suffix=''${hash:+-$hash}
index_file=/tmp/haddock$suffix-index.html
svg_file=/tmp/haddock$suffix.svg
eval "$(
echo 'gen_index() {'
echo ' html_head'
"$NIX_GHC_PREFIX"/bin/ghc-pkg dump | ${lib.getExe gnused} -n '
s/^---$/ reset/p
s/^\(name\|version\):\s*\([-A-Za-z0-9_.]\+\)$/ \1=\2/p
s/^haddock-html:\s*\([-A-Za-z0-9_./]\+\)$/ haddock_html \1/p
'
echo ' html_foot'
echo '}'
)"
gen_index > $index_file
"$NIX_GHC_PREFIX"/bin/ghc-pkg dot | ${lib.getExe' graphviz "tred"} | ${lib.getExe' graphviz "dot"} -Tsvg | ${lib.getExe gnused} '
s/<svg width="[0-9]\+pt" height="[0-9]\+pt"/<svg width="3600px" height="100%"/
' > $svg_file
echo $index_file
}
reset() {
unset name version
}
haddock_html() {
printf '<li>'
printf '<a href="%s/index.html">%s</a>' "$1" "$name-$version"
printf '</li>\n'
}
html_head() {
printf '<!doctype html>\n'
printf '<title>%s</title>\n' "$title"
printf '<link href="%s" rel="stylesheet" type="text/css">\n' \
"$NIX_GHC_DOCDIR/libraries/ocean.css"
printf '<h1>%s</h1>\n' "$header"
printf '<ul>\n'
}
html_foot() {
printf '</ul>\n'
printf '<a href="%s">graph</a>\n' "$svg_file"
}
main "$@"
''

30
packages/pdf-ocr.nix Normal file
View File

@@ -0,0 +1,30 @@
# OCR a PDF file to text using tesseract
{
lib,
writers,
poppler-utils,
tesseract,
coreutils,
}:
writers.writeDashBin "pdf-ocr" ''
set -efu
pdf_path="$(${lib.getExe' coreutils "realpath"} "$1")"
[ -f "$pdf_path" ] || {
echo "Usage: pdf-ocr FILE.pdf" >&2
exit 1
}
tmpdir="$(${lib.getExe' coreutils "mktemp"} -d)"
trap 'rm -rf $tmpdir' EXIT
cd "$tmpdir"
${lib.getExe' poppler-utils "pdftoppm"} -png "$pdf_path" pdf-ocr
for png in pdf-ocr*.png; do
${lib.getExe tesseract} "$png" "$png.txt" 2>/dev/null
done
cat pdf-ocr-*.txt
''

69
packages/pi.nix Normal file
View File

@@ -0,0 +1,69 @@
{
runCommand,
nodejs,
writeShellApplication,
lib,
jq,
cacert,
pi-llm,
}:
let
# Pre-install pi plugins into a fake npm global prefix
pluginPrefixRaw =
runCommand "pi-plugins-raw"
{
nativeBuildInputs = [
nodejs
cacert
];
outputHashMode = "recursive";
outputHashAlgo = "sha256";
outputHash = "sha256-+XaHU/Ale2YLQmdvfG73nG+tjRkWyb27bdLgI3JFLlU=";
impureEnvVars = [
"http_proxy"
"https_proxy"
];
SSL_CERT_FILE = "${cacert}/etc/ssl/certs/ca-bundle.crt";
}
''
export HOME=$TMPDIR
export npm_config_prefix=$out
npm install -g pi-hooks shitty-extensions
'';
# Remove the resistance extension (annoying terminator quote widget)
pluginPrefix = runCommand "pi-plugins" { } ''
cp -a ${pluginPrefixRaw} $out
chmod -R u+w $out
pkg=$out/lib/node_modules/shitty-extensions/package.json
${lib.getExe jq} '.pi.extensions |= map(select(contains("resistance") | not))' "$pkg" > "$pkg.tmp"
mv "$pkg.tmp" "$pkg"
'';
in
writeShellApplication {
name = "pi";
runtimeInputs = [ nodejs ];
text = ''
set -efu
export npm_config_prefix="${pluginPrefix}"
# Ensure settings.json has our plugins listed
SETTINGS_DIR="''${PI_CODING_AGENT_DIR:-$HOME/.pi/agent}"
SETTINGS_FILE="$SETTINGS_DIR/settings.json"
mkdir -p "$SETTINGS_DIR"
# Add packages to settings if not already present
if [ ! -f "$SETTINGS_FILE" ]; then
echo '{"packages":["npm:pi-hooks","npm:shitty-extensions"]}' > "$SETTINGS_FILE"
else
for pkg in "npm:pi-hooks" "npm:shitty-extensions"; do
if ! grep -q "$pkg" "$SETTINGS_FILE"; then
${lib.getExe jq} --arg p "$pkg" '.packages = ((.packages // []) + [$p] | unique)' "$SETTINGS_FILE" > "$SETTINGS_FILE.tmp"
mv "$SETTINGS_FILE.tmp" "$SETTINGS_FILE"
fi
done
fi
exec ${lib.getExe pi-llm} "$@"
'';
}

34
packages/picoclaw.nix Normal file
View File

@@ -0,0 +1,34 @@
{ lib, buildGoModule, fetchFromGitHub }:
buildGoModule (finalAttrs: {
pname = "picoclaw";
version = "0.1.1";
src = fetchFromGitHub {
owner = "sipeed";
repo = "picoclaw";
rev = "v${finalAttrs.version}";
hash = "sha256-nx/D8ir4/l0pTnMNORby2FNtU+ouKT0DUjP2vpJLmPk=";
};
postPatch = ''
substituteInPlace go.mod --replace "go 1.25.7" "go 1.25.5"
'';
proxyVendor = true;
# Set to lib.fakeHash or empty initially, then update with the actual hash Nix reports.
vendorHash = "sha256-XKwYmbMyf4yg/E4Yv0uMS9v0oAuMZJwvoaAPCL/1AAY=";
subPackages = [ "cmd/picoclaw" ];
ldflags = [
"-s" "-w"
"-X main.version=${finalAttrs.version}"
];
meta = with lib; {
description = "Ultra-efficient AI Assistant in Go for $10 hardware";
homepage = "https://github.com/sipeed/picoclaw";
license = licenses.mit; # Verify license in the repo
maintainers = [];
};
})

77
packages/prospekte.nix Normal file
View File

@@ -0,0 +1,77 @@
# Browse and view German supermarket flyers (Lidl, Aldi, REWE, Kaufland, Netto)
{
writers,
curl,
jq,
fzf,
zathura,
coreutils,
htmlq,
gnugrep,
gnused,
lib,
}:
writers.writeDashBin "prospekte" ''
export PATH=${lib.makeBinPath [ curl jq fzf zathura coreutils htmlq gnugrep gnused ]}:$PATH
lidl() {
echo LIDL
curl -sSL 'https://endpoints.lidl-flyer.com/v3/region-overview/lidl/de-DE/0.json' \
| jq -r '
.categories
| map(select(.name == "Filial-Angebote") | .subcategories | map(.flyers))
| flatten
| flatten
| .[]
| .pdfUrl
'
}
aldi_nord() {
echo ALDI nord
echo 'https://magazine.aldi-nord.de/aldi-nord/aldi-aktuell/GetPDF.ashx'
echo 'https://magazine.aldi-nord.de/aldi-nord/aldi-vorschau/GetPDF.ashx'
}
rewe_berlin() {
store_id=662366923
publisher_id=1062
echo REWE
curl -sSL "https://www.bonialserviceswidget.de/de/stores/$store_id/brochures?storeId=$store_id&publisherId=$publisher_id" | while read -r brochure_id; do
curl -sSL "https://www.bonialserviceswidget.de/de/v5/brochureDetails/$brochure_id?publisherId=$publisher_id" | jq -r .pdfUrl
done
}
kaufland() {
region_code=8920
echo KAUFLAND
curl -sSL https://filiale.kaufland.de/prospekte.html | htmlq --attribute href '.flyer a' | grep -Eo 'DE_de_KDZ[^/]*' | sed "s/_3000_/_''${region_code}_/" | while read -r flyer_id; do
curl -sSL "https://endpoints.leaflets.kaufland.com/v3/$flyer_id/flyer.json?regionCode=$region_code" | jq -r .flyer.pdfUrl
done
}
netto_schwarz() {
echo 'NETTO (schwarz)'
curl -sSL 'https://squid-api.tjek.com/v2/catalogs?dealer_ids=90f2VL&order_by=created' \
| jq -r '.[] | .id' \
| while read -r flyer_id; do
curl -sSL "https://squid-api.tjek.com/v2/catalogs/$flyer_id/download" \
| jq -r .pdf_url
done
}
dir="$(mktemp -d)"
trap 'rm -rf "$dir"' EXIT
prospekt_url="$( (
lidl
aldi_nord
rewe_berlin
kaufland
netto_schwarz
) | fzf)"
curl -sSL "$prospekt_url" -o "$dir/prospekt.pdf"
zathura "$dir/prospekt.pdf"
''

View File

@@ -6,6 +6,9 @@
jq,
yq,
}:
let
model = "gemini-2.5-flash-lite";
in
writers.writeBashBin "radio-news" ''
set -efu
PATH=$PATH:${
@@ -57,5 +60,5 @@ writers.writeBashBin "radio-news" ''
EOF
)
echo "$REQUEST" | curl "https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash-8b:generateContent?key=$GEMINI_API_KEY" -s -H "Content-Type: application/json" -d @-
echo "$REQUEST" | curl "https://generativelanguage.googleapis.com/v1beta/models/${model}:generateContent?key=$GEMINI_API_KEY" -s -H "Content-Type: application/json" -d @-
''

13
packages/readme.nix Normal file
View File

@@ -0,0 +1,13 @@
# Render a GitHub repo's README.md as a man page
{
lib,
writers,
curl,
pandoc,
man,
}:
writers.writeDashBin "readme" ''
${lib.getExe curl} -sSL "https://raw.githubusercontent.com/$*/master/README.md" \
| ${lib.getExe pandoc} -f gfm -t man -s \
| ${lib.getExe man} -l -
''

View File

@@ -1,27 +0,0 @@
{
lib,
stdenv,
bash,
fetchFromGitHub,
}:
stdenv.mkDerivation {
name = "vimv";
src = fetchFromGitHub {
owner = "thameera";
repo = "vimv";
rev = "4152496c1946f68a13c648fb7e583ef23dac4eb8";
sha256 = "1fsrfx2gs6bqx7wk7pgcji2i2x4alqpsi66aif4kqvnpqfhcfzjd";
};
phases = [ "installPhase" ];
installPhase = ''
mkdir -p $out/bin
sed 's:#!/bin/bash:#!${bash}/bin/bash:' $src/vimv > $out/bin/vimv
chmod 755 $out/bin/vimv
'';
meta = with lib; {
homepage = "https://github.com/thameera/vimv";
description = "Batch-rename files using Vim";
license = licenses.mit;
platforms = platforms.all;
};
}

View File

@@ -0,0 +1,9 @@
{ nix, writeShellScriptBin }:
writeShellScriptBin "yt-dlp-master" ''
args=$@
${nix}/bin/nix-shell -p '(yt-dlp.overrideAttrs (_: {
src = builtins.fetchTree "github:yt-dlp/yt-dlp";
patches = [];
postPatch = "python devscripts/update-version.py 0.99";
}))' -p deno --run "yt-dlp $args"
''

Submodule secrets updated: 83d9103f20...0c10b2e8bb

View File

@@ -1,4 +1,5 @@
secrets/alertmanager-token-reporters.age
secrets/anthropic-api-key.age
secrets/brevo-key.age
secrets/cifs-credentials-zodiac.age
secrets/copecart-ipn.age
@@ -54,6 +55,7 @@ secrets/matrix-token-lakai.age
secrets/matrix-token-lakai-env.age
secrets/maxmind-license-key.age
secrets/mega-password.age
secrets/meteora-auth.age
secrets/miniflux-api-token.age
secrets/miniflux-credentials.age
secrets/nextcloud-password-admin.age
@@ -61,6 +63,12 @@ secrets/nextcloud-password-database.age
secrets/nextcloud-password-fysi.age
secrets/nextcloud-password-kieran.age
secrets/onlyoffice-jwt-key.age
secrets/openclaw-gateway-token.age
secrets/openclaw-telegram-token.age
secrets/opencrow-gemini-key.age
secrets/opencrow-matrix-token.age
secrets/opencrow-openrouter-key.age
secrets/opencrow-soul.age
secrets/openweathermap-api-key.age
secrets/restic.age
secrets/secrets.nix

View File

@@ -16,6 +16,8 @@
./gemini.nix
./wallabag.nix
./nethack.nix
./opencrow.nix
./meteora.nix
];
niveum.passport = {

18
systems/ful/meteora.nix Normal file
View File

@@ -0,0 +1,18 @@
{ config, pkgs, ... }: {
age.secrets.meteora-auth = {
file = ../../secrets/meteora-auth.age;
owner = "nginx";
};
services.nginx = {
enable = true;
virtualHosts."meteora.xn--kiern-0qa.de" = {
forceSSL = true;
enableACME = true;
root = "${pkgs.meteora-website}";
locations."/" = {
basicAuthFile = config.age.secrets.meteora-auth.path;
};
};
};
}

83
systems/ful/opencrow.nix Normal file
View File

@@ -0,0 +1,83 @@
{
config,
pkgs,
...
}:
{
age.secrets = {
opencrow-matrix-token = {
file = ../../secrets/opencrow-matrix-token.age;
};
opencrow-soul = {
file = ../../secrets/opencrow-soul.age;
};
opencrow-gemini-key = {
file = ../../secrets/opencrow-gemini-key.age;
};
opencrow-openrouter-key = {
file = ../../secrets/opencrow-openrouter-key.age;
};
};
environment.systemPackages = [
pkgs.pi
];
services.opencrow = {
enable = true;
package = pkgs.opencrow;
extraPackages = [
pkgs.pi
pkgs.nix
];
environmentFiles = [
config.age.secrets.opencrow-matrix-token.path
config.age.secrets.opencrow-openrouter-key.path
config.age.secrets.opencrow-gemini-key.path
];
extraBindMounts."/run/opencrow/SOUL.md" = {
hostPath = config.age.secrets.opencrow-soul.path;
isReadOnly = true;
};
environment = {
NIX_REMOTE = "daemon";
PI_PERMISSION_LEVEL = "high";
OPENCROW_MATRIX_HOMESERVER = "https://matrix.org";
OPENCROW_MATRIX_USER_ID = "@fable_ai:matrix.org";
OPENCROW_SOUL_FILE = "/run/opencrow/SOUL.md";
OPENCROW_HEARTBEAT_INTERVAL = "2h";
# end of the month
OPENCROW_PI_PROVIDER = "openrouter";
OPENCROW_PI_MODEL = "stepfun/step-3.5-flash:free";
# OPENCROW_PI_PROVIDER = "google";
# OPENCROW_PI_MODEL = "gemini-2.0-flash";
# beginning of the month
# OPENCROW_PI_PROVIDER = "github-copilot";
# OPENCROW_PI_MODEL = "claude-opus-4.6";
};
};
containers.opencrow.config = {
nix.settings.experimental-features = [
"flakes"
"nix-command"
];
};
nix.settings.experimental-features = [
"flakes"
"nix-command"
];
services.restic.backups.niveum.paths = [
"/var/lib/opencrow"
];
}

View File

@@ -5,42 +5,23 @@
...
}:
let
irc-xxx = pkgs.lib.panoptikon.kpaste-irc {
irc-xxx = pkgs.panoptikonReporters.kpaste-irc {
target = lib.escapeShellArg "#xxx";
retiolumLink = true;
};
matrix =
{
server ? "matrix.4d2.org",
target,
}:
pkgs.writers.writeDash "matrix-reporter" ''
export RAW_MESSAGE="$(cat)"
export MESSAGE=$(printf '<b>%s</b><br><pre>%s</pre>' "$PANOPTIKON_WATCHER" "$RAW_MESSAGE")
export MATRIX_TOKEN="$(cat ${config.age.secrets.matrix-token-lakai.path})"
export JSON_PAYLOAD=$(${pkgs.jq}/bin/jq -n --arg msgtype "m.text" --arg body "$RAW_MESSAGE" --arg formattedBody "$MESSAGE" '{msgtype: $msgtype, body: $body, format: "org.matrix.custom.html", formatted_body: $formattedBody}')
${pkgs.curl}/bin/curl -X POST "https://${server}/_matrix/client/r0/rooms/${target}/send/m.room.message" \
-d "$JSON_PAYLOAD" \
-H "Authorization: Bearer $MATRIX_TOKEN" \
-H "Content-Type: application/json"
'';
matrix-kmein = pkgs.panoptikonReporters.matrix {
homeserver = "matrix.4d2.org";
roomId = lib.escapeShellArg "!zlwCuPiCNMSxDviFzA:4d2.org";
tokenPath = config.age.secrets.matrix-token-lakai.path;
};
matrix-kmein = matrix { target = "!zlwCuPiCNMSxDviFzA:4d2.org"; };
telegram-kmein = pkgs.panoptikonReporters.telegram {
tokenPath = config.age.secrets.telegram-token-kmein.path;
chatId = "-1001796440545";
};
telegram-kmein =
let
chatId = "-1001796440545";
in
pkgs.writers.writeDash "telegram-fulltext" ''
export TOKEN="$(cat "$CREDENTIALS_DIRECTORY/token")"
${pkgs.curl}/bin/curl -X POST "https://api.telegram.org/bot''${TOKEN}/sendMessage" \
-d chat_id=${chatId} \
-d text="$(cat)" \
| ${pkgs.jq}/bin/jq -e .ok
'';
irc-kmein = pkgs.lib.panoptikon.kpaste-irc {
irc-kmein = pkgs.panoptikonReporters.kpaste-irc {
messagePrefix = "$PANOPTIKON_WATCHER: ";
target = "kmein";
nick = "panoptikon-kmein";
@@ -48,7 +29,12 @@ let
};
in
{
age.secrets.telegram-token-kmein.file = ../../secrets/telegram-token-kmein.age;
age.secrets.telegram-token-kmein = {
file = ../../secrets/telegram-token-kmein.age;
owner = "panoptikon";
group = "panoptikon";
mode = "400";
};
age.secrets.matrix-token-lakai = {
file = ../../secrets/matrix-token-lakai.age;
owner = "panoptikon";
@@ -60,7 +46,7 @@ in
enable = true;
watchers = {
"github-meta" = {
script = pkgs.lib.panoptikon.urlJSON {
script = pkgs.panoptikonWatchers.json {
jqScript = ''
{
ssh_key_fingerprints: .ssh_key_fingerprints,
@@ -70,83 +56,71 @@ in
} "https://api.github.com/meta";
reporters = [ irc-xxx ];
};
lammla = {
script = pkgs.lib.panoptikon.url "http://lammla.info/index.php?reihe=30";
reporters = [ matrix-kmein ];
};
kratylos = {
script = pkgs.lib.panoptikon.url "https://kratylos.reichert-online.org/current_issue/KRATYLOS";
reporters = [ matrix-kmein ];
};
kobudo-tesshinkan = {
script = pkgs.lib.panoptikon.url "https://kobudo-tesshinkan.eu/index.php/de/termine-berichte/lehrgaenge/";
script = pkgs.panoptikonWatchers.html "https://kobudo-tesshinkan.eu/index.php/de/termine-berichte/lehrgaenge/";
reporters = [
telegram-kmein
matrix-kmein
];
};
zeno-free = {
script = pkgs.lib.panoptikon.urlSelector ".zenoCOMain" "http://www.zeno.org/Lesesaal/M/E-Books";
reporters = [ matrix-kmein ];
};
carolinawelslau = {
script = pkgs.lib.panoptikon.urlSelector "#main" "https://carolinawelslau.de/";
script = pkgs.panoptikonWatchers.htmlSelector "#main" "https://carolinawelslau.de/";
reporters = [ matrix-kmein ];
};
humboldt-preis = {
script = pkgs.lib.panoptikon.urlSelector "#content-core" "https://www.hu-berlin.de/de/ueberblick/menschen/ehrungen/humboldtpreis";
script = pkgs.panoptikonWatchers.htmlSelector "#content-core" "https://www.hu-berlin.de/de/ueberblick/menschen/ehrungen/humboldtpreis";
reporters = [ matrix-kmein ];
};
lisalittmann = {
script = pkgs.lib.panoptikon.urlSelector "#site-content" "https://lisalittmann.de/";
script = pkgs.panoptikonWatchers.htmlSelector "#site-content" "https://lisalittmann.de/";
reporters = [ matrix-kmein ];
};
lisalittmann-archive = {
script = pkgs.lib.panoptikon.urlSelector "#site-content" "https://lisalittmann.de/archive/";
script = pkgs.panoptikonWatchers.htmlSelector "#site-content" "https://lisalittmann.de/archive/";
reporters = [ matrix-kmein ];
};
lisalittmann-projects = {
script = pkgs.lib.panoptikon.urlSelector "#site-content" "https://lisalittmann.de/projects/";
script = pkgs.panoptikonWatchers.htmlSelector "#site-content" "https://lisalittmann.de/projects/";
reporters = [ matrix-kmein ];
};
tatort = {
script = pkgs.lib.panoptikon.urlSelector ".linklist" "https://www.daserste.de/unterhaltung/krimi/tatort/sendung/index.html";
script = pkgs.panoptikonWatchers.htmlSelector ".linklist" "https://www.daserste.de/unterhaltung/krimi/tatort/sendung/index.html";
reporters = [ matrix-kmein ];
};
warpgrid-idiomarium = {
script = pkgs.lib.panoptikon.urlSelector "#site-content" "https://warpgrid.de/idiomarium/";
script = pkgs.panoptikonWatchers.htmlSelector "#site-content" "https://warpgrid.de/idiomarium/";
reporters = [ matrix-kmein ];
};
warpgrid-futurism = {
script = pkgs.lib.panoptikon.urlSelector "#site-content" "https://warpgrid.de/futurism/";
script = pkgs.panoptikonWatchers.htmlSelector "#site-content" "https://warpgrid.de/futurism/";
reporters = [ matrix-kmein ];
};
warpgrid-imagiary = {
script = pkgs.lib.panoptikon.urlSelector "#site-content" "https://warpgrid.de/imagiary/";
script = pkgs.panoptikonWatchers.htmlSelector "#site-content" "https://warpgrid.de/imagiary/";
reporters = [ matrix-kmein ];
};
warpgrid-alchemy = {
script = pkgs.lib.panoptikon.urlSelector "#site-content" "https://warpgrid.de/alchemy/";
script = pkgs.panoptikonWatchers.htmlSelector "#site-content" "https://warpgrid.de/alchemy/";
reporters = [ matrix-kmein ];
};
indogermanische-forschungen = {
script = pkgs.lib.panoptikon.urlSelector "#latestIssue" "https://www.degruyter.com/journal/key/INDO/html";
script = pkgs.panoptikonWatchers.htmlSelector "#latestIssue" "https://www.degruyter.com/journal/key/INDO/html";
reporters = [ matrix-kmein ];
};
ig-neuigkeiten = {
script = pkgs.lib.panoptikon.urlSelector "[itemprop=articleBody]" "https://www.indogermanistik.org/aktuelles/neuigkeiten.html";
script = pkgs.panoptikonWatchers.htmlSelector "[itemprop=articleBody]" "https://www.indogermanistik.org/aktuelles/neuigkeiten.html";
reporters = [ matrix-kmein ];
};
ig-tagungen = {
script = pkgs.lib.panoptikon.urlSelector "[itemprop=articleBody]" "https://www.indogermanistik.org/tagungen/tagungen-der-ig.html";
script = pkgs.panoptikonWatchers.htmlSelector "[itemprop=articleBody]" "https://www.indogermanistik.org/tagungen/tagungen-der-ig.html";
reporters = [ matrix-kmein ];
};
fu-distant = {
script = pkgs.lib.panoptikon.urlSelector "#current_events" "https://www.geschkult.fu-berlin.de/en/e/ma-distant/Termine/index.html";
script = pkgs.panoptikonWatchers.htmlSelector "#current_events" "https://www.geschkult.fu-berlin.de/en/e/ma-distant/Termine/index.html";
reporters = [ matrix-kmein ];
};
fu-aegyptologie = {
script = pkgs.lib.panoptikon.urlSelector "#current_events" "https://www.geschkult.fu-berlin.de/e/aegyptologie/termine/index.html";
script = pkgs.panoptikonWatchers.htmlSelector "#current_events" "https://www.geschkult.fu-berlin.de/e/aegyptologie/termine/index.html";
reporters = [ matrix-kmein ];
};
};

View File

@@ -8,17 +8,14 @@
imports = [
./hardware-configuration.nix
../../configs/spacetime.nix
../../configs/admin-essentials.nix
../../configs/keyboard
../../configs/sound.nix
../../configs/printing.nix
../../configs/nix.nix
../../configs/fonts.nix
../../configs/mycelium.nix
../../configs/retiolum.nix
../../configs/sshd.nix
../../configs/sudo.nix
../../configs/zsh.nix
];
age.secrets = {

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