Compare commits

..

60 Commits

Author SHA1 Message Date
203bdfe89b added env vars 2026-01-03 19:37:37 -05:00
edcacfcecc adding open_webui to jeeves 2026-01-03 19:23:50 -05:00
156d624d81 remove great_cloud_of_witnesses 2026-01-03 10:08:53 -05:00
9a7cf03a00 removed luks encryption Storage and Media SSDs 2026-01-02 19:39:25 -05:00
6299d42f75 moving all data sets to zfs encryption 2026-01-02 13:41:45 -05:00
e6472b2cf5 adding claude-code 2026-01-01 23:30:25 -05:00
41d3a8fe1a removed filebrowser 2025-12-27 09:52:19 -05:00
github-actions[bot]
e6ac8f8021 flake.lock: Update
Flake lock file updates:

• Updated input 'firefox-addons':
    'gitlab:rycee/nur-expressions/981ca9c?dir=pkgs/firefox-addons' (2025-12-25)
  → 'gitlab:rycee/nur-expressions/03d7d31?dir=pkgs/firefox-addons' (2025-12-26)
• Updated input 'nixpkgs-master':
    'github:nixos/nixpkgs/ebe3ab4' (2025-12-25)
  → 'github:nixos/nixpkgs/088b069' (2025-12-27)
2025-12-26 21:35:18 -05:00
0f8f6f96d6 moved scratch pool to zfs encryption 2025-12-26 21:26:25 -05:00
4cb4bd6f3d removing qbit 2025-12-26 18:48:11 -05:00
c046710258 updating desktop kernelPackages 2025-12-26 11:53:26 -05:00
7f9fbe3602 updated to pkgs.zfs_2_4 2025-12-26 11:18:17 -05:00
github-actions[bot]
8ee3b4d6e5 flake.lock: Update
Flake lock file updates:

• Updated input 'firefox-addons':
    'gitlab:rycee/nur-expressions/0f01129?dir=pkgs/firefox-addons' (2025-12-19)
  → 'gitlab:rycee/nur-expressions/981ca9c?dir=pkgs/firefox-addons' (2025-12-25)
• Updated input 'home-manager':
    'github:nix-community/home-manager/bb35f07' (2025-12-19)
  → 'github:nix-community/home-manager/91cdb0e' (2025-12-25)
• Updated input 'nixos-hardware':
    'github:nixos/nixos-hardware/9154f45' (2025-11-29)
  → 'github:nixos/nixos-hardware/c5db956' (2025-12-24)
• Updated input 'nixpkgs':
    'github:nixos/nixpkgs/c6245e8' (2025-12-18)
  → 'github:nixos/nixpkgs/3e2499d' (2025-12-25)
• Updated input 'nixpkgs-master':
    'github:nixos/nixpkgs/704c236' (2025-12-20)
  → 'github:nixos/nixpkgs/ebe3ab4' (2025-12-25)
• Updated input 'sops-nix':
    'github:Mic92/sops-nix/443a7f2' (2025-12-15)
  → 'github:Mic92/sops-nix/9836912' (2025-12-21)
2025-12-26 11:18:17 -05:00
18b7fb2d60 added runners 2025-12-25 21:11:43 -05:00
2f1fa5c750 converted to github pat 2025-12-25 21:11:43 -05:00
164d0dd59e fixed bugs 2025-12-24 20:17:24 -05:00
d4459643ab zero padded nix builder names 2025-12-24 20:17:24 -05:00
c09dba0c37 fixed container dns 2025-12-24 20:17:24 -05:00
409f376166 setup a isolated vlan for the runners 2025-12-24 20:17:24 -05:00
a9a6e1f932 harding nix_builder.nix 2025-12-24 20:17:24 -05:00
6472f07a88 removed acceleration = "cuda"; 2025-12-21 21:24:17 -05:00
github-actions[bot]
51c79f6b40 flake.lock: Update
Flake lock file updates:

• Updated input 'firefox-addons':
    'gitlab:rycee/nur-expressions/cefce78?dir=pkgs/firefox-addons' (2025-11-21)
  → 'gitlab:rycee/nur-expressions/0f01129?dir=pkgs/firefox-addons' (2025-12-19)
• Updated input 'home-manager':
    'github:nix-community/home-manager/d10a9b1' (2025-11-21)
  → 'github:nix-community/home-manager/bb35f07' (2025-12-19)
• Updated input 'nixos-hardware':
    'github:nixos/nixos-hardware/899dc44' (2025-11-11)
  → 'github:nixos/nixos-hardware/9154f45' (2025-11-29)
• Updated input 'nixpkgs':
    'github:nixos/nixpkgs/89c2b23' (2025-11-17)
  → 'github:nixos/nixpkgs/c6245e8' (2025-12-18)
• Updated input 'nixpkgs-master':
    'github:nixos/nixpkgs/8a7cf7e' (2025-11-22)
  → 'github:nixos/nixpkgs/704c236' (2025-12-20)
• Updated input 'sops-nix':
    'github:Mic92/sops-nix/877bb49' (2025-11-20)
  → 'github:Mic92/sops-nix/443a7f2' (2025-12-15)
2025-12-21 21:24:17 -05:00
b0d5147296 removed ssh setting from nix_builder.nix 2025-12-21 20:54:58 -05:00
c56082b516 tighten security 2025-12-21 11:49:09 -05:00
34b728c88f added ./qmk.nix to rhapsody-in-green 2025-12-21 11:49:09 -05:00
5697458bad adding udev rules 2025-12-21 11:49:09 -05:00
276c2ac74b adding qmk.nix to rhapsody-in-green 2025-12-21 11:49:09 -05:00
69e5aa20d5 removed ssh-keys 2025-12-21 09:39:11 -05:00
3d1f773fa5 testing unix socket 2025-12-21 09:39:11 -05:00
14dd1fe52e removed nvidia gpu from jeeves 2025-12-21 08:35:01 -05:00
30fe41ea1b removed tests 2025-12-20 23:09:19 -05:00
3a17c5514d testing nix socket 2025-12-20 22:47:19 -05:00
c6586db91e testing sshless nix-cache copy 2025-12-20 22:26:30 -05:00
81b199373e adding unifi to brain HA 2025-12-20 22:00:45 -05:00
a957e23041 fixed energy out integrations 2025-12-20 22:00:45 -05:00
52389f729d adding more extraPackages to brain HA 2025-12-20 22:00:45 -05:00
cc2a609f52 added gitea to haproxy 2025-12-20 17:18:51 -05:00
ca4693a1ba removed log.console-warn 2025-12-20 17:18:51 -05:00
90e5e0855d added gitea to postgress.nix 2025-12-20 17:18:51 -05:00
e339667c2b updated gitea settings 2025-12-20 17:18:51 -05:00
85540ee920 setting up gitea 2025-12-20 17:18:51 -05:00
3be1b8aa8f fixed some coderabit issues 2025-12-20 12:20:21 -05:00
7c56954cda get splendor code ruff complient 2025-12-20 12:20:21 -05:00
290f972346 ran ruff check python --fix --unsafe-fixes 2025-12-20 12:20:21 -05:00
72c3ccfb6d ran ruff check python --fix 2025-12-20 12:20:21 -05:00
9630633ff5 temp 2025-12-20 12:20:21 -05:00
8c83f306b2 added more options for simulat.py 2025-12-20 12:20:21 -05:00
5b4609dc3b added can_bot_afford to improve profiling 2025-12-20 12:20:21 -05:00
d1be25c6e8 added load_cards and load_nobles 2025-12-20 12:20:21 -05:00
31910586d2 added __init__.py to splendor 2025-12-20 12:20:21 -05:00
b8dfd0852a moved max_token_take to GameConfig 2025-12-20 12:20:21 -05:00
6ce622e93e speed up check_nobles_for_player 2025-12-20 12:20:21 -05:00
55e652a51d added .gitignore to splendor 2025-12-20 12:20:21 -05:00
b5455a5483 cleaned up human.py 2025-12-20 12:20:21 -05:00
8baf388061 starting splendor 2025-12-20 12:20:21 -05:00
7ffb7b4a37 adding AGENTS.md 2025-12-06 17:54:03 -05:00
eb04f4a56d 'system' has been renamed to/replaced by 'stdenv.hostPlatform.system' 2025-12-06 12:45:55 -05:00
5b8e543226 added environment sensor 2025-12-06 12:33:10 -05:00
da48f62195 splint HA battery monitoring 2025-12-06 12:33:10 -05:00
60f2ab1039 testing google antigravity 2025-12-06 12:24:54 -05:00
47 changed files with 691 additions and 992 deletions

View File

@@ -25,4 +25,4 @@ jobs:
- name: Build default package - name: Build default package
run: "nixos-rebuild build --flake ./#${{ matrix.system }}" run: "nixos-rebuild build --flake ./#${{ matrix.system }}"
- name: copy to nix-cache - name: copy to nix-cache
run: nix copy --to ssh://jeeves .#nixosConfigurations.${{ matrix.system }}.config.system.build.toplevel run: nix copy --accept-flake-config --to unix:///host-nix/var/nix/daemon-socket/socket .#nixosConfigurations.${{ matrix.system }}.config.system.build.toplevel

13
.vscode/settings.json vendored
View File

