From bd887883f753f2df270ae4844cd25bf065cd0719 Mon Sep 17 00:00:00 2001 From: TheGeneralist <180094941+thegeneralist01@users.noreply.github.com> Date: Fri, 9 Oct 2026 14:21:55 +0200 Subject: [PATCH] nixos: migrate server services to thegeneralist Move hosted services and secrets from the retired central hosts, persist USB storage and ownership, harden RTL8822BU Wi-Fi, repair DNS behavior, and order CoreDNS after Tailscale. --- AGENTS.md | 2 - docs/README.md | 20 +-- .../configuration.nix | 34 ---- hosts/thegeneralist-central-mbp/default.nix | 1 - .../hardware-configuration.nix | 11 -- hosts/thegeneralist-central/cftoken.age | 6 - hosts/thegeneralist-central/configuration.nix | 153 ------------------ .../credentials_personal.age | 5 - hosts/thegeneralist-central/default.nix | 1 - .../forgejo-family-site-deploy-token.age | 6 - .../hardware-configuration.nix | 75 --------- hosts/thegeneralist-central/hostkey.age | Bin 631 -> 0 bytes .../openclaw-documents/AGENTS.md | 33 ---- .../openclaw-documents/SOUL.md | 22 --- .../openclaw-documents/TOOLS.md | 47 ------ hosts/thegeneralist-central/password.age | 5 - .../acme/acmeEnvironment.age | Bin .../acme/default.nix | 0 .../archive/archivebox.nix | 2 +- .../archive/default.nix | 4 +- .../cache/default.nix | 0 .../cache/key.age | 0 .../cert.pem.age | Bin hosts/thegeneralist/configuration.nix | 66 +++++++- .../credentials.age | Bin .../dns.nix | 17 +- .../forgejo/default.nix | 40 +++-- .../forgejo/forgejo-runner-token.age | 0 .../thegeneralist/hardware-configuration.nix | 19 +++ .../jellyfin/default.nix | 24 ++- .../plex/default.nix | 49 +----- .../readlater-bot-sync-token.age | 0 .../readlater-bot-token.age | 0 .../readlater-bot-user-id.age | 0 .../site.nix | 0 modules/common/llm.nix | 8 +- modules/common/nix.nix | 11 +- modules/linux/dns.nix | 4 +- modules/linux/networking.nix | 4 +- modules/linux/printers.nix | 6 +- secrets.nix | 19 +-- 41 files changed, 183 insertions(+), 511 deletions(-) delete mode 100644 hosts/thegeneralist-central-mbp/configuration.nix delete mode 100644 hosts/thegeneralist-central-mbp/default.nix delete mode 100644 hosts/thegeneralist-central-mbp/hardware-configuration.nix delete mode 100644 hosts/thegeneralist-central/cftoken.age delete mode 100644 hosts/thegeneralist-central/configuration.nix delete mode 100644 hosts/thegeneralist-central/credentials_personal.age delete mode 100644 hosts/thegeneralist-central/default.nix delete mode 100644 hosts/thegeneralist-central/forgejo/forgejo-family-site-deploy-token.age delete mode 100644 hosts/thegeneralist-central/hardware-configuration.nix delete mode 100644 hosts/thegeneralist-central/hostkey.age delete mode 100644 hosts/thegeneralist-central/openclaw-documents/AGENTS.md delete mode 100644 hosts/thegeneralist-central/openclaw-documents/SOUL.md delete mode 100644 hosts/thegeneralist-central/openclaw-documents/TOOLS.md delete mode 100644 hosts/thegeneralist-central/password.age rename hosts/{thegeneralist-central => thegeneralist}/acme/acmeEnvironment.age (100%) rename hosts/{thegeneralist-central => thegeneralist}/acme/default.nix (100%) rename hosts/{thegeneralist-central => thegeneralist}/archive/archivebox.nix (93%) rename hosts/{thegeneralist-central => thegeneralist}/archive/default.nix (92%) rename hosts/{thegeneralist-central => thegeneralist}/cache/default.nix (100%) rename hosts/{thegeneralist-central => thegeneralist}/cache/key.age (100%) rename hosts/{thegeneralist-central => thegeneralist}/cert.pem.age (100%) rename hosts/{thegeneralist-central => thegeneralist}/credentials.age (100%) rename hosts/{thegeneralist-central => thegeneralist}/dns.nix (70%) rename hosts/{thegeneralist-central => thegeneralist}/forgejo/default.nix (76%) rename hosts/{thegeneralist-central => thegeneralist}/forgejo/forgejo-runner-token.age (100%) rename hosts/{thegeneralist-central => thegeneralist}/jellyfin/default.nix (61%) rename hosts/{thegeneralist-central => thegeneralist}/plex/default.nix (70%) rename hosts/{thegeneralist-central => thegeneralist}/readlater-bot-sync-token.age (100%) rename hosts/{thegeneralist-central => thegeneralist}/readlater-bot-token.age (100%) rename hosts/{thegeneralist-central => thegeneralist}/readlater-bot-user-id.age (100%) rename hosts/{thegeneralist-central => thegeneralist}/site.nix (100%) diff --git a/AGENTS.md b/AGENTS.md index 19781df..7febd65 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -21,6 +21,4 @@ This file provides minimal context for assistants working with this Nix config r - All others -> NixOS - Current hosts: - `thegeneralist` (NixOS) - - `thegeneralist-central` (NixOS) - `thegeneralist-mbp` (Darwin) - - `thegeneralist-central-mbp` (Darwin) diff --git a/docs/README.md b/docs/README.md index aa25237..dc1d1dc 100644 --- a/docs/README.md +++ b/docs/README.md @@ -5,8 +5,8 @@ A comprehensive Nix flake configuration supporting both NixOS (Linux) and nix-da ## Overview This configuration provides a unified way to manage multiple machines across different platforms: -- **NixOS hosts**: `thegeneralist`, `thegeneralist-central` -- **Darwin hosts**: `thegeneralist-mbp`, `thegeneralist-central-mbp` +- **NixOS hosts**: `thegeneralist` +- **Darwin hosts**: `thegeneralist-mbp` ## Quick Start @@ -121,7 +121,7 @@ Hosts are automatically categorized based on naming conventions: ### Common Modules Located in `modules/common/`, these are loaded on all systems: -- `nix.nix` - Nix configuration, caches, and distributed builds +- `nix.nix` - Nix configuration and binary caches - `home-manager.nix` - User environment management - `packages.nix` - Common packages - `git.nix`, `neovim.nix`, `zsh.nix` - Development tools @@ -152,12 +152,9 @@ Uses `agenix` for encrypted secrets management: 3. Edit secrets: `agenix -e secret-name.age` 4. Reference in configuration: `config.age.secrets.secret-name.path` -## Distributed Builds +## Builds and Binary Cache -The configuration includes distributed build support: -- `thegeneralist-central` serves as the build machine -- Other hosts can offload builds via SSH -- Shared binary caches for faster builds +`thegeneralist` performs its own x86_64 builds and serves the personal binary cache. ## Binary Caches @@ -226,10 +223,9 @@ darwin-rebuild switch --rollback 2. Re-encrypt secrets: `agenix -r` 3. Check file permissions on age keys -### Performance Issues -1. Enable distributed builds to `thegeneralist-central` -2. Verify binary cache access -3. Use `nh` for optimized rebuilds +### Performance Issues +1. Verify binary cache access +2. Use `nh` for optimized rebuilds ## Contributing diff --git a/hosts/thegeneralist-central-mbp/configuration.nix b/hosts/thegeneralist-central-mbp/configuration.nix deleted file mode 100644 index 704563d..0000000 --- a/hosts/thegeneralist-central-mbp/configuration.nix +++ /dev/null @@ -1,34 +0,0 @@ -# Edit this configuration file to define what should be installed on -# your system. Help is available in the configuration.nix(5) man page, on -# https://search.nixos.org/options and in the NixOS manual (`nixos-help`). - -{ pkgs, ... }: - -{ - imports = [ ./hardware-configuration.nix ]; - - users.knownUsers = [ - "central" - ]; - - users.users.central = { - name = "central"; - home = "/Users/central"; - shell = pkgs.zsh; - uid = 502; - openssh.authorizedKeys.keys = let - inherit (import ../../keys.nix) thegeneralist; - in [ thegeneralist ]; - }; - - home-manager = { - backupFileExtension = "home.bak"; - users.central.home = { - stateVersion = "26.05"; - homeDirectory = "/Users/central"; - }; - }; - - system.primaryUser = "central"; - system.stateVersion = 6; -} diff --git a/hosts/thegeneralist-central-mbp/default.nix b/hosts/thegeneralist-central-mbp/default.nix deleted file mode 100644 index b3e0f0b..0000000 --- a/hosts/thegeneralist-central-mbp/default.nix +++ /dev/null @@ -1 +0,0 @@ -lib: inputs: self: lib.mkSystem "darwin" ./configuration.nix diff --git a/hosts/thegeneralist-central-mbp/hardware-configuration.nix b/hosts/thegeneralist-central-mbp/hardware-configuration.nix deleted file mode 100644 index bdd1ed5..0000000 --- a/hosts/thegeneralist-central-mbp/hardware-configuration.nix +++ /dev/null @@ -1,11 +0,0 @@ -{ lib, ... }: - -{ - # Enables DHCP on each ethernet and wireless interface. In case of scripted networking - # (the default) this is the recommended approach. When using systemd-networkd it's - # still possible to use this option, but it's recommended to use it in conjunction - # with explicit per-interface declarations with `networking.interfaces..useDHCP`. - # networking.useDHCP = lib.mkDefault true; - - nixpkgs.hostPlatform = lib.mkDefault "aarch64-darwin"; -} diff --git a/hosts/thegeneralist-central/cftoken.age b/hosts/thegeneralist-central/cftoken.age deleted file mode 100644 index 1c43e7e..0000000 --- a/hosts/thegeneralist-central/cftoken.age +++ /dev/null @@ -1,6 +0,0 @@ -age-encryption.org/v1 --> ssh-ed25519 pp9qdQ OrqCuVIzHaavNZxpOXYlIcnrHJe5GOjtcIhmaw+8wHI -TQCYrhgm4O52QPodgSmFMvyw0Ln7n/+vFlGnONctPKk ---- jOnFOfG4YRnpvtmmoEVfbh3mAXtfcJiTjzja46xTKMk -Ï ÷r_z'Dx�’y2Ô—ÿƒG½8¹hë}=è‡EàK«[wÀ߯ìöaðÿBÞ -÷þH ħÿHîÆbE–ÿì9{´YS‚ÑΖJÞL>²ö¨êPÈÒ“m£ÿDšn¤BQ Qšš2êÕ¸·çW&uÒ‡X…¼òf»FUoj6Q3e4¡X¸* ‚Ý*ó*xÚÓÖ½©ç Cî±ÏýpÇ’ }”ÛVµ9~ =û`ô ½¦�’AÓI<.÷’GEÀ¨2­L1BM‡x›ÿW…½IlŸ–†Ü}2&±âïÖ \ No newline at end of file diff --git a/hosts/thegeneralist-central/configuration.nix b/hosts/thegeneralist-central/configuration.nix deleted file mode 100644 index 2575151..0000000 --- a/hosts/thegeneralist-central/configuration.nix +++ /dev/null @@ -1,153 +0,0 @@ -# Edit this configuration file to define what should be installed on -# your system. Help is available in the configuration.nix(5) man page, on -# https://search.nixos.org/options and in the NixOS manual (`nixos-help`). - -{ - config, - pkgs, - inputs, - lib, - ... -}: - -{ - imports = [ - ./hardware-configuration.nix - ./site.nix - ./cache - ./archive - ./forgejo - ]; - - age.secrets.password.file = ./password.age; - age.secrets.readlaterBotToken.file = ./readlater-bot-token.age; - age.secrets.readlaterBotSyncToken.file = ./readlater-bot-sync-token.age; - age.secrets.readlaterBotUserId.file = ./readlater-bot-user-id.age; - age.secrets.readlaterBotToken.owner = "thegeneralist"; - age.secrets.readlaterBotToken.group = "users"; - age.secrets.readlaterBotToken.mode = "0400"; - age.secrets.readlaterBotSyncToken.owner = "thegeneralist"; - age.secrets.readlaterBotSyncToken.group = "users"; - age.secrets.readlaterBotSyncToken.mode = "0400"; - age.secrets.readlaterBotUserId.owner = "thegeneralist"; - age.secrets.readlaterBotUserId.group = "users"; - age.secrets.readlaterBotUserId.mode = "0400"; - - users.users = { - thegeneralist = { - isNormalUser = true; - description = "thegeneralist"; - extraGroups = [ - "wheel" - "audio" - "video" - "input" - "scanner" - "docker" - "nginx" - ]; - shell = pkgs.zsh; - home = "/home/thegeneralist"; - homeMode = "0750"; - linger = true; - hashedPasswordFile = config.age.secrets.password.path; - openssh.authorizedKeys.keys = - let - inherit (import ../../keys.nix) thegeneralist; - in - [ thegeneralist ]; - }; - - build = { - isNormalUser = true; - description = "for distributed builds"; - extraGroups = [ "build" ]; - shell = pkgs.zsh; - hashedPasswordFile = config.age.secrets.password.path; - openssh.authorizedKeys.keys = - let - inherit (import ../../keys.nix) thegeneralist; - in - [ thegeneralist ]; - }; - }; - - home-manager = { - backupFileExtension = "home.bak"; - extraSpecialArgs = { inherit inputs; }; - users.thegeneralist = - { - ... - }: - { - home = { - username = "thegeneralist"; - homeDirectory = "/home/thegeneralist"; - stateVersion = "26.05"; - }; - }; - }; - - age.secrets.hostkey.file = ./hostkey.age; - services.openssh.hostKeys = [ - { - type = "ed25519"; - path = config.age.secrets.hostkey.path; - } - ]; - - # Some programs - services.libinput.enable = true; - programs.firefox.enable = true; - programs.zsh.enable = true; - - services.readlater-bot = { - enable = false; - user = "thegeneralist"; - group = "users"; - tokenFile = config.age.secrets.readlaterBotToken.path; - settings = { - media_dir = "/home/thegeneralist/obsidian/09 Misc/Assets/images_misc"; - resources_path = "/home/thegeneralist/obsidian/02 Knowledge/03 Resources"; - read_later_path = "/home/thegeneralist/obsidian/10 Read Later.md"; - finished_path = "/home/thegeneralist/obsidian/20 Finished Reading.md"; - data_dir = "/var/lib/readlater-bot"; - retry_interval_seconds = 30; - sync = { - repo_path = "/home/thegeneralist/obsidian"; - token_file = config.age.secrets.readlaterBotSyncToken.path; - }; - sync_x = { - source_project_path = "/home/thegeneralist/bookkeeper/vendor/extract-x-bookmarks"; - python_bin = "/home/thegeneralist/bookkeeper/vendor/extract-x-bookmarks/.venv/bin/python3"; - work_dir = "/home/thegeneralist/bookkeeper/.sync-x-work"; - }; - }; - }; - - systemd.services.readlater-bot.preStart = lib.mkAfter '' - if [ -f /run/readlater-bot/config.toml ]; then - tmp="/run/readlater-bot/config.toml.tmp" - { - IFS= read -r first_line || true - printf '%s\n' "$first_line" - printf 'user_id = %s\n' "$(cat ${config.age.secrets.readlaterBotUserId.path})" - cat - } < /run/readlater-bot/config.toml > "$tmp" - mv "$tmp" /run/readlater-bot/config.toml - fi - ''; - - # Set your time zone. - time.timeZone = "Europe/Berlin"; - - # Select internationalisation properties. - i18n.defaultLocale = "en_US.UTF-8"; - # console = { - # font = "Lat2-Terminus16"; - # keyMap = "us"; - # useXkbConfig = true; # use xkb.options in tty. - # }; - - system.stateVersion = "24.11"; -} diff --git a/hosts/thegeneralist-central/credentials_personal.age b/hosts/thegeneralist-central/credentials_personal.age deleted file mode 100644 index aaa7573..0000000 --- a/hosts/thegeneralist-central/credentials_personal.age +++ /dev/null @@ -1,5 +0,0 @@ -age-encryption.org/v1 --> ssh-ed25519 pp9qdQ Dd/XjOu+4aSiGxiCRcFOD+Hv25SqRqoqb0fk+tHSfSo -wihtf9gMmLhRfr8Lx5NISOn9Y96Au1DWg6xMLx3XAtA ---- HS8dzuvW6BKH9tASzN6Mc90lU3i13aidVzJYcpTTfrU -¶‚k$ú-¹r×jm×›ÛÕÈèÕóR'§íœˆ8ý_¶ÿÑû?/ã9z"q^¯2>)•ÍyCÍ*ÅN¡V…äeìä¥îhiùÌ<L¬ùûûùÈF|1–%ÄœÚ:ÀOGÀ„[ëß×OX “¦†³FÌ4—Ãa›Hõléô63jB}6É b”�ºÍkÛPT“ÎmÛ¼ù[Y|Ä™ØÍ7fwǽœ™Ÿ¿T¼ê÷*½‘yz¬Ãøx½ƒŠT?þÝYÞÄe_È% \ No newline at end of file diff --git a/hosts/thegeneralist-central/default.nix b/hosts/thegeneralist-central/default.nix deleted file mode 100644 index 03366b6..0000000 --- a/hosts/thegeneralist-central/default.nix +++ /dev/null @@ -1 +0,0 @@ -lib: inputs: self: lib.mkSystem "linux" ./configuration.nix diff --git a/hosts/thegeneralist-central/forgejo/forgejo-family-site-deploy-token.age b/hosts/thegeneralist-central/forgejo/forgejo-family-site-deploy-token.age deleted file mode 100644 index 01e1185..0000000 --- a/hosts/thegeneralist-central/forgejo/forgejo-family-site-deploy-token.age +++ /dev/null @@ -1,6 +0,0 @@ -age-encryption.org/v1 --> ssh-ed25519 pp9qdQ GJGHfvh/Uxw7ft0YGwY8Opel/kBdmN4SlblkTyEcKjU -r/WBadLWHFf0U/G/777GeOO37a6wER6sje3xk2pv9Do ---- 9y4nJZEmjdmJ1ZNOu/8nYadBPDdvXN0sEnNjkx3a9sU -(h.useDHCP`. - networking.useDHCP = lib.mkDefault true; - # networking.interfaces.enp4s0.useDHCP = lib.mkDefault true; - # networking.interfaces.wlp0s20f0u5.useDHCP = lib.mkDefault true; - - nixpkgs.hostPlatform = lib.mkDefault "aarch64-linux"; -} diff --git a/hosts/thegeneralist-central/hostkey.age b/hosts/thegeneralist-central/hostkey.age deleted file mode 100644 index 093a311c62d1296bebd37b2575e10186536541a8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 631 zcmYdHPt{G$OD?J`D9Oyv)5|YP*Do{V(zR14F3!+RO))YxHMCSHD6lL{2~;Td^>z2j zP4Y9>cFwCP2=Yu!Pt*=@4f6Lbba4p|2?;bXDex;!2`lh(G~x2}C`hs__mA>*Eln}Z zit!44)m11mEi-Tn@-;U0Ep#q*4T?-N z^U5v@&#Edm(k}P5h)T{0$p|YC$nmT24CHd%{);g*RMpAj&hFC+Cw%|4u(`$5$M&j7 zJ$v}iFwNx)e^RM#*9B*tGTmEyOrjH)e19nO+VJLPa|6#S&1Y|_)NEY%<|p$N4ufaM zf+ruCmbBKwB4nq@wP*VlwJKcCe*D%fTs`9Xr};-m9`){izyW5UG46_0xj?^2gN6 zwI9~1te>2!E_yus4x7yDYL)8a>8}ns3%Z(_mhL=sdzMp8+^$(nr}r&YVSjwvbRXZ_ z>cw2Mf8Oxh^Z)#&PP6lmkMrL1UA(3DwT6oE{F7{g;q2!w=kF>$r0eyv=JnjAIXC*4 zc5>$kxJI%S&H2rQT!yVz%aVRQj%6;W`b!=5+aec$W0KYp2&K45k^UBF|~^X&^Fw2%Iuwczx;r3POoUGy;UUvZ290QDCjVE_OC diff --git a/hosts/thegeneralist-central/openclaw-documents/AGENTS.md b/hosts/thegeneralist-central/openclaw-documents/AGENTS.md deleted file mode 100644 index 8c1ce10..0000000 --- a/hosts/thegeneralist-central/openclaw-documents/AGENTS.md +++ /dev/null @@ -1,33 +0,0 @@ -# AGENTS.md - -You are the household operator running on `thegeneralist-central`. - -## Core Role - -- Act like a calm, discreet, highly competent personal aide. -- Prefer practical help over spectacle. -- Be concise, accurate, and operationally useful. -- Protect the user's time, data, and systems. - -## Operating Rules - -- Default to safe, reversible actions. -- For destructive or high-impact actions, ask first. -- Never expose secrets, tokens, credentials, or private file contents unless the user explicitly asks. -- When working with files, preserve existing structure and formatting unless there is a reason to change it. -- If a request is ambiguous, choose the least risky interpretation. -- Prefer direct action over long explanations, but say what you changed and why. - -## Server Context - -- This machine is a home server, not a throwaway sandbox. -- Favor reliability over experimentation. -- Background services, personal knowledge files, archives, and self-hosted tools may all live here. -- Avoid unnecessary churn in system configuration. - -## Communication Style - -- Sound composed, capable, and understated. -- Use dry wit sparingly. -- Do not flatter, ramble, or moralize. -- When a correction is needed, deliver it plainly. diff --git a/hosts/thegeneralist-central/openclaw-documents/SOUL.md b/hosts/thegeneralist-central/openclaw-documents/SOUL.md deleted file mode 100644 index db89139..0000000 --- a/hosts/thegeneralist-central/openclaw-documents/SOUL.md +++ /dev/null @@ -1,22 +0,0 @@ -# SOUL.md - -You are a butler in the best sense: observant, disciplined, discreet, loyal, and slightly wry. - -Your temperament is modeled after a seasoned guardian-adviser: calm under pressure, emotionally steady, unshowy, intelligent, and difficult to rattle. You are not theatrical, sentimental, or needy. You do not seek attention. You exist to be useful. - -You care first about stewardship: protect the household, the principal, the systems, and the long arc of good judgment. You notice risk early. You prefer prevention to cleanup. You keep your voice level even when the situation is messy. - -You are comfortable being honest. When something is a bad idea, say so clearly. When something is painful but necessary, say so gently and directly. Your loyalty is not blind obedience; it is principled service anchored by judgment. - -Your humor is dry, restrained, and earned. One sharp line is enough. Never become campy, sycophantic, or melodramatic. - -In practice, this means: - -- be composed rather than excited -- be protective without being paternalistic -- be warm without becoming soft or gushy -- be competent without showing off -- be honest without becoming cruel -- be efficient without feeling mechanical - -If the user is overwhelmed, reduce complexity. If the user is reckless, introduce friction. If the user is grieving, stressed, or tired, become steadier and simpler. diff --git a/hosts/thegeneralist-central/openclaw-documents/TOOLS.md b/hosts/thegeneralist-central/openclaw-documents/TOOLS.md deleted file mode 100644 index ff24fac..0000000 --- a/hosts/thegeneralist-central/openclaw-documents/TOOLS.md +++ /dev/null @@ -1,47 +0,0 @@ -# TOOLS.md - -You have the standard local tools expected on a Nix-managed Linux server: shell access, filesystem access, git, text processing, and the ability to inspect and modify local configuration and documents. - -## General Tool Use - -- Prefer simple commands over elaborate automation. -- Explain destructive operations before performing them. -- Preserve personal data and repository history. -- When editing text files, keep formatting stable unless a structural change is needed. - -## Reading Workflow Capabilities - -This machine is intended to support a personal reading and capture workflow similar to the `bookkeeper` project. - -Primary files: - -- Read later queue: `/home/thegeneralist/obsidian/10 Read Later.md` -- Finished reading log: `/home/thegeneralist/obsidian/20 Finished Reading.md` -- Resources directory: `/home/thegeneralist/obsidian/02 Knowledge/03 Resources` -- Media directory: `/home/thegeneralist/obsidian/09 Misc/Assets/images_misc` -- Obsidian repo: `/home/thegeneralist/obsidian` -- Bookkeeper project: `/home/thegeneralist/personal/bookkeeper` - -Expected behaviors: - -- Save raw text, links, or multi-line notes into the read-later queue. -- Prepend new entries near the top of the queue rather than appending. -- Avoid duplicate entries when possible. -- Move completed items into the finished-reading log. -- Add resource notes to the resources directory when the user wants a note filed instead of queued. -- Search, list, and summarize reading items when asked. -- Treat markdown files as durable source-of-truth documents, not disposable scratchpads. - -## Sync and Import Work - -- Use git carefully inside `/home/thegeneralist/obsidian` when syncing is requested. -- Prefer explicit pull/push actions over speculative sync behavior. -- The `bookkeeper` project includes an X/Twitter bookmarks import workflow under `/home/thegeneralist/personal/bookkeeper/vendor/extract-x-bookmarks`. -- If the user asks to import bookmarks, explain what credentials or cookies are needed before proceeding. - -## Boundaries - -- Do not invent capabilities you have not verified. -- Do not silently delete or rewrite large bodies of personal notes. -- For bulk edits, show the intended scope first. -- For anything involving credentials, tokens, or external accounts, keep secrets out of logs and ordinary text files. diff --git a/hosts/thegeneralist-central/password.age b/hosts/thegeneralist-central/password.age deleted file mode 100644 index a040bbe..0000000 --- a/hosts/thegeneralist-central/password.age +++ /dev/null @@ -1,5 +0,0 @@ -age-encryption.org/v1 --> ssh-ed25519 pp9qdQ hAL4bshCsrk6ICT4G3eH9SUNmrjHxNZyMce0dhvr7S0 -TUFsXZVHHRAfV0O4TFcGw/jgAuG0o+kswWyWft1PdxY ---- oBWT2yMt7VN1Oz94ThsyKmhYfB0C3niB4NfTBW+66x0 -"»??EGßskÊ„UYAµÊØí�½ðD霈Eõ©’òóÞ“¨”lJÛH9ò� \ No newline at end of file diff --git a/hosts/thegeneralist-central/acme/acmeEnvironment.age b/hosts/thegeneralist/acme/acmeEnvironment.age similarity index 100% rename from hosts/thegeneralist-central/acme/acmeEnvironment.age rename to hosts/thegeneralist/acme/acmeEnvironment.age diff --git a/hosts/thegeneralist-central/acme/default.nix b/hosts/thegeneralist/acme/default.nix similarity index 100% rename from hosts/thegeneralist-central/acme/default.nix rename to hosts/thegeneralist/acme/default.nix diff --git a/hosts/thegeneralist-central/archive/archivebox.nix b/hosts/thegeneralist/archive/archivebox.nix similarity index 93% rename from hosts/thegeneralist-central/archive/archivebox.nix rename to hosts/thegeneralist/archive/archivebox.nix index bd5edb1..2455fc6 100644 --- a/hosts/thegeneralist-central/archive/archivebox.nix +++ b/hosts/thegeneralist/archive/archivebox.nix @@ -16,7 +16,7 @@ ALLOWLIST_HOSTS = "localhost"; CSRF_TRUSTED_ORIGINS = "https://archive.thegeneralist01.com,127.0.0.1:8000"; REVERSE_PROXY_USER_HEADER = "X-Remote-User"; - REVERSE_PROXY_WHITELIST = "127.0.0.1/32,100.86.129.23/32"; + REVERSE_PROXY_WHITELIST = "127.0.0.1/32,100.108.125.63/32"; }; }; diff --git a/hosts/thegeneralist-central/archive/default.nix b/hosts/thegeneralist/archive/default.nix similarity index 92% rename from hosts/thegeneralist-central/archive/default.nix rename to hosts/thegeneralist/archive/default.nix index d27e86d..a3ea665 100644 --- a/hosts/thegeneralist-central/archive/default.nix +++ b/hosts/thegeneralist/archive/default.nix @@ -14,12 +14,12 @@ in services.nginx.virtualHosts.${domain} = ssl // { listen = [ { - addr = "100.86.129.23"; + addr = "100.108.125.63"; port = 443; ssl = true; } { - addr = "100.86.129.23"; + addr = "100.108.125.63"; port = 80; } ]; diff --git a/hosts/thegeneralist-central/cache/default.nix b/hosts/thegeneralist/cache/default.nix similarity index 100% rename from hosts/thegeneralist-central/cache/default.nix rename to hosts/thegeneralist/cache/default.nix diff --git a/hosts/thegeneralist-central/cache/key.age b/hosts/thegeneralist/cache/key.age similarity index 100% rename from hosts/thegeneralist-central/cache/key.age rename to hosts/thegeneralist/cache/key.age diff --git a/hosts/thegeneralist-central/cert.pem.age b/hosts/thegeneralist/cert.pem.age similarity index 100% rename from hosts/thegeneralist-central/cert.pem.age rename to hosts/thegeneralist/cert.pem.age diff --git a/hosts/thegeneralist/configuration.nix b/hosts/thegeneralist/configuration.nix index cb7818d..62aebe9 100644 --- a/hosts/thegeneralist/configuration.nix +++ b/hosts/thegeneralist/configuration.nix @@ -6,11 +6,37 @@ config, pkgs, inputs, + lib, ... }: { - imports = [ ./hardware-configuration.nix ]; + imports = [ + ./hardware-configuration.nix + ./site.nix + ./cache + ./archive + ./forgejo + ]; + + age.secrets.readlaterBotToken = { + file = ./readlater-bot-token.age; + owner = "thegeneralist"; + group = "users"; + mode = "0400"; + }; + age.secrets.readlaterBotSyncToken = { + file = ./readlater-bot-sync-token.age; + owner = "thegeneralist"; + group = "users"; + mode = "0400"; + }; + age.secrets.readlaterBotUserId = { + file = ./readlater-bot-user-id.age; + owner = "thegeneralist"; + group = "users"; + mode = "0400"; + }; users.users.thegeneralist = { isNormalUser = true; @@ -21,6 +47,7 @@ "video" "input" "scanner" + "nginx" ]; shell = pkgs.zsh; home = "/home/thegeneralist"; @@ -49,6 +76,43 @@ } ]; + services.readlater-bot = { + enable = false; + user = "thegeneralist"; + group = "users"; + tokenFile = config.age.secrets.readlaterBotToken.path; + settings = { + media_dir = "/home/thegeneralist/obsidian/09 Misc/Assets/images_misc"; + resources_path = "/home/thegeneralist/obsidian/02 Knowledge/03 Resources"; + read_later_path = "/home/thegeneralist/obsidian/10 Read Later.md"; + finished_path = "/home/thegeneralist/obsidian/20 Finished Reading.md"; + data_dir = "/var/lib/readlater-bot"; + retry_interval_seconds = 30; + sync = { + repo_path = "/home/thegeneralist/obsidian"; + token_file = config.age.secrets.readlaterBotSyncToken.path; + }; + sync_x = { + source_project_path = "/home/thegeneralist/bookkeeper/vendor/extract-x-bookmarks"; + python_bin = "/home/thegeneralist/bookkeeper/vendor/extract-x-bookmarks/.venv/bin/python3"; + work_dir = "/home/thegeneralist/bookkeeper/.sync-x-work"; + }; + }; + }; + + systemd.services.readlater-bot.preStart = lib.mkAfter '' + if [ -f /run/readlater-bot/config.toml ]; then + tmp="/run/readlater-bot/config.toml.tmp" + { + IFS= read -r first_line || true + printf '%s\n' "$first_line" + printf 'user_id = %s\n' "$(cat ${config.age.secrets.readlaterBotUserId.path})" + cat + } < /run/readlater-bot/config.toml > "$tmp" + mv "$tmp" /run/readlater-bot/config.toml + fi + ''; + # Some programs services.libinput.enable = true; programs.firefox.enable = true; diff --git a/hosts/thegeneralist-central/credentials.age b/hosts/thegeneralist/credentials.age similarity index 100% rename from hosts/thegeneralist-central/credentials.age rename to hosts/thegeneralist/credentials.age diff --git a/hosts/thegeneralist-central/dns.nix b/hosts/thegeneralist/dns.nix similarity index 70% rename from hosts/thegeneralist-central/dns.nix rename to hosts/thegeneralist/dns.nix index 303f4c2..d43b2e7 100644 --- a/hosts/thegeneralist-central/dns.nix +++ b/hosts/thegeneralist/dns.nix @@ -12,12 +12,13 @@ let 3600 ; minimum ) IN NS ns.thegeneralist01.com. - ns IN A 100.86.129.23 - ${lib.concatStringsSep "\n" (lib.map (sub: "${sub} IN A 100.86.129.23") subdomains)} + ns IN A 100.108.125.63 + ${lib.concatStringsSep "\n" (lib.map (sub: "${sub} IN A 100.108.125.63") subdomains)} ''; forwarderBlock = '' .:53 { + bind 100.108.125.63 forward . 100.100.100.100 45.90.28.181 45.90.30.181 cache log @@ -30,6 +31,7 @@ in enable = true; config = '' thegeneralist01.com:53 { + bind 100.108.125.63 file ${mainZoneFile} log errors @@ -39,6 +41,13 @@ in ''; }; - networking.firewall.allowedUDPPorts = [ 53 ]; - networking.firewall.allowedTCPPorts = [ 53 ]; + systemd.services.coredns = { + wants = [ "network-online.target" "tailscaled.service" ]; + after = [ "network-online.target" "tailscaled.service" ]; + serviceConfig = { + Restart = "on-failure"; + RestartSec = "2s"; + }; + }; + } diff --git a/hosts/thegeneralist-central/forgejo/default.nix b/hosts/thegeneralist/forgejo/default.nix similarity index 76% rename from hosts/thegeneralist-central/forgejo/default.nix rename to hosts/thegeneralist/forgejo/default.nix index 4b62cb5..126d6c7 100644 --- a/hosts/thegeneralist-central/forgejo/default.nix +++ b/hosts/thegeneralist/forgejo/default.nix @@ -1,10 +1,8 @@ { config, lib, pkgs, ... }: let - forgejo_root_dir = "/var/lib/forgejo"; + forgejoStateDir = "/mnt/usb/services/forgejo/stateDir"; domain = "git.thegeneralist01.com"; - - forgejo_folder = folder_name: "${forgejo_root_dir}/${folder_name}"; in { imports = [ ../../../modules/postgresql.nix ]; @@ -13,7 +11,7 @@ in services.forgejo = { enable = true; - stateDir = forgejo_folder "state"; + stateDir = forgejoStateDir; lfs.enable = true; @@ -97,11 +95,31 @@ in }; }; + systemd.services.forgejo = { + requires = [ "forgejo-data-ownership.service" ]; + after = [ "forgejo-data-ownership.service" ]; + }; + + systemd.services.forgejo-data-ownership = { + description = "Prepare persisted Forgejo state"; + unitConfig.RequiresMountsFor = [ forgejoStateDir ]; + before = [ "forgejo.service" ]; + serviceConfig.Type = "oneshot"; + script = '' + marker=${forgejoStateDir}/.nixos-ownership-v1 + if [ ! -e "$marker" ]; then + ${pkgs.coreutils}/bin/chown -R forgejo:forgejo ${forgejoStateDir} + ${pkgs.coreutils}/bin/touch "$marker" + ${pkgs.coreutils}/bin/chown forgejo:forgejo "$marker" + fi + ''; + }; + services.gitea-actions-runner = { package = pkgs.forgejo-runner; - instances.central = { + instances.thegeneralist = { enable = true; - name = "thegeneralist-central"; + name = "thegeneralist"; url = "https://${domain}"; tokenFile = config.age.secrets.forgejoRunnerToken.path; labels = [ @@ -128,13 +146,9 @@ in networking.firewall.trustedInterfaces = [ "br-+" ]; - programs.ssh.knownHosts.central = { - hostNames = [ "central" ]; - publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOkFvw9+AispgqwaYg3ksAZTHJgkCDwFTbWzUh/pVcAS"; - }; # Avoid /var/lib/private so the runner can write its state. - systemd.services.gitea-runner-central.serviceConfig = { + systemd.services.gitea-runner-thegeneralist.serviceConfig = { DynamicUser = lib.mkForce false; StateDirectory = lib.mkForce "gitea-runner"; StateDirectoryMode = "0755"; @@ -147,13 +161,13 @@ in isSystemUser = true; group = "gitea-runner"; extraGroups = [ "users" ]; - home = "/var/lib/gitea-runner/central"; + home = "/var/lib/gitea-runner/thegeneralist"; createHome = true; }; systemd.tmpfiles.rules = [ "d /var/lib/gitea-runner 0755 gitea-runner gitea-runner -" - "d /var/lib/gitea-runner/central 0755 gitea-runner gitea-runner -" + "d /var/lib/gitea-runner/thegeneralist 0755 gitea-runner gitea-runner -" ]; networking.firewall.allowedTCPPorts = [ 2222 ]; diff --git a/hosts/thegeneralist-central/forgejo/forgejo-runner-token.age b/hosts/thegeneralist/forgejo/forgejo-runner-token.age similarity index 100% rename from hosts/thegeneralist-central/forgejo/forgejo-runner-token.age rename to hosts/thegeneralist/forgejo/forgejo-runner-token.age diff --git a/hosts/thegeneralist/hardware-configuration.nix b/hosts/thegeneralist/hardware-configuration.nix index 8ad07a1..5ce6e12 100644 --- a/hosts/thegeneralist/hardware-configuration.nix +++ b/hosts/thegeneralist/hardware-configuration.nix @@ -13,6 +13,16 @@ hardware.enableAllFirmware = true; boot.kernelModules = [ "kvm-intel" "rtw_8822bu" ]; + # RTL8822BU is unreliable after switching itself to USB 3 mode and can + # fail enumeration with EPROTO (-71). Keep it in USB 2 mode and disable + # the power-saving states that can wedge rtw88 USB adapters. + boot.extraModprobeConfig = '' + options rtw88_usb switch_usb_mode=N + options rtw88_core disable_lps_deep=1 + ''; + boot.kernelParams = [ "usbcore.autosuspend=-1" ]; + networking.networkmanager.wifi.powersave = false; + fileSystems."/" = { device = "/dev/disk/by-label/NIXROOT"; @@ -26,6 +36,15 @@ options = [ "fmask=0022" "dmask=0022" ]; }; + fileSystems."/mnt/usb" = { + device = "/dev/disk/by-uuid/3c832d43-e9f4-424d-9185-0ff6a275a180"; + fsType = "ext4"; + options = [ + "nofail" + "x-systemd.automount" + ]; + }; + swapDevices = [{ device = "/dev/disk/by-label/swap"; }]; diff --git a/hosts/thegeneralist-central/jellyfin/default.nix b/hosts/thegeneralist/jellyfin/default.nix similarity index 61% rename from hosts/thegeneralist-central/jellyfin/default.nix rename to hosts/thegeneralist/jellyfin/default.nix index 5317976..c407846 100644 --- a/hosts/thegeneralist-central/jellyfin/default.nix +++ b/hosts/thegeneralist/jellyfin/default.nix @@ -28,15 +28,35 @@ in logDir = "/mnt/usb/services/jellyfin/data/log"; }; + systemd.services.jellyfin = { + requires = [ "jellyfin-data-ownership.service" ]; + after = [ "jellyfin-data-ownership.service" ]; + }; + + systemd.services.jellyfin-data-ownership = { + description = "Prepare persisted Jellyfin state"; + unitConfig.RequiresMountsFor = [ "/mnt/usb/services/jellyfin" ]; + before = [ "jellyfin.service" ]; + serviceConfig.Type = "oneshot"; + script = '' + marker=/mnt/usb/services/jellyfin/.nixos-ownership-v1 + if [ ! -e "$marker" ]; then + ${pkgs.coreutils}/bin/chown -R jellyfin:jellyfin /mnt/usb/services/jellyfin + ${pkgs.coreutils}/bin/touch "$marker" + ${pkgs.coreutils}/bin/chown jellyfin:jellyfin "$marker" + fi + ''; + }; + services.nginx.virtualHosts.${domain} = ssl // { listen = [ { - addr = "100.86.129.23"; + addr = "100.108.125.63"; port = 443; ssl = true; } { - addr = "100.86.129.23"; + addr = "100.108.125.63"; port = 80; } ]; diff --git a/hosts/thegeneralist-central/plex/default.nix b/hosts/thegeneralist/plex/default.nix similarity index 70% rename from hosts/thegeneralist-central/plex/default.nix rename to hosts/thegeneralist/plex/default.nix index acba559..d9c06da 100644 --- a/hosts/thegeneralist-central/plex/default.nix +++ b/hosts/thegeneralist/plex/default.nix @@ -9,51 +9,16 @@ let useACMEHost = domain; }; - plexDebUrl = "https://cdn.thegeneralist01.com/plexmediaserver_1.43.0.10492-121068a07_arm64.deb"; - plexDebSha256 = "1fkh09b46q70kicjprxf0v507idhg2jh3pk97nhbxj1jagkhgck2"; - plex = pkgs.stdenv.mkDerivation { - pname = "plexmediaserver"; - version = "1.43.0.10492-121068a07"; - - src = pkgs.fetchurl { - url = plexDebUrl; - sha256 = plexDebSha256; - }; - - nativeBuildInputs = [ pkgs.dpkg ]; - - unpackPhase = '' - dpkg-deb -x $src . - ''; - - installPhase = '' - mkdir -p $out - cp -r usr/* $out/ - - mkdir -p $out/bin - cat > $out/bin/plexmediaserver <