@@ -81,7 +81,6 @@
"FASTFOX", "FASTFOX",
"ffmpegthumbnailer", "ffmpegthumbnailer",
"filebot", "filebot",
"filebrowser",
"fileroller", "fileroller",
"findbar", "findbar",
"Fira", "Fira",
@@ -98,6 +97,7 @@
"getch", "getch",
"getmaxyx", "getmaxyx",
"ghdeploy", "ghdeploy",
"gitea",
"globalprivacycontrol", "globalprivacycontrol",
"gparted", "gparted",
"gtts", "gtts",
@@ -116,7 +116,9 @@
"httpchk", "httpchk",
"hurlenko", "hurlenko",
"hwloc", "hwloc",
"ical",
"ignorelist", "ignorelist",
"improv",
"INITDB", "INITDB",
"iocharset", "iocharset",
"ioit", "ioit",
@@ -126,6 +128,8 @@
"jnoortheen", "jnoortheen",
"jsbc", "jsbc",
"kagi", "kagi",
"keyformat",
"keylocation",
"kuma", "kuma",
"lazer", "lazer",
"levelname", "levelname",
@@ -225,12 +229,9 @@
"pylint", "pylint",
"pymetno", "pymetno",
"pymodbus", "pymodbus",
"pyopenweathermap",
"pyownet", "pyownet",
"pytest", "pytest",
"qbit",
"qbittorrent",
"qbittorrentvpn",
"qbitvpn",
"quicksuggest", "quicksuggest",
"radarr", "radarr",
"readahead", "readahead",
@@ -288,9 +289,11 @@
"twimg", "twimg",
"typer", "typer",
"uaccess", "uaccess",
"ubiquiti",
"ublock", "ublock",
"uiprotect", "uiprotect",
"uitour", "uitour",
"unifi",
"unrar", "unrar",
"unsubmitted", "unsubmitted",
"uptimekuma", "uptimekuma",

5
AGENTS.md Normal file
View File

@@ -0,0 +1,5 @@
## Dev environment tips
- use treefmt to format all files
- make python code ruff compliant
- use pytest to test python code

View File

@@ -16,7 +16,6 @@
./nh.nix ./nh.nix
./nix.nix ./nix.nix
./programs.nix ./programs.nix
./safe_reboot.nix
./ssh.nix ./ssh.nix
./snapshot_manager.nix ./snapshot_manager.nix
]; ];
@@ -24,7 +23,7 @@
boot = { boot = {
tmp.useTmpfs = true; tmp.useTmpfs = true;
kernelPackages = lib.mkDefault pkgs.linuxPackages_6_12; kernelPackages = lib.mkDefault pkgs.linuxPackages_6_12;
zfs.package = lib.mkDefault pkgs.zfs_2_3; zfs.package = lib.mkDefault pkgs.zfs_2_4;
}; };
hardware.enableRedistributableFirmware = true; hardware.enableRedistributableFirmware = true;
@@ -50,11 +49,6 @@
PYTHONPATH = "${inputs.self}/"; PYTHONPATH = "${inputs.self}/";
}; };
safe_reboot = {
enable = lib.mkDefault true;
datasetPrefix = "root_pool/";
};
zfs = { zfs = {
trim.enable = lib.mkDefault true; trim.enable = lib.mkDefault true;
autoScrub.enable = lib.mkDefault true; autoScrub.enable = lib.mkDefault true;

View File

@@ -1,56 +0,0 @@
{
config,
inputs,
lib,
pkgs,
...
}:
let
cfg = config.services.safe_reboot;
python_command =
lib.escapeShellArgs (
[
"${pkgs.my_python}/bin/python"
"-m"
"python.tools.safe_reboot"
]
++ lib.optionals (cfg.drivePath != null) [ cfg.drivePath ]
++ [
"--dataset-prefix"
cfg.datasetPrefix
"--check-only"
]
);
in
{
options.services.safe_reboot = {
enable = lib.mkEnableOption "Safe reboot dataset/drive validation";
datasetPrefix = lib.mkOption {
type = lib.types.str;
default = "root_pool/";
description = "Dataset prefix that must have exec enabled before rebooting.";
};
drivePath = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = null;
description = "Drive path that must exist before rebooting. Set to null to skip.";
};
};
config = lib.mkIf cfg.enable {
systemd.services.safe-reboot-check = {
description = "Safe reboot validation";
before = [ "systemd-reboot.service" ];
wantedBy = [ "reboot.target" ];
partOf = [ "reboot.target" ];
path = [ pkgs.zfs ];
environment = {
PYTHONPATH = "${inputs.self}/";
};
serviceConfig = {
Type = "oneshot";
ExecStart = python_command;
};
};
};
}

View File

@@ -1,8 +1,8 @@
{ pkgs, ... }: { pkgs, ... }:
{ {
boot = { boot = {
kernelPackages = pkgs.linuxPackages_6_17; kernelPackages = pkgs.linuxPackages_6_18;
zfs.package = pkgs.zfs_unstable; zfs.package = pkgs.zfs_2_4;
}; };
hardware.bluetooth = { hardware.bluetooth = {

View File

@@ -1,129 +0,0 @@
esphome:
name: batteries
friendly_name: batteries
esp32:
board: esp32dev
framework:
type: arduino
logger:
api:
encryption:
key: !secret api_key
external_components:
- source: github://syssi/esphome-jk-bms@main
ota:
- platform: esphome
password: !secret ota_password
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
captive_portal:
esp32_ble_tracker:
scan_parameters:
interval: 1100ms
window: 1100ms
active: true
ble_client:
- mac_address: "C8:47:80:29:0F:DB"
id: jk_ble0
- mac_address: "C8:47:80:37:9D:DD"
id: jk_ble1
jk_bms_ble:
- ble_client_id: jk_ble0
protocol_version: JK02_32S
throttle: 1s
id: jk_bms0
- ble_client_id: jk_ble1
protocol_version: JK02_32S
throttle: 1s
id: jk_bms1
sensor:
# BMS1 sensors
- platform: jk_bms_ble
jk_bms_ble_id: jk_bms0
total_voltage:
name: "JK0 Total Voltage"
current:
name: "JK0 Current"
state_of_charge:
name: "JK0 SoC"
power:
name: "JK0 Power"
temperature_sensor_1:
name: "JK0 Temp 1"
temperature_sensor_2:
name: "JK0 Temp 2"
balancing:
name: "JK0 balancing"
charging_cycles:
name: "JK0 charging cycles"
total_runtime:
name: "JK0 total runtime"
balancing_current:
name: "JK0 balancing current"
# BMS2 sensors
- platform: jk_bms_ble
jk_bms_ble_id: jk_bms1
total_voltage:
name: "JK1 Total Voltage"
current:
name: "JK1 Current"
state_of_charge:
name: "JK1 SoC"
power:
name: "Jk1 Power"
temperature_sensor_1:
name: "JK1 Temp 1"
temperature_sensor_2:
name: "Jk1 Temp 2"
balancing:
name: "JK1 balancing"
charging_cycles:
name: "JK1 charging cycles"
total_runtime:
name: "JK1 total runtime"
balancing_current:
name: "JK1 balancing current"
text_sensor:
- platform: jk_bms_ble
jk_bms_ble_id: jk_bms0
errors:
name: "JK0 Errors"
- platform: jk_bms_ble
jk_bms_ble_id: jk_bms1
errors:
name: "JK1 Errors"
switch:
- platform: jk_bms_ble
jk_bms_ble_id: jk_bms0
charging:
name: "JK0 Charging"
discharging:
name: "JK0 Discharging"
balancer:
name: "JK0 Balancing"
- platform: jk_bms_ble
jk_bms_ble_id: jk_bms1
charging:
name: "JK1 Charging"
discharging:
name: "JK1 Discharging"
balancer:
name: "JK1 Balancing"

132
esphome/battery0.yml Normal file
View File

@@ -0,0 +1,132 @@
esphome:
name: batteries
friendly_name: batteries
esp32:
board: esp32dev
framework:
type: arduino
logger:
api:
encryption:
key: !secret api_key
external_components:
- source: github://syssi/esphome-jk-bms@main
ota:
- platform: esphome
password: !secret ota_password
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
fast_connect: on
captive_portal:
esp32_ble_tracker:
scan_parameters:
interval: 1100ms
window: 1100ms
active: true
ble_client:
- mac_address: "C8:47:80:29:0F:DB"
id: jk_ble0
jk_bms_ble:
- ble_client_id: jk_ble0
protocol_version: JK02_32S
throttle: 1s
id: jk_bms0
button:
- platform: jk_bms_ble
retrieve_settings:
name: "JK0 retrieve settings"
retrieve_device_info:
name: "JK0 retrieve device info"
sensor:
- platform: jk_bms_ble
jk_bms_ble_id: jk_bms0
total_voltage:
name: "JK0 Total Voltage"
state_of_charge:
name: "JK0 SoC"
charging_power:
name: "JK0 charging power"
discharging_power:
name: "JK0 discharging power"
temperature_sensor_1:
name: "JK0 Temp 1"
temperature_sensor_2:
name: "JK0 Temp 2"
balancing:
name: "JK0 balancing"
total_runtime:
name: "JK0 total runtime"
balancing_current:
name: "JK0 balancing current"
delta_cell_voltage:
name: "JK0 cell delta voltage"
average_cell_voltage:
name: "JK0 cell average voltage"
cell_voltage_1:
name: "JK0 cell voltage 1"
cell_voltage_2:
name: "JK0 cell voltage 2"
cell_voltage_3:
name: "JK0 cell voltage 3"
cell_voltage_4:
name: "JK0 cell voltage 4"
cell_voltage_5:
name: "JK0 cell voltage 5"
cell_voltage_6:
name: "JK0 cell voltage 6"
cell_voltage_7:
name: "JK0 cell voltage 7"
cell_voltage_8:
name: "JK0 cell voltage 8"
cell_resistance_1:
name: "JK0 cell resistance 1"
cell_resistance_2:
name: "JK0 cell resistance 2"
cell_resistance_3:
name: "JK0 cell resistance 3"
cell_resistance_4:
name: "JK0 cell resistance 4"
cell_resistance_5:
name: "JK0 cell resistance 5"
cell_resistance_6:
name: "JK0 cell resistance 6"
cell_resistance_7:
name: "JK0 cell resistance 7"
cell_resistance_8:
name: "JK0 cell resistance 8"
total_charging_cycle_capacity:
name: "JK0 total charging cycle capacity"
text_sensor:
- platform: jk_bms_ble
jk_bms_ble_id: jk_bms0
errors:
name: "JK0 Errors"
switch:
- platform: jk_bms_ble
jk_bms_ble_id: jk_bms0
charging:
name: "JK0 Charging"
discharging:
name: "JK0 Discharging"
balancer:
name: "JK0 Balancing"
- platform: ble_client
ble_client_id: jk_ble0
name: "JK0 enable bluetooth connection"
id: ble_client_switch0

132
esphome/battery1.yml Normal file
View File

@@ -0,0 +1,132 @@
esphome:
name: battery1
friendly_name: battery1
esp32:
board: esp32dev
framework:
type: arduino
logger:
api:
encryption:
key: !secret api_key
external_components:
- source: github://syssi/esphome-jk-bms@main
ota:
- platform: esphome
password: !secret ota_password
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
fast_connect: on
captive_portal:
esp32_ble_tracker:
scan_parameters:
interval: 1100ms
window: 1100ms
active: true
ble_client:
- mac_address: "C8:47:80:37:9D:DD"
id: jk_ble1
jk_bms_ble:
- ble_client_id: jk_ble1
protocol_version: JK02_32S
throttle: 1s
id: jk_bms1
button:
- platform: jk_bms_ble
retrieve_settings:
name: "JK1 retrieve settings"
retrieve_device_info:
name: "JK1 retrieve device info"
sensor:
- platform: jk_bms_ble
jk_bms_ble_id: jk_bms1
total_voltage:
name: "JK1 Total Voltage"
state_of_charge:
name: "JK1 SoC"
charging_power:
name: "JK1 charging power"
discharging_power:
name: "JK1 discharging power"
temperature_sensor_1:
name: "JK1 Temp 1"
temperature_sensor_2:
name: "JK1 Temp 2"
balancing:
name: "JK1 balancing"
total_runtime:
name: "JK1 total runtime"
balancing_current:
name: "JK1 balancing current"
delta_cell_voltage:
name: "JK1 cell delta voltage"
average_cell_voltage:
name: "JK1 cell average voltage"
cell_voltage_1:
name: "JK1 cell voltage 1"
cell_voltage_2:
name: "JK1 cell voltage 2"
cell_voltage_3:
name: "JK1 cell voltage 3"
cell_voltage_4:
name: "JK1 cell voltage 4"
cell_voltage_5:
name: "JK1 cell voltage 5"
cell_voltage_6:
name: "JK1 cell voltage 6"
cell_voltage_7:
name: "JK1 cell voltage 7"
cell_voltage_8:
name: "JK1 cell voltage 8"
cell_resistance_1:
name: "JK1 cell resistance 1"
cell_resistance_2:
name: "JK1 cell resistance 2"
cell_resistance_3:
name: "JK1 cell resistance 3"
cell_resistance_4:
name: "JK1 cell resistance 4"
cell_resistance_5:
name: "JK1 cell resistance 5"
cell_resistance_6:
name: "JK1 cell resistance 6"
cell_resistance_7:
name: "JK1 cell resistance 7"
cell_resistance_8:
name: "JK1 cell resistance 8"
total_charging_cycle_capacity:
name: "JK1 total charging cycle capacity"
text_sensor:
- platform: jk_bms_ble
jk_bms_ble_id: jk_bms1
errors:
name: "JK1 Errors"
switch:
- platform: jk_bms_ble
jk_bms_ble_id: jk_bms1
charging:
name: "JK1 Charging"
discharging:
name: "JK1 Discharging"
balancer:
name: "JK1 Balancing"
- platform: ble_client
ble_client_id: jk_ble1
name: "JK1 enable bluetooth connection"
id: ble_client_switch0

48
esphome/environment.yml Normal file
View File

@@ -0,0 +1,48 @@
esphome:
name: "environment"
friendly_name: "environment"
esp32:
board: esp32dev
framework:
type: arduino
i2c:
sda: GPIO21
scl: GPIO22
scan: True
id: bus_a
sensor:
- platform: aht10
i2c_id: bus_a
address: 0x38
variant: AHT20
temperature:
name: "environment Temperature"
id: aht10_temperature
humidity:
name: "environment Humidity"
id: aht10_humidity
update_interval: 5s
web_server:
port: 80
logger:
level: DEBUG
api:
encryption:
key: !secret api_key
ota:
- platform: esphome
password: !secret ota_password
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
fast_connect: on
captive_portal:

36
flake.lock generated
View File

@@ -8,11 +8,11 @@
}, },
"locked": { "locked": {
"dir": "pkgs/firefox-addons", "dir": "pkgs/firefox-addons",
"lastModified": 1763697825, "lastModified": 1766762570,
"narHash": "sha256-AgCCcVPOi1tuzuW5/StlwqBjRWSX62oL97qWuxrq5UA=", "narHash": "sha256-Nevsj5NYurwp3I6nSMeh3uirwoinVSbCldqOXu4smms=",
"owner": "rycee", "owner": "rycee",
"repo": "nur-expressions", "repo": "nur-expressions",
"rev": "cefce78793603231be226fa77e7ad58e0e4899b8", "rev": "03d7d310ea91d6e4b47ed70aa86c781fcc5b38e1",
"type": "gitlab" "type": "gitlab"
}, },
"original": { "original": {
@@ -29,11 +29,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1763748372, "lastModified": 1766682973,
"narHash": "sha256-AUc78Qv3sWir0hvbmfXoZ7Jzq9VVL97l+sP9Jgms+JU=", "narHash": "sha256-GKO35onS711ThCxwWcfuvbIBKXwriahGqs+WZuJ3v9E=",
"owner": "nix-community", "owner": "nix-community",
"repo": "home-manager", "repo": "home-manager",
"rev": "d10a9b16b2a3ee28433f3d1c603f4e9f1fecb8e1", "rev": "91cdb0e2d574c64fae80d221f4bf09d5592e9ec2",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -44,11 +44,11 @@
}, },
"nixos-hardware": { "nixos-hardware": {
"locked": { "locked": {
"lastModified": 1762847253, "lastModified": 1766568855,
"narHash": "sha256-BWWnUUT01lPwCWUvS0p6Px5UOBFeXJ8jR+ZdLX8IbrU=", "narHash": "sha256-UXVtN77D7pzKmzOotFTStgZBqpOcf8cO95FcupWp4Zo=",
"owner": "nixos", "owner": "nixos",
"repo": "nixos-hardware", "repo": "nixos-hardware",
"rev": "899dc449bc6428b9ee6b3b8f771ca2b0ef945ab9", "rev": "c5db9569ac9cc70929c268ac461f4003e3e5ca80",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -60,11 +60,11 @@
}, },
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1763421233, "lastModified": 1766651565,
"narHash": "sha256-Stk9ZYRkGrnnpyJ4eqt9eQtdFWRRIvMxpNRf4sIegnw=", "narHash": "sha256-QEhk0eXgyIqTpJ/ehZKg9IKS7EtlWxF3N7DXy42zPfU=",
"owner": "nixos", "owner": "nixos",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "89c2b2330e733d6cdb5eae7b899326930c2c0648", "rev": "3e2499d5539c16d0d173ba53552a4ff8547f4539",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -76,11 +76,11 @@
}, },
"nixpkgs-master": { "nixpkgs-master": {
"locked": { "locked": {
"lastModified": 1763774007, "lastModified": 1766794443,
"narHash": "sha256-PPeHfKA11P09kBkBD5pS3tIAFjnG5muHQnODQGTY87g=", "narHash": "sha256-Q8IyTQ3Lu8vX/iqO3U+E4pjLbP1NsqFih6uElf8OYrQ=",
"owner": "nixos", "owner": "nixos",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "8a7cf7e9e18384533d9ecd0bfbcf475ac1dc497e", "rev": "088b069b8270ee36d83533c86b9f91d924d185d9",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -125,11 +125,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1763607916, "lastModified": 1766289575,
"narHash": "sha256-VefBA1JWRXM929mBAFohFUtQJLUnEwZ2vmYUNkFnSjE=", "narHash": "sha256-BOKCwOQQIP4p9z8DasT5r+qjri3x7sPCOq+FTjY8Z+o=",
"owner": "Mic92", "owner": "Mic92",
"repo": "sops-nix", "repo": "sops-nix",
"rev": "877bb495a6f8faf0d89fc10bd142c4b7ed2bcc0b", "rev": "9836912e37aef546029e48c8749834735a6b9dad",
"type": "github" "type": "github"
}, },
"original": { "original": {

View File

@@ -3,14 +3,14 @@
# When applied, the stable nixpkgs set (declared in the flake inputs) will be accessible through 'pkgs.stable' # When applied, the stable nixpkgs set (declared in the flake inputs) will be accessible through 'pkgs.stable'
stable = final: _prev: { stable = final: _prev: {
stable = import inputs.nixpkgs-stable { stable = import inputs.nixpkgs-stable {
system = final.system; system = final.stdenv.hostPlatform.system;
config.allowUnfree = true; config.allowUnfree = true;
}; };
}; };
# When applied, the master nixpkgs set (declared in the flake inputs) will be accessible through 'pkgs.master' # When applied, the master nixpkgs set (declared in the flake inputs) will be accessible through 'pkgs.master'
master = final: _prev: { master = final: _prev: {
master = import inputs.nixpkgs-master { master = import inputs.nixpkgs-master {
system = final.system; system = final.stdenv.hostPlatform.system;
config.allowUnfree = true; config.allowUnfree = true;
}; };
}; };

View File

@@ -1,111 +0,0 @@
"""Safe reboot helper."""
from __future__ import annotations
import logging
import sys
from pathlib import Path
from typing import TYPE_CHECKING, Annotated
import typer
from python.common import bash_wrapper, configure_logger
from python.zfs import Dataset, get_datasets
if TYPE_CHECKING:
from collections.abc import Sequence
logger = logging.getLogger(__name__)
def get_root_pool_datasets(dataset_prefix: str) -> list[Dataset]:
"""Return datasets that start with the provided prefix."""
return [dataset for dataset in get_datasets() if dataset.name.startswith(dataset_prefix)]
def get_non_executable_datasets(datasets: Sequence[Dataset]) -> list[str]:
"""Return dataset names that have exec disabled."""
return [dataset.name for dataset in datasets if dataset.exec.lower() != "on"]
def drive_present(drive: str) -> bool:
"""Check whether the provided drive exists."""
drive_path = drive.strip()
if not drive_path:
error = "Drive path cannot be empty"
raise ValueError(error)
return Path(drive_path).exists()
def reboot_system() -> None:
"""Call systemctl reboot."""
output, return_code = bash_wrapper("systemctl reboot")
if return_code != 0:
raise RuntimeError(output.strip() or "Failed to issue reboot command")
def validate_state(drive: str | None, dataset_prefix: str) -> list[str]:
"""Validate dataset and drive state."""
datasets = get_root_pool_datasets(dataset_prefix)
errors: list[str] = []
if not datasets:
errors.append(f"No datasets found with prefix {dataset_prefix}")
else:
non_exec_datasets = get_non_executable_datasets(datasets)
if non_exec_datasets:
errors.append(f"Datasets missing exec=on: {', '.join(non_exec_datasets)}")
if drive:
try:
if not drive_present(drive):
errors.append(f"Drive {drive} is not present")
except ValueError as err:
errors.append(str(err))
return errors
def reboot(
drive: Annotated[str | None, typer.Argument(help="Drive that must exist before rebooting.")] = None,
dataset_prefix: Annotated[
str,
typer.Option(
"--dataset-prefix",
"-p",
help="Datasets with this prefix are validated.",
),
] = "root_pool/",
dry_run: Annotated[
bool,
typer.Option(
"--check-only",
help="Only validate state without issuing the reboot command.",
),
] = False,
) -> None:
"""Validate datasets and drive before rebooting."""
configure_logger()
logger.info("Starting safe reboot checks")
if errors := validate_state(drive, dataset_prefix):
for error in errors:
logger.error(error)
sys.exit(1)
if dry_run:
logger.info("All checks passed")
return
logger.info("All checks passed, issuing reboot")
reboot_system()
def cli() -> None:
"""CLI entry point."""
typer.run(reboot)
if __name__ == "__main__":
cli()

View File

@@ -16,7 +16,6 @@
"Qihoo360-Light-R1-32B" "Qihoo360-Light-R1-32B"
]; ];
models = "/zfs/models"; models = "/zfs/models";
acceleration = "cuda";
openFirewall = true; openFirewall = true;
}; };
# open-webui = { # open-webui = {

View File

@@ -60,16 +60,20 @@
extraPackages = extraPackages =
python3Packages: with python3Packages; [ python3Packages: with python3Packages; [
aioesphomeapi # for esphome aioesphomeapi # for esphome
aiounifi # for ubiquiti integration
bleak-esphome # for esphome bleak-esphome # for esphome
esphome-dashboard-api # for esphome esphome-dashboard-api # for esphome
forecast-solar # for solar forecast forecast-solar # for solar forecast
gtts # not sure what wants this gtts # not sure what wants this
ical # for todo
jellyfin-apiclient-python # for jellyfin jellyfin-apiclient-python # for jellyfin
paho-mqtt # for mqtt paho-mqtt # for mqtt
psycopg2 # for postgresql psycopg2 # for postgresql
py-improv-ble-client # for esphome py-improv-ble-client # for esphome
pymodbus # for modbus pymodbus # for modbus
pyopenweathermap # for weather pyopenweathermap # for weather
uiprotect # for ubiquiti integration
unifi-discovery # for ubiquiti integration
]; ];
extraComponents = [ "isal" ]; extraComponents = [ "isal" ];
}; };

View File

@@ -1,45 +1,7 @@
template:
- sensor:
# Battery 0
- name: "JK0 charge power W"
unique_id: jk0_charge_power_w
unit_of_measurement: W
device_class: power
state_class: measurement
state: >
{% set p = states('sensor.batteries_jk0_power')|float(0) %}
{{ max(0, p) }}
- name: "JK0 discharge power W"
unique_id: jk0_discharge_power_w
unit_of_measurement: W
device_class: power
state_class: measurement
state: >
{% set p = states('sensor.batteries_jk0_power')|float(0) %}
{{ max(0, -p) }}
# Battery 1
- name: "JK1 charge power W"
unique_id: jk1_charge_power_w
unit_of_measurement: W
device_class: power
state_class: measurement
state: >
{% set p = states('sensor.batteries_jk1_power')|float(0) %}
{{ max(0, p) }}
- name: "JK1 discharge power W"
unique_id: jk1_discharge_power_w
unit_of_measurement: W
device_class: power
state_class: measurement
state: >
{% set p = states('sensor.batteries_jk1_power')|float(0) %}
{{ max(0, -p) }}
sensor: sensor:
# Battery 0 # Battery 0
- platform: integration - platform: integration
source: sensor.jk0_charge_power_w source: sensor.batteries_jk0_charging_power
name: "JK0 energy in" name: "JK0 energy in"
unique_id: jk0_energy_in_kwh unique_id: jk0_energy_in_kwh
unit_prefix: k unit_prefix: k
@@ -48,7 +10,7 @@ sensor:
max_sub_interval: max_sub_interval:
minutes: 5 minutes: 5
- platform: integration - platform: integration
source: sensor.jk0_discharge_power_w source: sensor.batteries_jk0_discharging_power
name: "JK0 energy out" name: "JK0 energy out"
unique_id: jk0_energy_out_kwh unique_id: jk0_energy_out_kwh
unit_prefix: k unit_prefix: k
@@ -59,7 +21,7 @@ sensor:
# Battery 1 # Battery 1
- platform: integration - platform: integration
source: sensor.jk1_charge_power_w source: sensor.battery1_jk1_charging_power
name: "JK1 energy in" name: "JK1 energy in"
unique_id: jk1_energy_in_kwh unique_id: jk1_energy_in_kwh
unit_prefix: k unit_prefix: k
@@ -68,7 +30,7 @@ sensor:
max_sub_interval: max_sub_interval:
minutes: 5 minutes: 5
- platform: integration - platform: integration
source: sensor.jk1_discharge_power_w source: sensor.battery1_jk1_discharging_power
name: "JK1 energy out" name: "JK1 energy out"
unique_id: jk1_energy_out_kwh unique_id: jk1_energy_out_kwh
unit_prefix: k unit_prefix: k

View File

@@ -17,7 +17,6 @@ in
./services ./services
./hardware.nix ./hardware.nix
./networking.nix ./networking.nix
./nvidia.nix
./programs.nix ./programs.nix
./runners ./runners
./syncthing.nix ./syncthing.nix

View File

@@ -1,60 +0,0 @@
{
config,
pkgs,
...
}:
let
vars = import ../vars.nix;
in
{
# environment.systemPackages = with pkgs; [ php.withExtensions ({ all, ... }: [ all.pdo_pgsql ]) ];
services.httpd = {
enable = true;
adminAddr = "webmaster@localhost";
enablePHP = true;
phpPackage = pkgs.php.withExtensions (
{ enabled, all }:
enabled
++ [
all.pdo
all.pdo_pgsql
]
);
extraModules = [ "rewrite" ];
virtualHosts.great_cloud_of_witnesses = {
hostName = "localhost";
listen = [
{
ip = "*";
port = 8092;
}
];
documentRoot = "${vars.services}/great_cloud_of_witnesses";
extraConfig = ''
<Directory "${vars.services}/great_cloud_of_witnesses">
AllowOverride All
Require all granted
</Directory>
'';
};
};
sops.secrets.gcw_password = {
sopsFile = ../../../users/secrets.yaml;
neededForUsers = true;
};
users = {
users.gcw = {
isSystemUser = true;
hashedPasswordFile = config.sops.secrets.gcw_password.path;
group = "gcw";
};
groups.gcw = { };
};
}

View File

@@ -1,47 +0,0 @@
let
vars = import ../vars.nix;
in
{
networking.firewall = {
allowedTCPPorts = [
6882
8081
8118
];
allowedUDPPorts = [ 6882 ];
};
virtualisation.oci-containers.containers.qbitvpn = {
image = "binhex/arch-qbittorrentvpn:5.0.3-1-01";
devices = [ "/dev/net/tun:/dev/net/tun" ];
extraOptions = [ "--cap-add=NET_ADMIN" ];
ports = [
"6882:6881"
"6882:6881/udp"
"8081:8081"
"8118:8118"
];
volumes = [
"${vars.docker_configs}/qbitvpn:/config"
"${vars.qbitvpn}:/data"
"${vars.qbitvpn_scratch}:/data/incomplete"
"/etc/localtime:/etc/localtime:ro"
];
environment = {
WEBUI_PORT = "8081";
PUID = "600";
PGID = "100";
VPN_ENABLED = "yes";
VPN_CLIENT = "openvpn";
STRICT_PORT_FORWARD = "yes";
ENABLE_PRIVOXY = "yes";
LAN_NETWORK = "192.168.90.0/24";
NAME_SERVERS = "1.1.1.1,1.0.0.1";
UMASK = "000";
DEBUG = "false";
DELUGE_DAEMON_LOG_LEVEL = "debug";
DELUGE_WEB_LOG_LEVEL = "debug";
};
environmentFiles = [ "${vars.secrets}/docker/qbitvpn" ];
autoStart = true;
};
}

View File

@@ -61,31 +61,7 @@ in
"luks-root-pool-wwn-0x55cd2e4150f01556-part2" = "luks-root-pool-wwn-0x55cd2e4150f01556-part2" =
makeLuksSSD "/dev/disk/by-id/wwn-0x55cd2e4150f01556-part2"; makeLuksSSD "/dev/disk/by-id/wwn-0x55cd2e4150f01556-part2";
# Media pool
"luks-media_pool-nvme-INTEL_SSDPEK1A118GA_BTOC14120V2J118B-part1" =
makeLuksSSD "/dev/disk/by-id/nvme-INTEL_SSDPEK1A118GA_BTOC14120V2J118B-part1";
"luks-media_pool-nvme-INTEL_SSDPEK1A118GA_BTOC14120WAG118B-part1" =
makeLuksSSD "/dev/disk/by-id/nvme-INTEL_SSDPEK1A118GA_BTOC14120WAG118B-part1";
"luks-media_pool-nvme-INTEL_SSDPE2ME012T4_CVMD5130000G1P2HGN-part1" =
makeLuksSSD "/dev/disk/by-id/nvme-INTEL_SSDPE2ME012T4_CVMD5130000G1P2HGN-part1";
"luks-media_pool-nvme-INTEL_SSDPE2ME012T4_CVMD5130000U1P2HGN-part1" =
makeLuksSSD "/dev/disk/by-id/nvme-INTEL_SSDPE2ME012T4_CVMD5130000U1P2HGN-part1";
# Scratch pool
"luks-scratch-pool-ata-CT480BX500SSD1_2314E6C3C01C-part1" =
makeLuksSSD "/dev/disk/by-id/ata-CT480BX500SSD1_2314E6C3C01C-part1";
"luks-scratch-pool-ata-CT480BX500SSD1_2314E6C3C01E-part1" =
makeLuksSSD "/dev/disk/by-id/ata-CT480BX500SSD1_2314E6C3C01E-part1";
# Storage pool # Storage pool
"luks-storage_pool-nvme-Samsung_SSD_970_EVO_Plus_2TB_S6S2NS0T834822N-part1" =
makeLuksSSD "/dev/disk/by-id/nvme-Samsung_SSD_970_EVO_Plus_2TB_S6S2NS0T834822N-part1";
"luks-storage_pool-nvme-Samsung_SSD_970_EVO_Plus_2TB_S6S2NS0T834817F-part1" =
makeLuksSSD "/dev/disk/by-id/nvme-Samsung_SSD_970_EVO_Plus_2TB_S6S2NS0T834817F-part1";
"luks-storage_pool-nvme-INTEL_MEMPEK1W016GA_PHBT828104DF016D-part1" =
makeLuksSSD "/dev/disk/by-id/nvme-INTEL_MEMPEK1W016GA_PHBT828104DF016D-part1";
"luks-storage_pool-nvme-INTEL_MEMPEK1W016GA_PHBT828105A8016D-part1" =
makeLuksSSD "/dev/disk/by-id/nvme-INTEL_MEMPEK1W016GA_PHBT828105A8016D-part1";
"luks-storage_pool-wwn-0x5000cca23bc438dd-part1" = "luks-storage_pool-wwn-0x5000cca23bc438dd-part1" =
makeLuksDevice "/dev/disk/by-id/wwn-0x5000cca23bc438dd-part1"; makeLuksDevice "/dev/disk/by-id/wwn-0x5000cca23bc438dd-part1";
"luks-storage_pool-wwn-0x5000cca23bd035f5-part1" = "luks-storage_pool-wwn-0x5000cca23bd035f5-part1" =

View File

@@ -2,31 +2,71 @@
networking = { networking = {
hostName = "jeeves"; hostName = "jeeves";
hostId = "0e15ce35"; hostId = "0e15ce35";
firewall.enable = true; firewall = {
enable = true;
interfaces.br-nix-builder = {
allowedTCPPorts = [ ];
allowedUDPPorts = [ ];
};
};
useNetworkd = true; useNetworkd = true;
}; };
systemd.network = { systemd.network = {
enable = true; enable = true;
wait-online = {
enable = false;
anyInterface = true;
};
netdevs = {
"20-br-nix-builder" = {
netdevConfig = {
Kind = "bridge";
Name = "br-nix-builder";
};
};
"30-internet-vlan" = {
netdevConfig = {
Kind = "vlan";
Name = "internet-vlan";
};
vlanConfig.Id = 100;
};
};
networks = { networks = {
"10-1GB_Primary" = { "10-1GB_Primary" = {
matchConfig.Name = "enp98s0f0"; matchConfig.Name = "enp97s0f1";
address = [ "192.168.99.14/24" ]; address = [ "192.168.99.14/24" ];
routes = [ { Gateway = "192.168.99.1"; } ]; routes = [ { Gateway = "192.168.99.1"; } ];
vlan = [ "internet-vlan" ];
linkConfig.RequiredForOnline = "routable"; linkConfig.RequiredForOnline = "routable";
}; };
"10-1GB_Secondary" = { "50-internet-vlan" = {
matchConfig.Name = "enp98s0f1"; matchConfig.Name = "internet-vlan";
DHCP = "yes"; bridge = [ "br-nix-builder" ];
linkConfig.RequiredForOnline = "no";
}; };
"10-10GB_Primary" = { "60-br-nix-builder" = {
matchConfig.Name = "enp97s0f0np0"; matchConfig.Name = "br-nix-builder";
DHCP = "yes"; bridgeConfig = { };
linkConfig.RequiredForOnline = "routable"; address = [ "192.168.3.10/24" ];
}; routingPolicyRules = [
"10-10GB_Secondary" = { {
matchConfig.Name = "enp97s0f1np1"; From = "192.168.3.0/24";
DHCP = "yes"; Table = 100;
Priority = 100;
}
];
routes = [
{
Gateway = "192.168.3.1";
Table = 100;
GatewayOnLink = false;
Metric = 2048;
PreferredSource = "192.168.3.10";
}
];
linkConfig.RequiredForOnline = "no";
}; };
}; };
}; };

View File

@@ -1,16 +0,0 @@
{ config, ... }:
{
nixpkgs.config.cudaSupport = true;
services.xserver.videoDrivers = [ "nvidia" ];
hardware = {
nvidia = {
modesetting.enable = true;
powerManagement.enable = true;
package = config.boot.kernelPackages.nvidiaPackages.beta;
nvidiaSettings = true;
open = false;
};
nvidia-container-toolkit.enable = true;
};
}

View File

@@ -16,11 +16,20 @@
}; };
services.nix_builder.containers = { services.nix_builder.containers = {
nix-builder-0.enable = true; nix-builder-00.enable = true;
nix-builder-1.enable = true; nix-builder-01.enable = true;
nix-builder-2.enable = true; nix-builder-02.enable = true;
nix-builder-3.enable = true; nix-builder-03.enable = true;
nix-builder-4.enable = true; nix-builder-04.enable = true;
nix-builder-5.enable = true; nix-builder-05.enable = true;
nix-builder-06.enable = true;
nix-builder-07.enable = true;
nix-builder-08.enable = true;
nix-builder-09.enable = true;
nix-builder-10.enable = true;
nix-builder-11.enable = true;
nix-builder-12.enable = true;
nix-builder-13.enable = true;
nix-builder-14.enable = true;
}; };
} }

View File

@@ -6,106 +6,130 @@
}: }:
with lib; with lib;
let let
vars = import ../vars.nix; vars = import ../vars.nix;
cfg = config.services.nix_builder;
in in
{ {
options.services.nix_builder.containers = mkOption { options.services.nix_builder = {
type = types.attrsOf ( bridgeName = mkOption {
types.submodule ( type = types.str;
{ name, ... }: default = "br-nix-builder";
{ description = "Bridge name for the builder containers.";
options.enable = mkEnableOption "GitHub runner container"; };
}
) containers = mkOption {
); type = types.attrsOf (
default = { }; types.submodule (
description = "GitHub runner container configurations"; { name, ... }:
{
options.enable = mkEnableOption "GitHub runner container";
}
)
);
default = { };
description = "GitHub runner container configurations";
};
}; };
config.containers = mapAttrs ( config = {
name: cfg: containers = mapAttrs (
mkIf cfg.enable { name: containerCfg:
autoStart = true; mkIf containerCfg.enable {
bindMounts = { autoStart = true;
"/storage" = { privateNetwork = true;
mountPoint = "/zfs/media/github-runners/${name}"; hostBridge = cfg.bridgeName;
isReadOnly = false; ephemeral = true;
bindMounts = {
storage = {
hostPath = "/zfs/media/github-runners/${name}";
mountPoint = "/zfs/media/github-runners/${name}";
isReadOnly = false;
};
host-nix = {
mountPoint = "/host-nix/var/nix/daemon-socket";
hostPath = "/nix/var/nix/daemon-socket";
isReadOnly = false;
};
pat = {
hostPath = "${vars.secrets}/services/github-runners/runner_pat";
mountPoint = "${vars.secrets}/services/github-runners/runner_pat";
isReadOnly = true;
};
}; };
"/secrets".mountPoint = "${vars.secrets}/services/github-runners/${name}"; config =
"ssh-keys".mountPoint = "${vars.secrets}/services/github-runners/id_ed25519_github-runners"; {
}; config,
config = pkgs,
{ lib,
config, ...
pkgs, }:
lib, {
... networking = {
}: useDHCP = lib.mkDefault true;
{ interfaces.eth0.useDHCP = true;
nix.settings = { # Ensure containers don't inherit the host's stub resolver (127.0.0.53) which was causing issues
trusted-substituters = [ useHostResolvConf = false;
"https://cache.nixos.org"
"https://cache.tmmworkshop.com"
"https://nix-community.cachix.org"
];
substituters = [
"https://cache.nixos.org/?priority=2&want-mass-query=true"
"https://cache.tmmworkshop.com/?priority=2&want-mass-query=true"
"https://nix-community.cachix.org/?priority=10&want-mass-query=true"
];
trusted-public-keys = [
"cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY="
"cache.tmmworkshop.com:jHffkpgbmEdstQPoihJPYW9TQe6jnQbWR2LqkNGV3iA="
"nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs="
];
experimental-features = [
"flakes"
"nix-command"
];
};
programs.ssh.extraConfig = ''
Host jeeves
Port 629
User github-runners
HostName jeeves
IdentityFile ${vars.secrets}/services/github-runners/id_ed25519_github-runners
StrictHostKeyChecking no
UserKnownHostsFile /dev/null
'';
nixpkgs = {
overlays = builtins.attrValues outputs.overlays;
config.allowUnfree = true;
};
services.github-runners.${name} = {
enable = true;
replace = true;
workDir = "/zfs/media/github-runners/${name}";
url = "https://github.com/RichieCahill/dotfiles";
extraLabels = [ "nixos" ];
tokenFile = "${vars.secrets}/services/github-runners/${name}";
user = "github-runners";
group = "github-runners";
extraPackages = with pkgs; [
nixfmt-rfc-style
nixos-rebuild
openssh
treefmt
my_python
];
};
users = {
users.github-runners = {
shell = pkgs.bash;
isSystemUser = true;
group = "github-runners";
uid = 601;
}; };
groups.github-runners.gid = 601; nix.settings = {
trusted-substituters = [
"https://cache.nixos.org"
"https://cache.tmmworkshop.com"
"https://nix-community.cachix.org"
];
substituters = [
"https://cache.nixos.org/?priority=2&want-mass-query=true"
"https://cache.tmmworkshop.com/?priority=2&want-mass-query=true"
"https://nix-community.cachix.org/?priority=10&want-mass-query=true"
];
trusted-public-keys = [
"cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY="
"cache.tmmworkshop.com:jHffkpgbmEdstQPoihJPYW9TQe6jnQbWR2LqkNGV3iA="
"nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs="
];
experimental-features = [
"flakes"
"nix-command"
];
sandbox = true;
allowed-users = [ "github-runners" ];
trusted-users = [
"root"
"github-runners"
];
};
nixpkgs = {
overlays = builtins.attrValues outputs.overlays;
config.allowUnfree = true;
};
services.github-runners.${name} = {
enable = true;
replace = true;
workDir = "/zfs/media/github-runners/${name}";
url = "https://github.com/RichieCahill/dotfiles";
extraLabels = [ "nixos" ];
tokenFile = "${vars.secrets}/services/github-runners/runner_pat";
user = "github-runners";
group = "github-runners";
extraPackages = with pkgs; [
nixfmt-rfc-style
nixos-rebuild
treefmt
my_python
];
};
users = {
users.github-runners = {
shell = pkgs.bash;
isSystemUser = true;
group = "github-runners";
uid = 601;
};
groups.github-runners.gid = 601;
};
system.stateVersion = "24.05";
}; };
system.stateVersion = "24.11"; }
}; ) cfg.containers;
} };
) config.services.nix_builder.containers;
} }

View File

@@ -12,31 +12,30 @@ sudo zpool add storage -o ashift=12 special mirror
sudo zpool add storage -o ashift=12 logs mirror sudo zpool add storage -o ashift=12 logs mirror
# scratch # scratch
sudo zpool create -o ashift=12 -O acltype=posixacl -O atime=off -O dnodesize=auto -O xattr=sa -O compression=zstd -m /zfs/scratch scratch sudo zpool create scratch -o ashift=12 -O acltype=posixacl -O atime=off -O dnodesize=auto -O xattr=sa -O compression=zstd -O encryption=aes-256-gcm -O keyformat=hex -O keylocation=file:///key -m /zfs/scratch
# media datasets # media datasets
sudo zfs create -o compression=zstd-9 media/docker sudo zfs create media/secure -o encryption=aes-256-gcm -o keyformat=hex -o keylocation=file:///root/zfs.key
sudo zfs create -o compression=zstd-9 -o sync=disabled media/github-runners sudo zfs create media/secure/docker -o compression=zstd-9
sudo zfs create -o copies=3 media/notes sudo zfs create media/secure/github-runners -o compression=zstd-9 -o sync=disabled
sudo zfs create -o compression=zstd-9 media/plex sudo zfs create media/secure/home_assistant -o compression=zstd-19
sudo zfs create -o compression=zstd-9 media/services sudo zfs create media/secure/notes -o copies=2
sudo zfs create -o compression=zstd-19 media/home_assistant sudo zfs create media/secure/postgres -o recordsize=16k -o primarycache=metadata
sudo zfs create -o exec=off media/share sudo zfs create media/secure/services -o compression=zstd-9
sudo zfs create -o recordsize=16k -o primarycache=metadata -o mountpoint=/zfs/media/database/postgres media/postgres sudo zfs create media/secure/share -o mountpoint=/zfs/media/share -o exec=off
# scratch datasets # scratch datasets
sudo zfs create -o recordsize=16k -o sync=disabled scratch/qbitvpn sudo zfs create scratch/kafka -o mountpoint=/zfs/scratch/kafka -o recordsize=1M
sudo zfs create -o recordsize=16k -o sync=disabled scratch/transmission sudo zfs create scratch/transmission -o mountpoint=/zfs/scratch/transmission -o recordsize=16k -o sync=disabled
sudo zfs create -o recordsize=1M scratch/kafka
# storage datasets # storage datasets
sudo zfs create -o recordsize=1M -o compression=zstd-19 storage/archive sudo zfs create storage/ollama -o recordsize=1M -o compression=zstd-19 -o sync=disabled
sudo zfs create -o compression=zstd-19 storage/main sudo zfs create storage/secure -o encryption=aes-256-gcm -o keyformat=hex -o keylocation=file:///root/zfs.key
sudo zfs create -o recordsize=16K -o compression=zstd-19 -o copies=2 storage/photos sudo zfs create storage/secure/archive -o recordsize=1M -o compression=zstd-19
sudo zfs create -o recordsize=1M -o compression=zstd-19 storage/plex sudo zfs create storage/secure/library -o recordsize=1M -o compression=zstd-19
sudo zfs create -o compression=zstd-19 -o copies=3 storage/secrets sudo zfs create storage/secure/main -o compression=zstd-19
sudo zfs create -o compression=zstd-19 storage/syncthing sudo zfs create storage/secure/photos -o recordsize=16K -o compression=zstd-19 -o copies=2
sudo zfs create -o recordsize=1M -o compression=zstd-9 -o exec=off -o sync=disabled storage/qbitvpn sudo zfs create storage/secure/plex -o recordsize=1M -o compression=zstd-19
sudo zfs create -o recordsize=1M -o compression=zstd-9 -o exec=off -o sync=disabled storage/transmission sudo zfs create storage/secure/secrets -o compression=zstd-19 -o copies=3
sudo zfs create -o recordsize=1M -o compression=zstd-19 storage/library sudo zfs create storage/secure/syncthing -o compression=zstd-19
sudo zfs create -o recordsize=1M -o compression=zstd-19 -o sync=disabled storage/ollama sudo zfs create storage/secure/transmission -o recordsize=1M -o compression=zstd-9 -o exec=off -o sync=disabled

View File

@@ -1,21 +0,0 @@
{
pkgs,
...
}:
let
vars = import ../vars.nix;
in
{
systemd.services.filebrowser = {
description = "filebrowser";
after = [ "network.target" ];
wantedBy = [ "multi-user.target" ];
serviceConfig = {
Type = "simple";
User = "richie";
Group = "users";
ExecStart = "${pkgs.filebrowser}/bin/filebrowser --root=/zfs --address=0.0.0.0 --database=${vars.docker_configs}/filebrowser/filebrowser.db";
Restart = "on-failure";
};
};
}

View File

@@ -0,0 +1,46 @@
let
vars = import ../vars.nix;
in
{
networking.firewall.allowedTCPPorts = [ 6443 ];
services.gitea = {
enable = true;
appName = "TMM Workshop";
stateDir = "${vars.services}/gitea/";
lfs.enable = true;
database = {
type = "postgres";
name = "gitea";
user = "gitea";
socket = "/run/postgresql";
port = 5432;
createDatabase = false;
};
settings = {
service.DISABLE_REGISTRATION = true;
server = {
DOMAIN = "tmmworkshop.com";
ROOT_URL = "https://gitea.tmmworkshop.com/";
HTTP_PORT = 6443;
SSH_PORT = 2223;
SSH_LISTEN_PORT = 2224;
START_SSH_SERVER = true;
PUBLIC_URL_DETECTION = "auto";
};
repository = {
ENABLE_PUSH_CREATE_USER = true;
DEFAULT_MERGE_STYLE = "rebase-merge";
};
log = {
LEVEL = "Trace";
ENABLE_SSH_LOG = true;
};
};
};
systemd.services.gitea = {
requires = [ "docker.service" ];
after = [ "docker.service" ];
};
}

View File

@@ -27,21 +27,21 @@ frontend ContentSwitching
# tmmworkshop.com # tmmworkshop.com
acl host_audiobookshelf hdr(host) -i audiobookshelf.tmmworkshop.com acl host_audiobookshelf hdr(host) -i audiobookshelf.tmmworkshop.com
acl host_cache hdr(host) -i cache.tmmworkshop.com acl host_cache hdr(host) -i cache.tmmworkshop.com
acl host_filebrowser hdr(host) -i filebrowser.tmmworkshop.com
acl host_homeassistant hdr(host) -i homeassistant.tmmworkshop.com acl host_homeassistant hdr(host) -i homeassistant.tmmworkshop.com
acl host_jellyfin hdr(host) -i jellyfin.tmmworkshop.com acl host_jellyfin hdr(host) -i jellyfin.tmmworkshop.com
acl host_share hdr(host) -i share.tmmworkshop.com acl host_share hdr(host) -i share.tmmworkshop.com
acl host_gcw hdr(host) -i gcw.tmmworkshop.com acl host_gcw hdr(host) -i gcw.tmmworkshop.com
acl host_n8n hdr(host) -i n8n.tmmworkshop.com acl host_n8n hdr(host) -i n8n.tmmworkshop.com
acl host_gitea hdr(host) -i gitea.tmmworkshop.com
use_backend audiobookshelf_nodes if host_audiobookshelf use_backend audiobookshelf_nodes if host_audiobookshelf
use_backend cache_nodes if host_cache use_backend cache_nodes if host_cache
use_backend filebrowser_nodes if host_filebrowser
use_backend homeassistant_nodes if host_homeassistant use_backend homeassistant_nodes if host_homeassistant
use_backend jellyfin if host_jellyfin use_backend jellyfin if host_jellyfin
use_backend share_nodes if host_share use_backend share_nodes if host_share
use_backend gcw_nodes if host_gcw use_backend gcw_nodes if host_gcw
use_backend n8n if host_n8n use_backend n8n if host_n8n
use_backend gitea if host_gitea
backend audiobookshelf_nodes backend audiobookshelf_nodes
mode http mode http
@@ -51,10 +51,6 @@ backend cache_nodes
mode http mode http
server server 127.0.0.1:5000 server server 127.0.0.1:5000
backend filebrowser_nodes
mode http
server server 127.0.0.1:8080
backend homeassistant_nodes backend homeassistant_nodes
mode http mode http
server server 192.168.90.35:8123 server server 192.168.90.35:8123
@@ -77,3 +73,7 @@ backend gcw_nodes
backend n8n backend n8n
mode http mode http
server server 127.0.0.1:5678 server server 127.0.0.1:5678
backend gitea
mode http
server server 127.0.0.1:6443

View File

@@ -9,13 +9,25 @@ in
host = "0.0.0.0"; host = "0.0.0.0";
loadModels = [ loadModels = [
"codellama:7b" "codellama:7b"
"deepscaler:1.5b"
"deepseek-r1:14b" "deepseek-r1:14b"
"deepseek-r1:32b" "deepseek-r1:32b"
"deepseek-r1:8b" "deepseek-r1:8b"
"devstral-small-2:24b"
"functiongemma:270m"
"gemma3:12b" "gemma3:12b"
"gemma3:27b" "gemma3:27b"
"gpt-oss:120b" "gpt-oss:120b"
"gpt-oss:20b" "gpt-oss:20b"
"llama3.1:70b"
"llama3.1:8b"
"llama3.2:1b"
"llama3.2:3b"
"magistral:24b"
"ministral-3:14b"
"nemotron-3-nano:30b"
"qwen3-coder:30b"
"qwen3-vl:32b"
"qwen3:14b" "qwen3:14b"
"qwen3:30b" "qwen3:30b"
]; ];

View File

@@ -0,0 +1,16 @@
let
vars = import ../vars.nix;
in
{
services.open-webui = {
stateDir = "${vars.services}/open_webui/";
enable = true;
openFirewall = true;
environment = {
ANONYMIZED_TELEMETRY = "False";
DO_NOT_TRACK = "True";
SCARF_NO_ANALYTICS = "True";
OLLAMA_API_BASE_URL = "http://127.0.0.1:11434";
};
};
}

View File

@@ -28,25 +28,7 @@ in
#type database DBuser origin-address auth-method #type database DBuser origin-address auth-method
local hass hass trust local hass hass trust
local gitea gitea trust
# ipv4
host hass hass 192.168.90.1/24 trust
host hass hass 127.0.0.1/32 trust
# ipv6
host hass hass ::1/128 trust
# megan
host megan megan 192.168.90.1/24 trust
host megan megan 127.0.0.1/32 trust
host gcw megan 192.168.90.1/24 trust
host gcw megan 127.0.0.1/32 trust
# gcw
local gcw gcw trust
host gcw gcw 192.168.90.1/24 trust
host gcw gcw 127.0.0.1/32 trust
# math # math
local postgres math trust local postgres math trust
@@ -97,17 +79,7 @@ in
}; };
} }
{ {
name = "megan"; name = "gitea";
ensureDBOwnership = true;
ensureClauses = {
login = true;
createrole = true;
createdb = true;
replication = true;
};
}
{
name = "gcw";
ensureDBOwnership = true; ensureDBOwnership = true;
ensureClauses = { ensureClauses = {
login = true; login = true;
@@ -128,12 +100,9 @@ in
} }
]; ];
ensureDatabases = [ ensureDatabases = [
"gcw"
"hass" "hass"
"gitea"
"math" "math"
"megan"
"mxr_dev"
"mxr_prod"
"n8n" "n8n"
"richie" "richie"
]; ];

View File

@@ -3,9 +3,7 @@ services = [
"audiobookshelf", "audiobookshelf",
"cloud_flare_tunnel", "cloud_flare_tunnel",
"haproxy", "haproxy",
"docker-qbitvpn",
"docker", "docker",
"filebrowser",
"home-assistant", "home-assistant",
"jellyfin", "jellyfin",
] ]

View File

@@ -64,24 +64,12 @@ hourly = 12
daily = 14 daily = 14
monthly = 2 monthly = 2
["scratch/qbitvpn"]
15_min = 0
hourly = 0
daily = 0
monthly = 0
["scratch/transmission"] ["scratch/transmission"]
15_min = 0 15_min = 0
hourly = 0 hourly = 0
daily = 0 daily = 0
monthly = 0 monthly = 0
["storage/qbitvpn"]
15_min = 0
hourly = 0
daily = 0
monthly = 0
["storage/transmission"] ["storage/transmission"]
15_min = 0 15_min = 0
hourly = 0 hourly = 0

View File

@@ -10,8 +10,6 @@ in
docker_configs = "${zfs_media}/docker/configs"; docker_configs = "${zfs_media}/docker/configs";
home_assistant = "${zfs_media}/home_assistant"; home_assistant = "${zfs_media}/home_assistant";
notes = "${zfs_media}/notes"; notes = "${zfs_media}/notes";
qbitvpn = "${zfs_storage}/qbitvpn";
qbitvpn_scratch = "${zfs_scratch}/qbitvpn";
secrets = "${zfs_storage}/secrets"; secrets = "${zfs_storage}/secrets";
services = "${zfs_media}/services"; services = "${zfs_media}/services";
share = "${zfs_media}/share"; share = "${zfs_media}/share";

View File

@@ -13,6 +13,7 @@
./hardware.nix ./hardware.nix
./llms.nix ./llms.nix
./syncthing.nix ./syncthing.nix
./qmk.nix
inputs.nixos-hardware.nixosModules.framework-13-7040-amd inputs.nixos-hardware.nixosModules.framework-13-7040-amd
]; ];

View File

@@ -0,0 +1,34 @@
{ pkgs, ... }:
{
environment.systemPackages = with pkgs; [
qmk
vial
];
services = {
udev = {
packages = [ pkgs.qmk-udev-rules ];
extraRules = ''
# Keychron keyboards
KERNEL=="hidraw*", ATTRS{idVendor}=="3434", MODE="0660", GROUP="plugdev"
SUBSYSTEM=="usb", ATTR{idVendor}=="3434", MODE="0660", GROUP="plugdev"
# Some boards use 32f0 as vendor id
KERNEL=="hidraw*", ATTRS{idVendor}=="32f0", MODE="0660", GROUP="plugdev"
SUBSYSTEM=="usb", ATTR{idVendor}=="32f0", MODE="0660", GROUP="plugdev"
# Keychron HID device permissions
SUBSYSTEM=="usb", ATTR{idVendor}=="3434", MODE="0660", GROUP="plugdev"
SUBSYSTEM=="hidraw", ATTRS{idVendor}=="3434", MODE="0660", GROUP="plugdev"
KERNEL=="hidraw*", ATTRS{idVendor}=="32f0", MODE="0660", GROUP="plugdev"
SUBSYSTEM=="usb", ATTR{idVendor}=="32f0", MODE="0660", GROUP="plugdev"
# Keychron / QMK common bootloaders
SUBSYSTEM=="usb", ATTR{idVendor}=="0483", ATTR{idProduct}=="df11", MODE="0660", GROUP="plugdev"
SUBSYSTEM=="usb", ATTR{idVendor}=="03eb", MODE="0660", GROUP="plugdev"
SUBSYSTEM=="usb", ATTR{idVendor}=="16c0", MODE="0660", GROUP="plugdev"
'';
};
};
}

View File

@@ -1,96 +0,0 @@
"""Tests for safe_reboot."""
from __future__ import annotations
from typing import TYPE_CHECKING
import pytest
from python.tools.safe_reboot import reboot
from python.zfs.dataset import Dataset
if TYPE_CHECKING:
from pytest_mock import MockerFixture
SAFE_REBOOT = "python.tools.safe_reboot"
def create_dataset(mocker: MockerFixture, name: str, exec_state: str) -> Dataset:
"""Create a mock dataset."""
dataset = mocker.MagicMock(spec=Dataset)
dataset.name = name
dataset.exec = exec_state
return dataset
def test_reboot_reboots_when_checks_pass(mocker: MockerFixture) -> None:
"""The command should reboot when all checks pass."""
dataset = create_dataset(mocker, "root_pool/root", "on")
mocker.patch(f"{SAFE_REBOOT}.get_datasets", return_value=(dataset,))
mocker.patch(f"{SAFE_REBOOT}.drive_present", return_value=True)
mock_bash = mocker.patch(f"{SAFE_REBOOT}.bash_wrapper", return_value=("", 0))
reboot("/dev/disk/root-drive")
mock_bash.assert_called_once_with("systemctl reboot")
def test_reboot_reboots_without_drive_requirement(mocker: MockerFixture) -> None:
"""The command should reboot even when no drive is provided."""
dataset = create_dataset(mocker, "root_pool/root", "on")
mocker.patch(f"{SAFE_REBOOT}.get_datasets", return_value=(dataset,))
mock_bash = mocker.patch(f"{SAFE_REBOOT}.bash_wrapper", return_value=("", 0))
reboot(None)
mock_bash.assert_called_once_with("systemctl reboot")
def test_reboot_errors_on_non_exec_dataset(mocker: MockerFixture) -> None:
"""The command should exit when a dataset lacks exec."""
dataset = create_dataset(mocker, "root_pool/root", "off")
mocker.patch(f"{SAFE_REBOOT}.get_datasets", return_value=(dataset,))
mocker.patch(f"{SAFE_REBOOT}.drive_present", return_value=True)
mocker.patch(f"{SAFE_REBOOT}.bash_wrapper", return_value=("", 0))
with pytest.raises(SystemExit) as excinfo:
reboot("/dev/disk/root-drive")
assert excinfo.value.code == 1
def test_reboot_errors_when_driver_missing(mocker: MockerFixture) -> None:
"""The command should exit when the requested driver is absent."""
dataset = create_dataset(mocker, "root_pool/root", "on")
mocker.patch(f"{SAFE_REBOOT}.get_datasets", return_value=(dataset,))
mocker.patch(f"{SAFE_REBOOT}.drive_present", return_value=False)
mocker.patch(f"{SAFE_REBOOT}.bash_wrapper", return_value=("", 0))
with pytest.raises(SystemExit) as excinfo:
reboot("/dev/disk/root-drive")
assert excinfo.value.code == 1
def test_reboot_errors_when_no_datasets_found(mocker: MockerFixture) -> None:
"""The command should exit when no datasets match the prefix."""
mocker.patch(f"{SAFE_REBOOT}.get_datasets", return_value=())
mocker.patch(f"{SAFE_REBOOT}.drive_present", return_value=True)
mocker.patch(f"{SAFE_REBOOT}.bash_wrapper", return_value=("", 0))
with pytest.raises(SystemExit) as excinfo:
reboot("/dev/disk/root-drive")
assert excinfo.value.code == 1
def test_reboot_check_only_skips_reboot(mocker: MockerFixture) -> None:
"""The command should only validate when --check-only is provided."""
dataset = create_dataset(mocker, "root_pool/root", "on")
mocker.patch(f"{SAFE_REBOOT}.get_datasets", return_value=(dataset,))
mocker.patch(f"{SAFE_REBOOT}.drive_present", return_value=True)
mock_bash = mocker.patch(f"{SAFE_REBOOT}.bash_wrapper", return_value=("", 0))
reboot("/dev/disk/root-drive", check_only=True)
mock_bash.assert_not_called()

View File

@@ -1,30 +0,0 @@
{
pkgs,
config,
...
}:
{
sops.secrets.megan_password = {
sopsFile = ../secrets.yaml;
neededForUsers = true;
};
users = {
users.megan = {
isNormalUser = true;
hashedPasswordFile = "${config.sops.secrets.megan_password.path}";
shell = pkgs.zsh;
group = "megan";
extraGroups = [
"audio"
"video"
"users"
];
uid = 1101;
};
groups.megan.gid = 1101;
};
home-manager.users.megan = import ./systems/${config.networking.hostName}.nix;
}

View File

@@ -1,9 +0,0 @@
{
imports = [
./direnv.nix
./git.nix
./zsh.nix
];
programs.starship.enable = true;
}

View File

@@ -1,8 +0,0 @@
{
programs.direnv = {
enable = true;
enableZshIntegration = true;
nix-direnv.enable = true;
};
}

View File

@@ -1,14 +0,0 @@
{
programs.git = {
enable = true;
settings = {
user = {
email = "mousikos112@gmail.com";
name = "megan";
};
pull.rebase = true;
color.ui = true;
};
lfs.enable = true;
};
}

View File

@@ -1,31 +0,0 @@
{
programs.zsh = {
enable = true;
syntaxHighlighting.enable = true;
history.size = 10000;
oh-my-zsh = {
enable = true;
plugins = [
"git"
"docker"
"docker-compose"
"colored-man-pages"
"rust"
"systemd"
"tmux"
"ufw"
"z"
];
};
shellAliases = {
"lrt" = "eza --icons -lsnew";
"ls" = "eza";
"ll" = "eza --long --group";
"la" = "eza --all";
"rspace" = "'for f in *\ *; do mv \"$f\" \"\${f// /_}\"; done'";
"rebuild" = "sudo nixos-rebuild switch --flake /home/richie/dotfiles#$HOST";
"nix-test" = "nixos-rebuild test --flake /home/richie/dotfiles";
};
};
}

View File

@@ -1,18 +0,0 @@
{ config, ... }:
{
imports = [
./cli
./programs.nix
];
programs = {
home-manager.enable = true;
git.enable = true;
};
home = {
username = "megan";
homeDirectory = "/home/${config.home.username}";
stateVersion = "24.05";
};
}

View File

@@ -1,41 +0,0 @@
{ pkgs, ... }:
{
home.packages = with pkgs; [
# cli
bat
btop
eza
fd
ffmpegthumbnailer
fzf
git
gnupg
imagemagick
jq
ncdu
neofetch
ouch
p7zip
poppler
rar
ripgrep
starship
tmux
unzip
yazi
zoxide
# system info
hwloc
lynis
pciutils
smartmontools
usbutils
# networking
iperf3
nmap
wget
# python
poetry
ruff
];
}

View File

@@ -1,5 +0,0 @@
{
imports = [
../home/global.nix
];
}

View File

@@ -41,6 +41,7 @@ in
"scanner" "scanner"
"transmission" "transmission"
"uaccess" "uaccess"
"uucp"
"wireshark" "wireshark"
]; ];
uid = 1000; uid = 1000;

View File

@@ -9,23 +9,25 @@
home.packages = with pkgs; [ home.packages = with pkgs; [
candy-icons candy-icons
chromium chromium
discord-canary
gimp gimp
gparted
jetbrains.datagrip
mediainfo mediainfo
nemo
nemo-fileroller
obs-studio obs-studio
obsidian obsidian
prismlauncher prismlauncher
proxychains
prusa-slicer prusa-slicer
signal-desktop
sweet-nova sweet-nova
util-linux util-linux
vlc vlc
# comms
discord-canary
signal-desktop
zoom-us zoom-us
# dev tools
claude-code
gparted
jetbrains.datagrip
master.antigravity-fhs
proxychains
# games # games
dwarf-fortress dwarf-fortress
tower-pixel-dungeon tower-pixel-dungeon