Compare commits
87 Commits
b5e358ee22
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2910dcb3ef | ||
|
|
8d62cffc8e | ||
|
|
7670f2fa94 | ||
|
|
b5c7024ea4 | ||
|
|
6d29835303 | ||
|
|
67119653b5 | ||
|
|
f95cf4a546 | ||
|
|
dc94b8fc44 | ||
|
|
b46e2a6269 | ||
|
|
fc62d7ab32 | ||
|
|
e0aa2b337e | ||
|
|
4fb24672bf | ||
|
|
81318b3fb1 | ||
|
|
ae1f68b105 | ||
|
|
b7a38d7634 | ||
|
|
6cd22f0448 | ||
|
|
9c73be46ac | ||
|
|
293f0a3096 | ||
|
|
d8606ad2ed | ||
|
|
7671ec686f | ||
|
|
5ed2ece05c | ||
|
|
2b1424cfd2 | ||
|
|
661629924e | ||
|
|
d7191f7a02 | ||
|
|
480c9a2a07 | ||
|
|
5e2e2ab29b | ||
|
|
b36c452c3a | ||
|
|
95c6cefd24 | ||
|
|
a8dda9d32d | ||
|
|
7a5f577806 | ||
|
|
a7482ee146 | ||
|
|
5382bf7251 | ||
|
|
ead7e5a379 | ||
|
|
416e8a4edc | ||
|
|
2da1278b37 | ||
|
|
60ccc776de | ||
| f7bef14c19 | |||
| ed85b66017 | |||
|
|
a2cb88c970 | ||
|
|
b7ce1866d0 | ||
|
|
6d5422f447 | ||
|
|
41298f0980 | ||
|
|
b7c4e38148 | ||
|
|
005addff1b | ||
|
|
17cd7ba593 | ||
|
|
893bb199b1 | ||
|
|
44e39fda6c | ||
|
|
229b989902 | ||
|
|
00a43a5a48 | ||
|
|
788ea5ad26 | ||
|
|
1fd29a5f4f | ||
|
|
a15db616b4 | ||
|
|
7cedfba30d | ||
|
|
ef9a11d76b | ||
|
|
c50c98e7b2 | ||
|
|
6079e6446c | ||
|
|
afbffaa203 | ||
|
|
c09268891e | ||
|
|
e1f7c2291a | ||
|
|
9e64325f5e | ||
|
|
6603fac1c4 | ||
|
|
cb1776d670 | ||
|
|
3517e394c6 | ||
|
|
81f9025dc9 | ||
|
|
2ef113bc0e | ||
|
|
d14a7ba395 | ||
|
|
eddef549e7 | ||
|
|
4ba0fa0dd5 | ||
|
|
08cc3379ad | ||
|
|
2a290f2fe2 | ||
|
|
0c7e745e55 | ||
|
|
542fd2485c | ||
|
|
caf7fbc590 | ||
|
|
ee11d72de8 | ||
|
|
b8ab2171dc | ||
|
|
dce2142794 | ||
|
|
237e120124 | ||
|
|
afdb5bfd99 | ||
|
|
d7f9ea971c | ||
|
|
f01817a15f | ||
|
|
917e741b7f | ||
|
|
0997fad0c6 | ||
|
|
ba4cf6c86b | ||
|
|
3f13527e51 | ||
|
|
efe5cb0f99 | ||
|
|
86557548db | ||
|
|
a74adc7f95 |
@@ -14,3 +14,8 @@ trust_level = "trusted"
|
|||||||
[mcp_servers.nixos-mcp]
|
[mcp_servers.nixos-mcp]
|
||||||
command = "nixos-mcp"
|
command = "nixos-mcp"
|
||||||
cwd = "/home/jawz/Development/NixOS"
|
cwd = "/home/jawz/Development/NixOS"
|
||||||
|
|
||||||
|
[mcp_servers.nixos]
|
||||||
|
command = "nix"
|
||||||
|
args = ["run", "github:utensils/mcp-nixos", "--"]
|
||||||
|
startup_timeout_sec = 300
|
||||||
|
|||||||
@@ -5,20 +5,30 @@ on:
|
|||||||
branches: [ main ]
|
branches: [ main ]
|
||||||
paths:
|
paths:
|
||||||
- 'scripts/**'
|
- 'scripts/**'
|
||||||
- 'docs/**'
|
- 'scripts/mcp-server/**'
|
||||||
|
- 'parts/packages.nix'
|
||||||
|
- 'flake.nix'
|
||||||
|
- 'flake.lock'
|
||||||
- '.gitea/workflows/mcp-tests.yml'
|
- '.gitea/workflows/mcp-tests.yml'
|
||||||
pull_request:
|
pull_request:
|
||||||
paths:
|
paths:
|
||||||
- 'scripts/**'
|
- 'scripts/**'
|
||||||
- 'docs/**'
|
- 'scripts/mcp-server/**'
|
||||||
|
- 'parts/packages.nix'
|
||||||
|
- 'flake.nix'
|
||||||
|
- 'flake.lock'
|
||||||
- '.gitea/workflows/mcp-tests.yml'
|
- '.gitea/workflows/mcp-tests.yml'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
mcp-tests:
|
mcp-tests:
|
||||||
runs-on: nixos
|
runs-on: nixos
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
working-directory: ${{ github.workspace }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Run MCP lint/format/tests via nix-shell
|
- name: Run MCP lint/format/tests via nix-shell
|
||||||
run: ./scripts/mcp-server/run-tests.sh
|
run: |
|
||||||
|
nix run .#mcp-tests
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ keys:
|
|||||||
- &devkey age1lufn6t35gs4wgevyr2gud4eec7lvkn7pgnnv4tja64ww3hef7gqq8fas37
|
- &devkey age1lufn6t35gs4wgevyr2gud4eec7lvkn7pgnnv4tja64ww3hef7gqq8fas37
|
||||||
- &workstation age17jlsydpgl35qx5ahc3exu44jt8dfa63chymt6xqp9xx0r6dh347qpg55cz
|
- &workstation age17jlsydpgl35qx5ahc3exu44jt8dfa63chymt6xqp9xx0r6dh347qpg55cz
|
||||||
- &server age15hx530yrqmhm80vsjmffyg9deq9gssj7hl5rsqdnsn3dwegj9qusv4sjf5
|
- &server age15hx530yrqmhm80vsjmffyg9deq9gssj7hl5rsqdnsn3dwegj9qusv4sjf5
|
||||||
- &vps age1ml3smrs5mwz4ds84gk0eyss86nwsmp07qh0npxsuae7lfwwpsghssavytw
|
- &vps age1v2ahkl759cftpcdq4mla2cvmgz4jlnmgj7qtgc9732zxrfvxf3lq76zjpr
|
||||||
creation_rules:
|
creation_rules:
|
||||||
- path_regex: secrets/secrets.yaml$
|
- path_regex: secrets/secrets.yaml$
|
||||||
key_groups:
|
key_groups:
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ Auto-generated from feature plans. Last updated: 2026-01-30
|
|||||||
- None (in-memory tool definitions; filesystem access for repo interactions) (002-mcp-server)
|
- None (in-memory tool definitions; filesystem access for repo interactions) (002-mcp-server)
|
||||||
- Nix (flakes; nixpkgs 25.11) + nixpkgs, flake-parts, sops-nix (003-vps-image-migration)
|
- Nix (flakes; nixpkgs 25.11) + nixpkgs, flake-parts, sops-nix (003-vps-image-migration)
|
||||||
- N/A (configuration repo) (003-vps-image-migration)
|
- N/A (configuration repo) (003-vps-image-migration)
|
||||||
|
- Nix (flakes; nixpkgs 25.11) + NixOS modules, sops-nix, nginx, wireguard, openssh, nftables (004-vps-migration)
|
||||||
|
- Files (configuration and secrets) (004-vps-migration)
|
||||||
|
|
||||||
- Documentation set (AI-facing constitution and playbooks) in Markdown (001-ai-docs)
|
- Documentation set (AI-facing constitution and playbooks) in Markdown (001-ai-docs)
|
||||||
|
|
||||||
@@ -28,9 +30,9 @@ specs/001-ai-docs/ # Planning artifacts (plan, research, tasks, data model
|
|||||||
- Keep language business-level and technology-agnostic in AI-facing docs.
|
- Keep language business-level and technology-agnostic in AI-facing docs.
|
||||||
|
|
||||||
## Recent Changes
|
## Recent Changes
|
||||||
|
- 004-vps-migration: Added Nix (flakes; nixpkgs 25.11) + NixOS modules, sops-nix, nginx, wireguard, openssh, iptables
|
||||||
- 003-vps-image-migration: Added Nix (flakes; nixpkgs 25.11) + nixpkgs, flake-parts, sops-nix
|
- 003-vps-image-migration: Added Nix (flakes; nixpkgs 25.11) + nixpkgs, flake-parts, sops-nix
|
||||||
- 003-vps-image-migration: Added [if applicable, e.g., PostgreSQL, CoreData, files or N/A]
|
- 003-vps-image-migration: Added [if applicable, e.g., PostgreSQL, CoreData, files or N/A]
|
||||||
- 003-vps-image-migration: Added Nix (flakes; nixpkgs 25.11) + nixpkgs, flake-parts, sops-nix
|
|
||||||
|
|
||||||
|
|
||||||
<!-- MANUAL ADDITIONS START -->
|
<!-- MANUAL ADDITIONS START -->
|
||||||
|
|||||||
230
README.org
230
README.org
@@ -6,198 +6,72 @@
|
|||||||
|
|
||||||
* Overview
|
* Overview
|
||||||
|
|
||||||
This repository contains my personal NixOS configuration flake, managing
|
This repository is a NixOS configuration flake with a local MCP server
|
||||||
multiple hosts with a modular approach. Designed the configuration for a
|
(`nixos-mcp`) that helps Codex CLI work with the repo’s documentation and
|
||||||
self-hosted infrastructure with services and development environments.
|
maintenance workflows. The README focuses on how to use the MCP server and
|
||||||
|
how the repo is structured.
|
||||||
|
|
||||||
* Architecture
|
* MCP Server (nixos-mcp)
|
||||||
|
|
||||||
** Hosts
|
** What it is
|
||||||
- =workstation= :: Main development machine with GNOME desktop
|
Local-only MCP server that exposes repo documentation helpers over stdio so
|
||||||
- =server= :: Primary server with containerized services
|
Codex CLI can read the constitution, playbooks, and reference map without
|
||||||
- =miniserver= :: Secondary server for additional services
|
manual navigation.
|
||||||
- =galaxy= :: Minimal configuration host
|
|
||||||
- =emacs= :: Development VM for Emacs configuration
|
|
||||||
|
|
||||||
** Key Features
|
** Tool Catalog
|
||||||
- Modular configuration system
|
- =show-constitution= :: Open =docs/constitution.md=
|
||||||
- SOPS-based secrets management
|
- =list-playbooks= :: List =docs/playbooks/=
|
||||||
- Container orchestration with Podman
|
- =show-reference= :: Open =docs/reference/index.md=
|
||||||
- Automated builds and caching
|
- =search-docs= :: Search the docs set
|
||||||
- Multi-language development environments
|
- =list-mcp-tasks= :: Show MCP tasks from =specs/002-mcp-server/tasks.md=
|
||||||
- Self-hosted service stack
|
- =sync-docs= :: Check tool catalog vs. docs anchors
|
||||||
|
|
||||||
* Quick Start
|
** Quick Start (new users)
|
||||||
|
|
||||||
** Prerequisites
|
|
||||||
- NixOS 23.05 or later
|
|
||||||
- SOPS configured with age keys
|
|
||||||
- SSH keys for remote builds
|
|
||||||
|
|
||||||
** Initial Setup
|
|
||||||
#+BEGIN_SRC bash
|
#+BEGIN_SRC bash
|
||||||
# Clone the repository git clone <repository-url> /home/jawz/Development/NixOS
|
# Enter the MCP dev shell (includes codex + nixos-mcp)
|
||||||
cd /home/jawz/Development/NixOS
|
nix develop .#mcp
|
||||||
|
|
||||||
# Install dependencies nix flake update
|
# Run the MCP server (stdio mode)
|
||||||
|
nixos-mcp
|
||||||
# Build and switch to configuration sudo nixos-rebuild switch --flake
|
|
||||||
.#<hostname>
|
|
||||||
#+END_SRC
|
#+END_SRC
|
||||||
|
Configure Codex CLI to use a local stdio MCP endpoint and allowlist
|
||||||
|
=nixos-mcp= in =.codex/requirements.toml=. The dev shell sets
|
||||||
|
=CODEX_HOME=.codex= for a repo-local Codex config.
|
||||||
|
|
||||||
** Development Environment
|
** Reference
|
||||||
#+BEGIN_SRC bash
|
Detailed invocation notes live in =docs/reference/mcp-server.md=.
|
||||||
# Enter development shell for specific language nix develop .#<language>
|
|
||||||
|
|
||||||
# Available languages: python, rust, go, haskell, javascript, julia, zig, sh,
|
* Repository Structure
|
||||||
cc, nix
|
|
||||||
#+END_SRC
|
|
||||||
|
|
||||||
* Configuration Structure
|
** Docs and Guidance
|
||||||
|
- =docs/constitution.md= :: AI constitution (authoritative rules)
|
||||||
|
- =docs/reference/index.md= :: Reference map
|
||||||
|
- =docs/reference/mcp-server.md= :: MCP server reference
|
||||||
|
- =docs/playbooks/= :: Repeatable workflows
|
||||||
|
- =specs/002-mcp-server/= :: MCP feature plan + tasks
|
||||||
|
|
||||||
** Core Configuration
|
** MCP Server
|
||||||
- =config/base.nix= :: Common system configuration
|
- =scripts/mcp-server/= :: Python MCP server implementation, tests, and CLI
|
||||||
- =config/jawz.nix= :: User and SSH configuration
|
- =modules/dev/mcp.nix= :: Dev shell and optional global install
|
||||||
- =config/stylix.nix= :: Theming configuration
|
|
||||||
- =config/schemes.nix= :: Color scheme definitions
|
|
||||||
|
|
||||||
** Host Configurations
|
** Flake Parts
|
||||||
- =hosts/<hostname>/configuration.nix= :: Host-specific settings
|
- =parts/core.nix= :: Shared library + base composition
|
||||||
- =hosts/<hostname>/hardware-configuration.nix= :: Hardware-specific config
|
- =parts/hosts.nix= :: Host definitions
|
||||||
- =hosts/<hostname>/toggles.nix= :: Feature toggles
|
- =parts/packages.nix= :: Package outputs
|
||||||
|
- =parts/devshells.nix= :: Dev shells (including MCP)
|
||||||
|
|
||||||
** Modules
|
* Flake Inputs (high level)
|
||||||
- =modules/apps/= :: Application packages and configurations
|
|
||||||
- =modules/dev/= :: Development environment modules
|
|
||||||
- =modules/servers/= :: Self-hosted service configurations
|
|
||||||
- =modules/services/= :: System service configurations
|
|
||||||
- =modules/scripts/= :: Custom scripts and utilities
|
|
||||||
- =modules/shell/= :: Shell and terminal configurations
|
|
||||||
|
|
||||||
* Services
|
** Core Inputs
|
||||||
|
- =nixpkgs= (25.11) and =nixpkgs-small= (25.11-small)
|
||||||
** Core Services
|
- =nixpkgs-unstable= (rolling)
|
||||||
- PostgreSQL 17 :: Database backend
|
- =flake-parts= (structure)
|
||||||
- Nginx :: Reverse proxy and web server
|
- =home-manager= (user configs)
|
||||||
- Podman :: Container runtime
|
- =stylix= (theming)
|
||||||
- Syncthing :: File synchronization
|
- =sops-nix= (secrets integration)
|
||||||
- WireGuard :: VPN connectivity
|
- =nur=, =nix-gaming=, =hyprland= (extra packages)
|
||||||
|
- Content inputs: =wallpapers=, =fonts=, =qbit_manage=
|
||||||
** Self-Hosted Applications
|
- Repo inputs: =jawz-scripts=, =prem2resolve=, =lidarr-mb-gap=
|
||||||
- Nextcloud :: File sharing and collaboration
|
|
||||||
- Gitea :: Git repository hosting
|
|
||||||
- Jellyfin :: Media server
|
|
||||||
- Plex :: Media streaming
|
|
||||||
- Sonarr/Radarr/Lidarr :: Media management
|
|
||||||
- Vaultwarden :: Password manager
|
|
||||||
- Homepage :: Service dashboard
|
|
||||||
- And more...
|
|
||||||
|
|
||||||
* Development
|
|
||||||
|
|
||||||
** Available Development Shells
|
|
||||||
The configuration provides development shells for my favorite programming
|
|
||||||
languages:
|
|
||||||
|
|
||||||
#+BEGIN_SRC bash
|
|
||||||
# Python development nix develop .#python
|
|
||||||
|
|
||||||
# Rust development nix develop .#rust
|
|
||||||
|
|
||||||
# Go development nix develop .#go
|
|
||||||
|
|
||||||
# JavaScript/Node.js development nix develop .#javascript
|
|
||||||
|
|
||||||
# Haskell development nix develop .#haskell
|
|
||||||
|
|
||||||
# Julia development nix develop .#julia
|
|
||||||
|
|
||||||
# Zig development nix develop .#zig
|
|
||||||
|
|
||||||
# Shell scripting nix develop .#sh
|
|
||||||
|
|
||||||
# C/C++ development nix develop .#cc
|
|
||||||
|
|
||||||
# Nix development nix develop .#nix
|
|
||||||
#+END_SRC
|
|
||||||
|
|
||||||
** Adding New Modules
|
|
||||||
1. Create module file in appropriate directory under =modules/=
|
|
||||||
2. Add module to =modules/modules.nix= if needed
|
|
||||||
3. Enable module in host configuration or toggles
|
|
||||||
|
|
||||||
** Adding New Hosts
|
|
||||||
1. Create host directory under =hosts/<hostname>/
|
|
||||||
2. Add =configuration.nix= and =hardware-configuration.nix=
|
|
||||||
3. Add host to =flake.nix= outputs
|
|
||||||
4. Create =toggles.nix= for feature management
|
|
||||||
|
|
||||||
* Secrets Management
|
|
||||||
|
|
||||||
** SOPS Configuration
|
|
||||||
Manage secrets using SOPS with age encryption:
|
|
||||||
|
|
||||||
- =secrets/secrets.yaml= :: Main secrets file
|
|
||||||
- =secrets/keys.yaml= :: SSH and encryption keys
|
|
||||||
- =secrets/env.yaml= :: Environment variables
|
|
||||||
- =secrets/wireguard.yaml= :: VPN configuration
|
|
||||||
- =secrets/certs.yaml= :: SSL certificates
|
|
||||||
|
|
||||||
** Adding New Secrets
|
|
||||||
#+BEGIN_SRC bash
|
|
||||||
# Edit secrets file sops secrets/secrets.yaml
|
|
||||||
|
|
||||||
# Add new secret sops -i -a 'new-secret: "value"' secrets/secrets.yaml
|
|
||||||
#+END_SRC
|
|
||||||
|
|
||||||
* CI/CD
|
|
||||||
|
|
||||||
** GitHub Actions
|
|
||||||
The repository includes automated workflows:
|
|
||||||
|
|
||||||
- =weekly-build-cache.yml= :: Weekly builds and cache updates
|
|
||||||
- =build-schemes.yml= :: Color scheme builds
|
|
||||||
|
|
||||||
** Build Cache
|
|
||||||
Builds are automatically cached using Atticd for faster rebuilds.
|
|
||||||
|
|
||||||
|
|
||||||
* Customization
|
|
||||||
|
|
||||||
** Theming
|
|
||||||
The configuration uses Stylix for theming. Define color schemes in
|
|
||||||
=config/schemes.nix= and can set them via the =config/stylix.nix= file.
|
|
||||||
|
|
||||||
** Adding New Services
|
|
||||||
1. Create service module in =modules/servers/=
|
|
||||||
2. Add service configuration
|
|
||||||
3. Enable service in host toggles
|
|
||||||
4. Add to homepage if needed
|
|
||||||
|
|
||||||
** Custom Scripts
|
|
||||||
Scripts are in =modules/scripts/= and toggle them per host.
|
|
||||||
|
|
||||||
* Troubleshooting
|
|
||||||
|
|
||||||
** Common Issues
|
|
||||||
|
|
||||||
*** Build Failures
|
|
||||||
- Check flake inputs are up to date: =nix flake update=
|
|
||||||
- Verify all required secrets are present
|
|
||||||
- Check host-specific configuration
|
|
||||||
|
|
||||||
*** Service Issues
|
|
||||||
- Check service status: =systemctl status <service>=
|
|
||||||
- View logs: =journalctl -u <service>=
|
|
||||||
- Verify firewall rules
|
|
||||||
|
|
||||||
*** Development Environment
|
|
||||||
- Rebuild development shell: =nix develop .#<language>=
|
|
||||||
- Check available packages: =nix search nixpkgs <package>=
|
|
||||||
|
|
||||||
** Getting Help
|
|
||||||
- Check NixOS documentation
|
|
||||||
- Review module documentation
|
|
||||||
- Check service-specific documentation
|
- Check service-specific documentation
|
||||||
|
|
||||||
* Maintenance
|
* Maintenance
|
||||||
|
|||||||
@@ -79,7 +79,6 @@ in
|
|||||||
"galaxy"
|
"galaxy"
|
||||||
"phone"
|
"phone"
|
||||||
"vps"
|
"vps"
|
||||||
"windows_vm"
|
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -116,5 +116,28 @@ in
|
|||||||
base0F = "#dc143c"; # crimson
|
base0F = "#dc143c"; # crimson
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
punk = mkScheme {
|
||||||
|
color = "red";
|
||||||
|
polarity = "light";
|
||||||
|
image = "${wallpapers}/punk.jpeg";
|
||||||
|
base16Scheme = {
|
||||||
|
base00 = "#f7f3ee";
|
||||||
|
base01 = "#efe6dc";
|
||||||
|
base02 = "#e1d3c6";
|
||||||
|
base03 = "#c8b2a0";
|
||||||
|
base04 = "#a18673";
|
||||||
|
base05 = "#6e5646";
|
||||||
|
base06 = "#3f2f28";
|
||||||
|
base07 = "#1a1512";
|
||||||
|
base08 = "#c7423a";
|
||||||
|
base09 = "#d28b61";
|
||||||
|
base0A = "#c9a24c";
|
||||||
|
base0B = "#7d8b6a";
|
||||||
|
base0C = "#5f8f8a";
|
||||||
|
base0D = "#4f6a86";
|
||||||
|
base0E = "#8a5b6a";
|
||||||
|
base0F = "#8b5a3c";
|
||||||
|
};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ let
|
|||||||
schemesFile = import ./schemes.nix {
|
schemesFile = import ./schemes.nix {
|
||||||
inherit pkgs inputs;
|
inherit pkgs inputs;
|
||||||
};
|
};
|
||||||
scheme = schemesFile.schemes.space;
|
scheme = schemesFile.schemes.punk;
|
||||||
cfg = config.my.stylix;
|
cfg = config.my.stylix;
|
||||||
gnomeEnabled = config.services.desktopManager.gnome.enable;
|
gnomeEnabled = config.services.desktopManager.gnome.enable;
|
||||||
in
|
in
|
||||||
|
|||||||
@@ -7,10 +7,10 @@
|
|||||||
|
|
||||||
## Repository Overview
|
## Repository Overview
|
||||||
- Architecture: Flake-based repo using `flake-parts` with inputs for pkgs (stable/unstable), stylix, home-manager, sops-nix, and service overlays. Common modules are composed through `parts/core.nix` and `parts/hosts.nix`.
|
- Architecture: Flake-based repo using `flake-parts` with inputs for pkgs (stable/unstable), stylix, home-manager, sops-nix, and service overlays. Common modules are composed through `parts/core.nix` and `parts/hosts.nix`.
|
||||||
- Module auto-import: `modules/modules.nix` auto-imports `.nix` files under `modules/apps`, `modules/dev`, `modules/scripts`, `modules/servers`, `modules/services`, `modules/shell`, and `modules/network`, excluding `librewolf.nix`. Factories live in `modules/factories/` (`mkserver`, `mkscript`), and shared options are in `modules/nix` and `modules/users`.
|
- Module auto-import: `modules/modules.nix` auto-imports `.nix` files under `modules/apps`, `modules/dev`, `modules/scripts`, `modules/servers`, `modules/services`, `modules/shell`, `modules/websites`, and `modules/network`, excluding `librewolf.nix`. Factories live in `modules/factories/` (`mkserver`, `mkscript`), and shared options are in `modules/nix` and `modules/users`.
|
||||||
- Hosts and toggles: Host definitions live in `hosts/<name>/configuration.nix` with host-specific toggles in `hosts/<name>/toggles.nix`. The `my` namespace carries toggles for apps/dev/scripts/services/shell, feature flags like `enableProxy` and `enableContainers`, and per-host `interfaces` and `ips` maps.
|
- Hosts and toggles: Host definitions live in `hosts/<name>/configuration.nix` with host-specific toggles in `hosts/<name>/toggles.nix`. The `my` namespace carries toggles for apps/dev/scripts/services/shell, feature flags like `enableProxy` and `enableContainers`, and per-host `interfaces` and `ips` maps.
|
||||||
- Main server and proxies: `my.mainServer` selects the host that should serve traffic by default (default `miniserver`; overridden to `server` in `hosts/server/toggles.nix`). Reverse proxies use helpers in `parts/core.nix` (`proxy`, `proxyReverse`, `proxyReverseFix`, `proxyReversePrivate`) and pick IPs from `my.ips` plus the hostName/ip set by `mkserver` options.
|
- Main server and proxies: `my.mainServer` selects the host that should serve traffic by default (default `vps`). Reverse proxies use helpers in `parts/core.nix` (`proxy`, `proxyReverse`, `proxyReverseFix`, `proxyReversePrivate`) and pick IPs from `my.ips` plus the hostName/ip set by `mkserver` options. Nginx defaults to `proxyReverse` for any server with `enableProxy = true` unless `useDefaultProxy = false` or the server is listed in the Fix/Private proxy lists.
|
||||||
- Secure hosts and secrets: `my.secureHost` gates SOPS secrets. Secure hosts load secrets from `secrets/*.yaml` and wireguard definitions; non-secure hosts (e.g., `hosts/emacs`) skip secret-dependent services. Default SOPS file is `secrets/secrets.yaml` via `config/base.nix`.
|
- Secure hosts and secrets: `my.secureHost` gates SOPS secrets. Secure hosts load secrets from `secrets/*.yaml` and wireguard definitions; non-secure hosts (e.g., `hosts/emacs`) skip secret-dependent services. Default SOPS file is `secrets/secrets.yaml` via `config/base.nix`. Proxy-only services that need private certificates must still define their cert secrets when `enableProxy = true`.
|
||||||
|
|
||||||
## Coding Conventions
|
## Coding Conventions
|
||||||
- No blank lines between code blocks; keep markdown examples tight.
|
- No blank lines between code blocks; keep markdown examples tight.
|
||||||
@@ -33,7 +33,8 @@ config.services = {
|
|||||||
- Factory: Shared option constructors in `modules/factories/` (use `mkserver` for server modules, `mkscript` for script units).
|
- Factory: Shared option constructors in `modules/factories/` (use `mkserver` for server modules, `mkscript` for script units).
|
||||||
- Options: Settings under the `my` namespace (e.g., `my.services.<service>`, `my.scripts.<script>`).
|
- Options: Settings under the `my` namespace (e.g., `my.services.<service>`, `my.scripts.<script>`).
|
||||||
- Toggles: Enablement maps in `hosts/<name>/toggles.nix` controlling categories (apps/dev/shell/scripts/services/servers/units) and features (`enableProxy`, `enableContainers`).
|
- Toggles: Enablement maps in `hosts/<name>/toggles.nix` controlling categories (apps/dev/shell/scripts/services/servers/units) and features (`enableProxy`, `enableContainers`).
|
||||||
- Servers: Reverse-proxied services under `modules/servers/`, normally created with `mkserver` options.
|
- Servers: Reverse-proxied services under `modules/servers/`, normally created with `mkserver` options (including `useDefaultProxy` to opt out of default proxyReverse).
|
||||||
|
- Websites: Static nginx vhosts under `modules/websites/` (portfolio/blog, mb-report), gated by `my.websites.*.enableProxy`.
|
||||||
- Scripts: Units defined via `mkscript` with `enable`, `install`, `service`, `users`, `timer`, and `package` fields.
|
- Scripts: Units defined via `mkscript` with `enable`, `install`, `service`, `users`, `timer`, and `package` fields.
|
||||||
- Playbooks: Workflow guides under `docs/playbooks/` for repeatable tasks.
|
- Playbooks: Workflow guides under `docs/playbooks/` for repeatable tasks.
|
||||||
- Reference map: Navigation index under `docs/reference/index.md` for paths and responsibilities.
|
- Reference map: Navigation index under `docs/reference/index.md` for paths and responsibilities.
|
||||||
@@ -45,8 +46,8 @@ config.services = {
|
|||||||
- VPS enrollment flow: The vps host generates its own key on first boot, then operators enroll the public key, re-encrypt secrets, and redeploy. Follow `docs/playbooks/enroll-vps.md`.
|
- VPS enrollment flow: The vps host generates its own key on first boot, then operators enroll the public key, re-encrypt secrets, and redeploy. Follow `docs/playbooks/enroll-vps.md`.
|
||||||
|
|
||||||
## Module Categories and Active Hosts
|
## Module Categories and Active Hosts
|
||||||
- Module categories: apps, dev, scripts, servers, services, shell, network, users, nix, patches. Factories sit in `modules/factories/` and are imported explicitly.
|
- Module categories: apps, dev, scripts, servers, services, shell, websites, network, users, nix. Factories sit in `modules/factories/` and are imported explicitly; patch artifacts live at the repo root in `patches/`.
|
||||||
- Active hosts: `workstation`, `server`, `miniserver`, `galaxy`, `emacs`. Host roles and secure status are defined in `hosts/<name>/configuration.nix` and toggles in `hosts/<name>/toggles.nix`.
|
- Active hosts: `workstation`, `server`, `miniserver`, `galaxy`, `emacs`, `vps`. Host roles and secure status are defined in `hosts/<name>/configuration.nix` and toggles in `hosts/<name>/toggles.nix`.
|
||||||
|
|
||||||
## Precedence and Conflict Resolution
|
## Precedence and Conflict Resolution
|
||||||
- Precedence: This constitution is authoritative for AI. Human docs must be updated to match. If conflicts are found, align human docs to the constitution and log the resolution in `specs/001-ai-docs/research.md`.
|
- Precedence: This constitution is authoritative for AI. Human docs must be updated to match. If conflicts are found, align human docs to the constitution and log the resolution in `specs/001-ai-docs/research.md`.
|
||||||
|
|||||||
25
docs/playbooks/add-wireguard-peer.md
Normal file
25
docs/playbooks/add-wireguard-peer.md
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
# Playbook: Add WireGuard Peer (Friend or Guest)
|
||||||
|
|
||||||
|
## When to use
|
||||||
|
- Adding a new WireGuard peer in the friends (10.8.0.0/24) or guests (10.9.0.0/24) subnet.
|
||||||
|
- Updating firewall rules to allow access to specific ports for that peer.
|
||||||
|
|
||||||
|
## Inputs
|
||||||
|
- Peer name (e.g., `friend5`, `guest2`)
|
||||||
|
- Peer public key (WireGuard)
|
||||||
|
- Peer IP address (e.g., `10.8.0.6` or `10.9.0.3`)
|
||||||
|
- Access scope (ports/services the peer should reach)
|
||||||
|
|
||||||
|
## Steps
|
||||||
|
1. Add the peer IP to `my.ips` in `modules/modules.nix`.
|
||||||
|
2. Add the peer to the VPS WireGuard peers list in `modules/services/wireguard.nix`.
|
||||||
|
3. If the peer is a guest/friend, ensure `allowedIPs` includes the relevant subnets in `hosts/server/configuration.nix`.
|
||||||
|
4. Add or adjust VPS firewall rules in `hosts/vps/configuration.nix` (`networking.firewall.extraForwardRules`) to allow the requested ports.
|
||||||
|
5. Rebuild both hosts:
|
||||||
|
- `nixos-rebuild switch --flake .#vps`
|
||||||
|
- `nixos-rebuild switch --flake .#server`
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
- On VPS: `sudo wg show`
|
||||||
|
- On VPS: `sudo nft list ruleset | rg -n "<peer ip>|<port>"`
|
||||||
|
- From peer: confirm access to allowed endpoints (HTTP/TCP/ICMP as defined).
|
||||||
@@ -7,24 +7,26 @@
|
|||||||
- servers → `modules/servers/` (reverse-proxied services built via `mkserver`)
|
- servers → `modules/servers/` (reverse-proxied services built via `mkserver`)
|
||||||
- services → `modules/services/` (supporting services like syncthing, wireguard)
|
- services → `modules/services/` (supporting services like syncthing, wireguard)
|
||||||
- shell → `modules/shell/` (shell customizations and CLI tooling)
|
- shell → `modules/shell/` (shell customizations and CLI tooling)
|
||||||
|
- websites → `modules/websites/` (static nginx vhosts for portfolio/blog and reports)
|
||||||
- network → `modules/network/` (networking rules, firewall helpers)
|
- network → `modules/network/` (networking rules, firewall helpers)
|
||||||
- users → `modules/users/` (user-related options)
|
- users → `modules/users/` (user-related options)
|
||||||
- nix → `modules/nix/` (Nix configuration and helpers)
|
- nix → `modules/nix/` (Nix configuration and helpers)
|
||||||
- patches → `patches/` (patch artifacts referenced by modules)
|
|
||||||
- factories → `modules/factories/` (`mkserver.nix`, `mkscript.nix` shared helpers)
|
- factories → `modules/factories/` (`mkserver.nix`, `mkscript.nix` shared helpers)
|
||||||
|
## Root Directories
|
||||||
|
- patches → `patches/` (patch artifacts referenced by modules)
|
||||||
|
|
||||||
## Auto-Import Rules
|
## Auto-Import Rules
|
||||||
- Source: `modules/modules.nix` uses `inputs.self.lib.autoImport` to load `.nix` files from module directories.
|
- Source: `modules/modules.nix` uses `inputs.self.lib.autoImport` to load `.nix` files from module directories.
|
||||||
- Filter: Excludes `librewolf.nix`; all other `.nix` files in target dirs are loaded automatically.
|
- Filter: Excludes `librewolf.nix`; all other `.nix` files in target dirs are loaded automatically.
|
||||||
- Implication: Place new modules in the correct category directory with a `.nix` filename; no manual import wiring required unless adding a new factory.
|
- Implication: Place new modules in the correct category directory with a `.nix` filename; no manual import wiring required unless adding a new factory. Patch artifacts under `patches/` are not auto-imported.
|
||||||
|
|
||||||
## Hosts and Roles
|
## Hosts and Roles
|
||||||
- Configs: `hosts/<name>/configuration.nix` with toggles in `hosts/<name>/toggles.nix`.
|
- Configs: `hosts/<name>/configuration.nix` with toggles in `hosts/<name>/toggles.nix`.
|
||||||
- Active hosts: `workstation`, `server`, `miniserver`, `galaxy`, `emacs`, `vps`.
|
- Active hosts: `workstation`, `server`, `miniserver`, `galaxy`, `emacs`, `vps`.
|
||||||
- Roles:
|
- Roles:
|
||||||
- workstation: developer desktop; provides build power for distributed builds.
|
- workstation: developer desktop; provides build power for distributed builds.
|
||||||
- server: primary services host (overrides `my.mainServer = "server"` and enables proxies/containers).
|
- server: primary services host; runs most services and WireGuard targets.
|
||||||
- miniserver: small-footprint server; default `mainServer` in shared options.
|
- miniserver: small-footprint server.
|
||||||
- galaxy: small server variant using nixpkgs-small.
|
- galaxy: small server variant using nixpkgs-small.
|
||||||
- emacs: VM profile, `my.secureHost = false` for secret-free usage.
|
- emacs: VM profile, `my.secureHost = false` for secret-free usage.
|
||||||
- vps: Linode VPS image target, secure host with enrollment-based secrets.
|
- vps: Linode VPS image target, secure host with enrollment-based secrets.
|
||||||
@@ -32,8 +34,9 @@
|
|||||||
|
|
||||||
## Proxy, Firewall, and Networking
|
## Proxy, Firewall, and Networking
|
||||||
- Proxy enablement: `my.enableProxy` toggles Nginx reverse proxy; assertions require at least one `my.servers.*.enableProxy` when enabled.
|
- Proxy enablement: `my.enableProxy` toggles Nginx reverse proxy; assertions require at least one `my.servers.*.enableProxy` when enabled.
|
||||||
- Proxy helpers: use `parts/core.nix` helpers (`proxy`, `proxyReverse`, `proxyReverseFix` for header preservation, `proxyReversePrivate` for mutual TLS). `mkserver` supplies `host`, `ip`, `url`, and `enableProxy` defaults per service.
|
- Proxy helpers: use `parts/core.nix` helpers (`proxy`, `proxyReverse`, `proxyReverseFix` for header preservation, `proxyReversePrivate` for mutual TLS). `mkserver` supplies `host`, `ip`, `url`, `enableProxy`, and `useDefaultProxy`.
|
||||||
- Main server selection: `my.mainServer` chooses where services live by default; `mkserver` sets `isLocal` based on this and picks IPs from `my.ips`.
|
- Default proxying: any server with `enableProxy = true` gets a `proxyReverse` vhost unless `useDefaultProxy = false` or it is listed in `proxyReverseFix` / `proxyReversePrivate`.
|
||||||
|
- Main server selection: `my.mainServer` chooses where services live by default (default `vps`); `mkserver` sets `isLocal` based on this and picks IPs from `my.ips`.
|
||||||
- Firewall generation: `inputs.self.lib.generateFirewallPorts` combines static ports, additional ports, and service ports from `my.servers` (excluding native firewall services). Use `my.network.firewall` settings and `getServicesWithNativeFirewall` to derive open ports.
|
- Firewall generation: `inputs.self.lib.generateFirewallPorts` combines static ports, additional ports, and service ports from `my.servers` (excluding native firewall services). Use `my.network.firewall` settings and `getServicesWithNativeFirewall` to derive open ports.
|
||||||
|
|
||||||
## Secrets Map
|
## Secrets Map
|
||||||
@@ -46,7 +49,7 @@
|
|||||||
- `secrets/wireguard.yaml` → WireGuard peers and private keys.
|
- `secrets/wireguard.yaml` → WireGuard peers and private keys.
|
||||||
- `secrets/secrets.yaml` → default SOPS file (general secrets, fallback when unspecified).
|
- `secrets/secrets.yaml` → default SOPS file (general secrets, fallback when unspecified).
|
||||||
- `secrets/ssh/` → host SSH keys and related artifacts.
|
- `secrets/ssh/` → host SSH keys and related artifacts.
|
||||||
- secureHost: Only hosts with `my.secureHost = true` consume SOPS entries and WireGuard interfaces. Keep secret references behind `lib.mkIf config.my.secureHost`.
|
- secureHost: Only hosts with `my.secureHost = true` consume SOPS entries and WireGuard interfaces. Keep secret references behind `lib.mkIf config.my.secureHost`; proxy-only services that use private certs must still declare their cert secrets when `enableProxy = true`.
|
||||||
|
|
||||||
## Stylix and Theming
|
## Stylix and Theming
|
||||||
- Stylix module: `config/stylix.nix` and stylix inputs in `flake.nix` apply theming. Host toggle `my.stylix.enable` controls activation (see host toggles).
|
- Stylix module: `config/stylix.nix` and stylix inputs in `flake.nix` apply theming. Host toggle `my.stylix.enable` controls activation (see host toggles).
|
||||||
@@ -59,7 +62,7 @@
|
|||||||
- MCP server reference: `docs/reference/mcp-server.md` (tool catalog, `nixos-mcp` wrapper, invocation, sync-docs)
|
- MCP server reference: `docs/reference/mcp-server.md` (tool catalog, `nixos-mcp` wrapper, invocation, sync-docs)
|
||||||
|
|
||||||
## Quick Audit Checklist
|
## Quick Audit Checklist
|
||||||
- Module coverage: All categories (apps, dev, scripts, servers, services, shell, network, users, nix, patches) have corresponding entries and auto-import rules.
|
- Module coverage: All categories (apps, dev, scripts, servers, services, shell, websites, network, users, nix) have corresponding entries and auto-import rules; `patches/` is documented as a root directory.
|
||||||
- Host coverage: Active hosts listed with roles and secureHost status; `mainServer` noted.
|
- Host coverage: Active hosts listed with roles and secureHost status; `mainServer` noted.
|
||||||
- Proxy rules: `enableProxy` usage, proxy helper selection, and `my.ips` mappings documented.
|
- Proxy rules: `enableProxy` usage, proxy helper selection, and `my.ips` mappings documented.
|
||||||
- Secrets map: Every secrets file and secureHost gating captured; new secret types aligned to file purposes.
|
- Secrets map: Every secrets file and secureHost gating captured; new secret types aligned to file purposes.
|
||||||
|
|||||||
@@ -43,6 +43,7 @@
|
|||||||
;;neotree ; a project drawer, like NERDTree for vim
|
;;neotree ; a project drawer, like NERDTree for vim
|
||||||
ophints ; highlight the region an operation acts on
|
ophints ; highlight the region an operation acts on
|
||||||
(popup +defaults) ; tame sudden yet inevitable temporary windows
|
(popup +defaults) ; tame sudden yet inevitable temporary windows
|
||||||
|
(smooth-scroll +interpolate) ; So smooth you won't believe it's not butter
|
||||||
;;tabs ; a tab bar for Emacs
|
;;tabs ; a tab bar for Emacs
|
||||||
(treemacs +lsp) ; a project drawer, like neotree but cooler
|
(treemacs +lsp) ; a project drawer, like neotree but cooler
|
||||||
;;unicode ; extended unicode support for various languages
|
;;unicode ; extended unicode support for various languages
|
||||||
@@ -64,6 +65,7 @@
|
|||||||
;;parinfer ; turn lisp into python, sort of
|
;;parinfer ; turn lisp into python, sort of
|
||||||
rotate-text ; cycle region at point between text candidates
|
rotate-text ; cycle region at point between text candidates
|
||||||
snippets ; my elves. They type so I don't have to
|
snippets ; my elves. They type so I don't have to
|
||||||
|
(whitespace +guess +trim) ; a butler for your whitespace
|
||||||
;;word-wrap ; soft wrapping with language-aware indent
|
;;word-wrap ; soft wrapping with language-aware indent
|
||||||
|
|
||||||
:emacs
|
:emacs
|
||||||
@@ -71,6 +73,7 @@
|
|||||||
electric ; smarter, keyword-based electric-indent
|
electric ; smarter, keyword-based electric-indent
|
||||||
eww ; the internet is gross
|
eww ; the internet is gross
|
||||||
ibuffer ; interactive buffer management
|
ibuffer ; interactive buffer management
|
||||||
|
tramp ; remote files at your arthritic fingertips
|
||||||
(undo +tree) ; persistent, smarter undo for your inevitable mistakes
|
(undo +tree) ; persistent, smarter undo for your inevitable mistakes
|
||||||
vc ; version-control and Emacs, sitting in a tree
|
vc ; version-control and Emacs, sitting in a tree
|
||||||
|
|
||||||
@@ -96,21 +99,24 @@
|
|||||||
;;ein ; tame Jupyter notebooks with emacs
|
;;ein ; tame Jupyter notebooks with emacs
|
||||||
(eval +overlay) ; run code, run (also, repls)
|
(eval +overlay) ; run code, run (also, repls)
|
||||||
(lookup +dictionary + offline) ; navigate your code and its documentation
|
(lookup +dictionary + offline) ; navigate your code and its documentation
|
||||||
|
llm ; when I said you needed friends, I didn't mean...
|
||||||
(lsp +peek) ; M-x vscode
|
(lsp +peek) ; M-x vscode
|
||||||
magit ; a git porcelain for Emacs
|
magit ; a git porcelain for Emacs
|
||||||
;;make ; run make tasks from Emacs
|
;;make ; run make tasks from Emacs
|
||||||
;;pass ; password manager for nerds
|
;;pass ; password manager for nerds
|
||||||
;;pdf ; pdf enhancements
|
;;pdf ; pdf enhancements
|
||||||
;;prodigy ; FIXME managing external services & code builders
|
;;prodigy ; FIXME managing external services & code builders
|
||||||
;;terraform ; infrastructure as code
|
(terraform +lsp) ; infrastructure as code
|
||||||
tmux ; an API for interacting with tmux
|
tmux ; an API for interacting with tmux
|
||||||
;; tree-sitter ; syntax and parsing, sitting in a tree...
|
;; tree-sitter ; syntax and parsing, sitting in a tree...
|
||||||
upload ; map local to remote projects via ssh/ftp
|
upload ; map local to remote projects via ssh/ftp
|
||||||
|
|
||||||
:os
|
:os
|
||||||
;;(:if (featurep :system 'macos) macos) ; improve compatibility with macOS tty ; improve the terminal Emacs experience
|
;;(:if (featurep :system 'macos) macos) ; improve compatibility with macOS tty ; improve the terminal Emacs experience
|
||||||
|
tty ; improve the terminal Emacs experience
|
||||||
|
|
||||||
:lang
|
:lang
|
||||||
|
;;ada ; In strong typing we (blindly) trust
|
||||||
;;agda ; types of types of types of types...
|
;;agda ; types of types of types of types...
|
||||||
;;beancount ; mind the GAAP
|
;;beancount ; mind the GAAP
|
||||||
(cc +lsp) ; C > C++ == 1
|
(cc +lsp) ; C > C++ == 1
|
||||||
@@ -139,6 +145,7 @@
|
|||||||
;;hy ; readability of scheme w/ speed of python
|
;;hy ; readability of scheme w/ speed of python
|
||||||
;;idris ; a language you can depend on
|
;;idris ; a language you can depend on
|
||||||
(json +lsp) ; At least it ain't XML
|
(json +lsp) ; At least it ain't XML
|
||||||
|
;;janet ; Fun fact: Janet is me!
|
||||||
;;(java +lsp) ; the poster child for carpal tunnel syndrome
|
;;(java +lsp) ; the poster child for carpal tunnel syndrome
|
||||||
(javascript +lsp) ; all(hope(abandon(ye(who(enter(here))))))
|
(javascript +lsp) ; all(hope(abandon(ye(who(enter(here))))))
|
||||||
(julia +lsp) ; a better, faster MATLAB
|
(julia +lsp) ; a better, faster MATLAB
|
||||||
|
|||||||
@@ -49,15 +49,9 @@
|
|||||||
;; ...Or *all* packages (NOT RECOMMENDED; will likely break things)
|
;; ...Or *all* packages (NOT RECOMMENDED; will likely break things)
|
||||||
;(unpin! t)
|
;(unpin! t)
|
||||||
|
|
||||||
;; (package! nixos-options) ;; enable when migrating to nixos
|
|
||||||
;; (package! quick-preview) ;; preview files with sushi
|
|
||||||
|
|
||||||
|
|
||||||
;; (package! codeium :recipe (:host github :repo "Exafunction/codeium.el"))
|
|
||||||
(package! config-general-mode)
|
|
||||||
(package! dired-open)
|
(package! dired-open)
|
||||||
(package! dired-subtree)
|
(package! dired-subtree)
|
||||||
;; (package! doom-modeline-now-playing)
|
(package! expand-region)
|
||||||
(package! ini-mode)
|
(package! ini-mode)
|
||||||
(package! insert-esv) ;; bible passages
|
(package! insert-esv) ;; bible passages
|
||||||
(package! olivetti) ;; writing mode centering text, looks like word
|
(package! olivetti) ;; writing mode centering text, looks like word
|
||||||
@@ -68,11 +62,14 @@
|
|||||||
(package! peep-dired) ;; kind of cool but never could make it work
|
(package! peep-dired) ;; kind of cool but never could make it work
|
||||||
(package! php-cs-fixer)
|
(package! php-cs-fixer)
|
||||||
(package! systemd)
|
(package! systemd)
|
||||||
|
;; :recipe (:host github :repo "tecosaur/ox-chameleon"))
|
||||||
;; (package! 2048-game)
|
;; (package! 2048-game)
|
||||||
;; (package! academic-phrases)
|
;; (package! academic-phrases)
|
||||||
;; (package! caddyfile-mode)
|
;; (package! caddyfile-mode)
|
||||||
;; (package! clippy)
|
;; (package! clippy)
|
||||||
|
;; (package! codeium :recipe (:host github :repo "Exafunction/codeium.el"))
|
||||||
;; (package! crontab-mode) ;; crontab colors
|
;; (package! crontab-mode) ;; crontab colors
|
||||||
|
;; (package! doom-modeline-now-playing)
|
||||||
;; (package! evil-tutor) ;; vim tutorial
|
;; (package! evil-tutor) ;; vim tutorial
|
||||||
;; (package! ewal) ;; theme colors based on pywal
|
;; (package! ewal) ;; theme colors based on pywal
|
||||||
;; (package! ewal-doom-themes)
|
;; (package! ewal-doom-themes)
|
||||||
@@ -81,16 +78,14 @@
|
|||||||
;; (package! flycheck-aspell)
|
;; (package! flycheck-aspell)
|
||||||
;; (package! ivy-posframe)
|
;; (package! ivy-posframe)
|
||||||
;; (package! mw-thesaurus)
|
;; (package! mw-thesaurus)
|
||||||
|
;; (package! nixos-options) ;; enable when migrating to nixos
|
||||||
;; (package! org-appear) ;; couldn't get it to work
|
;; (package! org-appear) ;; couldn't get it to work
|
||||||
;; (package! org-recur) ;; works but I want to keep org vanilla
|
;; (package! org-recur) ;; works but I want to keep org vanilla
|
||||||
;; (package! ox-chameleon
|
;; (package! ox-chameleon
|
||||||
;; :recipe (:host github :repo "tecosaur/ox-chameleon"))
|
;; (package! quick-preview) ;; preview files with sushi
|
||||||
;; (package! renpy)
|
;; (package! renpy)
|
||||||
;; (package! resize-window)
|
;; (package! resize-window)
|
||||||
;; (package! tldr)
|
;; (package! tldr)
|
||||||
;; (package! typit) ;; type speed test
|
;; (package! typit) ;; type speed test
|
||||||
;; (package! vimgolf) ;; vim puzzles
|
;; (package! vimgolf) ;; vim puzzles
|
||||||
;; (package! wc-mode) ;; displays character count of buffer
|
;; (package! wc-mode) ;; displays character count of buffer
|
||||||
|
|
||||||
(package! expand-region)
|
|
||||||
(package! gptel :recipe (:nonrecursive t))
|
|
||||||
|
|||||||
62
flake.lock
generated
62
flake.lock
generated
@@ -422,11 +422,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1769580047,
|
"lastModified": 1770260404,
|
||||||
"narHash": "sha256-tNqCP/+2+peAXXQ2V8RwsBkenlfWMERb+Uy6xmevyhM=",
|
"narHash": "sha256-3iVX1+7YUIt23hBx1WZsUllhbmP2EnXrV8tCRbLxHc8=",
|
||||||
"owner": "nix-community",
|
"owner": "nix-community",
|
||||||
"repo": "home-manager",
|
"repo": "home-manager",
|
||||||
"rev": "366d78c2856de6ab3411c15c1cb4fb4c2bf5c826",
|
"rev": "0d782ee42c86b196acff08acfbf41bb7d13eed5b",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -513,11 +513,11 @@
|
|||||||
"xdph": "xdph"
|
"xdph": "xdph"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1769965155,
|
"lastModified": 1770330959,
|
||||||
"narHash": "sha256-KAgGtueNnwLtekIh9tnmLAUDFbRxSff0OZwQwSi05Nk=",
|
"narHash": "sha256-OPmJ6dBL615GGX7ENJXtJm4zeMv5uXDjmO8WB1MI5wM=",
|
||||||
"owner": "hyprwm",
|
"owner": "hyprwm",
|
||||||
"repo": "Hyprland",
|
"repo": "Hyprland",
|
||||||
"rev": "a0ec2e4daf8e508761f6bc53fc163fbb92ac7aa1",
|
"rev": "562171ab668e7ee98a9d2bbb62a9477ad2b1e24e",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -806,11 +806,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1769914377,
|
"lastModified": 1770345361,
|
||||||
"narHash": "sha256-8wH3ZYNs36V0A3f/ikraqdoVE++BfnXg9Ql8nAuUkHw=",
|
"narHash": "sha256-/kldWxogKCw1ykliO6lLwLE4aqUQ+yZOS9fHiFdI7u0=",
|
||||||
"owner": "fufexan",
|
"owner": "fufexan",
|
||||||
"repo": "nix-gaming",
|
"repo": "nix-gaming",
|
||||||
"rev": "f7d17740ed90663b11ae907d33b3fed9fc9e15a9",
|
"rev": "2b6e7914c9ba6ad7effd53fab40d7acf34873069",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -885,11 +885,11 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs-small": {
|
"nixpkgs-small": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1769983561,
|
"lastModified": 1770235692,
|
||||||
"narHash": "sha256-Hv7yml1x0gU14H1zxGATCXb+ueeBH7DFBUTIMkLWD4A=",
|
"narHash": "sha256-VvqTQ2RYZE4PUWiefJKAPKW3H4fahCbp3L8yv4c7B8s=",
|
||||||
"owner": "nixos",
|
"owner": "nixos",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "8dcf33716a525a7a0b6815cd8d0f4fa9b13abb1b",
|
"rev": "39dfe6111bc5b19a710bcf409a9b179688f381eb",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -901,11 +901,11 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs-unstable": {
|
"nixpkgs-unstable": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1769789167,
|
"lastModified": 1770197578,
|
||||||
"narHash": "sha256-kKB3bqYJU5nzYeIROI82Ef9VtTbu4uA3YydSk/Bioa8=",
|
"narHash": "sha256-AYqlWrX09+HvGs8zM6ebZ1pwUqjkfpnv8mewYwAo+iM=",
|
||||||
"owner": "nixos",
|
"owner": "nixos",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "62c8382960464ceb98ea593cb8321a2cf8f9e3e5",
|
"rev": "00c21e4c93d963c50d4c0c89bfa84ed6e0694df2",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -917,11 +917,11 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs_2": {
|
"nixpkgs_2": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1769900590,
|
"lastModified": 1770136044,
|
||||||
"narHash": "sha256-I7Lmgj3owOTBGuauy9FL6qdpeK2umDoe07lM4V+PnyA=",
|
"narHash": "sha256-tlFqNG/uzz2++aAmn4v8J0vAkV3z7XngeIIB3rM3650=",
|
||||||
"owner": "nixos",
|
"owner": "nixos",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "41e216c0ca66c83b12ab7a98cc326b5db01db646",
|
"rev": "e576e3c9cf9bad747afcddd9e34f51d18c855b4e",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -960,11 +960,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1770024533,
|
"lastModified": 1770368476,
|
||||||
"narHash": "sha256-EXQWqlbhkkune23d6xq/0bz3iyJzkVklvDsSlYE3n3Y=",
|
"narHash": "sha256-myWSpD+v5TmQ47grn+gbLou1dNy5hCXvfvc6VGF72kk=",
|
||||||
"owner": "nix-community",
|
"owner": "nix-community",
|
||||||
"repo": "nur",
|
"repo": "nur",
|
||||||
"rev": "cdb7c463555d7c6de5ba7251f9b4940249b651e8",
|
"rev": "2959831b0338e196f2d864fb5e6bb309fa1c99c1",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -1088,11 +1088,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1769921679,
|
"lastModified": 1770145881,
|
||||||
"narHash": "sha256-twBMKGQvaztZQxFxbZnkg7y/50BW9yjtCBWwdjtOZew=",
|
"narHash": "sha256-ktjWTq+D5MTXQcL9N6cDZXUf9kX8JBLLBLT0ZyOTSYY=",
|
||||||
"owner": "Mic92",
|
"owner": "Mic92",
|
||||||
"repo": "sops-nix",
|
"repo": "sops-nix",
|
||||||
"rev": "1e89149dcfc229e7e2ae24a8030f124a31e4f24f",
|
"rev": "17eea6f3816ba6568b8c81db8a4e6ca438b30b7c",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -1122,11 +1122,11 @@
|
|||||||
"tinted-zed": "tinted-zed"
|
"tinted-zed": "tinted-zed"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1769885983,
|
"lastModified": 1770308890,
|
||||||
"narHash": "sha256-jLS7410B58f+3WfZ4PQ28aaaTONnmxlfAbDPdNuciLc=",
|
"narHash": "sha256-7bx8Bn9B2g/loBaz+uLwdKI2rUW+RhDPyP/MqAgvrxU=",
|
||||||
"owner": "danth",
|
"owner": "danth",
|
||||||
"repo": "stylix",
|
"repo": "stylix",
|
||||||
"rev": "fe06391a1e1905fc7e6c13443ea439a89695ca69",
|
"rev": "7e7fa955abac04a8e118b1cedf930a8fd41c34a6",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -1331,11 +1331,11 @@
|
|||||||
"wallpapers": {
|
"wallpapers": {
|
||||||
"flake": false,
|
"flake": false,
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1759463031,
|
"lastModified": 1770517108,
|
||||||
"narHash": "sha256-9goyp+g9N4+9PA9V2QerTsxlVy9MA4LXufJaruj2MMs=",
|
"narHash": "sha256-QYvx6j7r1ItxaaKN/wI0nGB6r5aWG46zfcoXCTOgK18=",
|
||||||
"ref": "refs/heads/main",
|
"ref": "refs/heads/main",
|
||||||
"rev": "0212af5b70347f0721cfe88c25e1efb77b645a2d",
|
"rev": "dcb86c4c592b8cf838a0fd6d139254619cbbd869",
|
||||||
"revCount": 2,
|
"revCount": 3,
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://git.lebubu.org/jawz/wallpapers.git"
|
"url": "https://git.lebubu.org/jawz/wallpapers.git"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ in
|
|||||||
hostName = "server";
|
hostName = "server";
|
||||||
firewall = {
|
firewall = {
|
||||||
allowedUDPPorts = config.networking.firewall.allowedTCPPorts;
|
allowedUDPPorts = config.networking.firewall.allowedTCPPorts;
|
||||||
interfaces.wg0.allowedTCPPorts = [ 8081 ];
|
interfaces.wg0.allowedTCPPorts = [ config.my.servers.nextcloud.port ];
|
||||||
};
|
};
|
||||||
wireguard.interfaces.wg0 = lib.mkIf config.my.secureHost {
|
wireguard.interfaces.wg0 = lib.mkIf config.my.secureHost {
|
||||||
ips = [ "${config.my.ips.wg-server}/32" ];
|
ips = [ "${config.my.ips.wg-server}/32" ];
|
||||||
@@ -78,7 +78,9 @@ in
|
|||||||
endpoint = "${config.my.ips.vps}:51820";
|
endpoint = "${config.my.ips.vps}:51820";
|
||||||
allowedIPs = [
|
allowedIPs = [
|
||||||
"${config.my.ips.wg-vps}/32"
|
"${config.my.ips.wg-vps}/32"
|
||||||
"${config.my.ips.wg-friends}/24" # all friends
|
config.my.subnets.wg-homelab
|
||||||
|
config.my.subnets.wg-friends
|
||||||
|
config.my.subnets.wg-guests
|
||||||
];
|
];
|
||||||
persistentKeepalive = 25;
|
persistentKeepalive = 25;
|
||||||
}
|
}
|
||||||
@@ -116,8 +118,11 @@ in
|
|||||||
sshKeyFile = config.sops.secrets."private_keys/lidarr-mb-gap".path;
|
sshKeyFile = config.sops.secrets."private_keys/lidarr-mb-gap".path;
|
||||||
sshKnownHosts = {
|
sshKnownHosts = {
|
||||||
vps = {
|
vps = {
|
||||||
hostNames = [ config.my.ips.vps ];
|
hostNames = [
|
||||||
publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMvtTURGBtAFXxxfzMJVoNJrtWLykOloJ5XYjxGh1OUx";
|
config.my.ips.vps
|
||||||
|
"[${config.my.ips.vps}]:3456"
|
||||||
|
];
|
||||||
|
publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPp0wAuZXk96OyA/+2YpQalokS9lZdacjJqY9zN8IScP";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ let
|
|||||||
mkEnabledIp = inputs.self.lib.mkEnabledIp config.my.ips.wg-server;
|
mkEnabledIp = inputs.self.lib.mkEnabledIp config.my.ips.wg-server;
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
mainServer = "server";
|
|
||||||
emacs = {
|
emacs = {
|
||||||
enable = true;
|
enable = true;
|
||||||
users = "jawz";
|
users = "jawz";
|
||||||
@@ -68,7 +67,6 @@ in
|
|||||||
"maloja"
|
"maloja"
|
||||||
"mealie"
|
"mealie"
|
||||||
"metube"
|
"metube"
|
||||||
"microbin"
|
|
||||||
"multi-scrobbler"
|
"multi-scrobbler"
|
||||||
"paperless"
|
"paperless"
|
||||||
"plex"
|
"plex"
|
||||||
@@ -78,15 +76,14 @@ in
|
|||||||
"radarr"
|
"radarr"
|
||||||
"sabnzbd"
|
"sabnzbd"
|
||||||
"sonarr"
|
"sonarr"
|
||||||
"yamtrack"
|
|
||||||
"stash"
|
"stash"
|
||||||
"synapse"
|
"synapse"
|
||||||
"syncplay"
|
"syncplay"
|
||||||
"unpackerr"
|
"unpackerr"
|
||||||
|
"yamtrack"
|
||||||
]
|
]
|
||||||
// enableList mkEnabledIp [
|
// enableList mkEnabledIp [
|
||||||
"audiobookshelf"
|
"audiobookshelf"
|
||||||
"isso"
|
|
||||||
"keycloak"
|
"keycloak"
|
||||||
"linkwarden"
|
"linkwarden"
|
||||||
"oauth2-proxy"
|
"oauth2-proxy"
|
||||||
|
|||||||
@@ -1,36 +1,183 @@
|
|||||||
{
|
{
|
||||||
|
config,
|
||||||
lib,
|
lib,
|
||||||
|
pkgs,
|
||||||
inputs,
|
inputs,
|
||||||
...
|
...
|
||||||
}:
|
}:
|
||||||
|
let
|
||||||
|
externalInterface = config.my.interfaces.${config.networking.hostName};
|
||||||
|
wgInterface = "wg0";
|
||||||
|
ips = {
|
||||||
|
homeServer = config.my.ips.wg-server;
|
||||||
|
wgFriend1 = config.my.ips.wg-friend1;
|
||||||
|
wgGuest1 = config.my.ips.wg-guest1;
|
||||||
|
wgGuest2 = config.my.ips.wg-guest2;
|
||||||
|
};
|
||||||
|
subnets = {
|
||||||
|
wgFriends = config.my.subnets.wg-friends;
|
||||||
|
wgGuests = config.my.subnets.wg-guests;
|
||||||
|
wgHomelab = config.my.subnets.wg-homelab;
|
||||||
|
};
|
||||||
|
ports = {
|
||||||
|
giteaSsh = 22;
|
||||||
|
ssh = 3456;
|
||||||
|
web = [
|
||||||
|
80
|
||||||
|
443
|
||||||
|
];
|
||||||
|
wg = 51820;
|
||||||
|
syncthing = 22000;
|
||||||
|
synapseFederation = 8448;
|
||||||
|
};
|
||||||
|
portsStr = {
|
||||||
|
giteaSsh = toString ports.giteaSsh;
|
||||||
|
syncthing = toString ports.syncthing;
|
||||||
|
synapseFederation = toString ports.synapseFederation;
|
||||||
|
synapseClient = toString config.my.servers.synapse.port;
|
||||||
|
syncplay = toString config.my.servers.syncplay.port;
|
||||||
|
stash = toString config.my.servers.stash.port;
|
||||||
|
jellyfin = toString config.my.servers.jellyfin.port;
|
||||||
|
audiobookshelf = toString config.my.servers.audiobookshelf.port;
|
||||||
|
kavita = toString config.my.servers.kavita.port;
|
||||||
|
};
|
||||||
|
in
|
||||||
{
|
{
|
||||||
imports = [
|
imports = [
|
||||||
./hardware-configuration.nix
|
./hardware-configuration.nix
|
||||||
|
./nginx-nextcloud.nix
|
||||||
../../config/base.nix
|
../../config/base.nix
|
||||||
];
|
];
|
||||||
my = {
|
my =
|
||||||
secureHost = true;
|
import ./toggles.nix {
|
||||||
users.nixremote = {
|
inherit config inputs lib;
|
||||||
enable = true;
|
}
|
||||||
authorizedKeys = inputs.self.lib.getSshKeys [
|
// {
|
||||||
"nixworkstation"
|
secureHost = true;
|
||||||
"nixserver"
|
users.nixremote = {
|
||||||
"nixminiserver"
|
enable = true;
|
||||||
];
|
authorizedKeys = inputs.self.lib.getSshKeys [
|
||||||
|
"nixworkstation"
|
||||||
|
"nixserver"
|
||||||
|
"nixminiserver"
|
||||||
|
];
|
||||||
|
};
|
||||||
};
|
};
|
||||||
services.network.enable = true;
|
|
||||||
interfaces = lib.mkMerge [
|
|
||||||
{
|
|
||||||
vps = "eth0";
|
|
||||||
}
|
|
||||||
];
|
|
||||||
};
|
|
||||||
image.modules.linode = { };
|
|
||||||
networking.hostName = "vps";
|
|
||||||
sops.age = {
|
sops.age = {
|
||||||
generateKey = true;
|
generateKey = true;
|
||||||
keyFile = "/var/lib/sops-nix/key.txt";
|
keyFile = "/var/lib/sops-nix/key.txt";
|
||||||
sshKeyPaths = [ "/etc/ssh/ssh_host_ed25519_key" ];
|
sshKeyPaths = [ "/etc/ssh/ssh_host_ed25519_key" ];
|
||||||
};
|
};
|
||||||
|
image.modules.linode = { };
|
||||||
environment.systemPackages = [ ];
|
environment.systemPackages = [ ];
|
||||||
|
networking = {
|
||||||
|
hostName = "vps";
|
||||||
|
nat = {
|
||||||
|
inherit externalInterface;
|
||||||
|
enable = true;
|
||||||
|
internalInterfaces = [ "wg0" ];
|
||||||
|
forwardPorts = [
|
||||||
|
{
|
||||||
|
sourcePort = ports.giteaSsh;
|
||||||
|
proto = "tcp";
|
||||||
|
destination = "${ips.homeServer}:${portsStr.giteaSsh}";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
nftables = {
|
||||||
|
enable = true;
|
||||||
|
tables.vps-snat = {
|
||||||
|
family = "ip";
|
||||||
|
content = ''
|
||||||
|
chain postrouting {
|
||||||
|
type nat hook postrouting priority srcnat;
|
||||||
|
iifname "${externalInterface}" oifname "${wgInterface}" ip daddr ${ips.homeServer}/32 tcp dport ${portsStr.giteaSsh} masquerade comment "snat ssh forward"
|
||||||
|
}
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
firewall = {
|
||||||
|
enable = true;
|
||||||
|
filterForward = true;
|
||||||
|
checkReversePath = "loose";
|
||||||
|
allowedTCPPorts = [ ports.ssh ] ++ ports.web;
|
||||||
|
allowedUDPPorts = [ ports.wg ];
|
||||||
|
extraForwardRules = ''
|
||||||
|
iifname "${wgInterface}" ip saddr ${subnets.wgFriends} ip daddr ${ips.homeServer}/32 tcp dport ${portsStr.syncthing} accept
|
||||||
|
iifname "${wgInterface}" ip saddr ${ips.homeServer}/32 ip daddr ${subnets.wgFriends} tcp dport ${portsStr.syncthing} accept
|
||||||
|
|
||||||
|
iifname "${wgInterface}" ip saddr ${subnets.wgFriends} ip daddr ${ips.homeServer}/32 tcp dport { ${portsStr.synapseClient}, ${portsStr.synapseFederation}, ${portsStr.syncplay} } accept
|
||||||
|
|
||||||
|
iifname "${wgInterface}" ip saddr ${subnets.wgFriends} ip daddr ${ips.homeServer}/32 icmp type echo-request accept
|
||||||
|
iifname "${wgInterface}" ip saddr ${ips.wgFriend1}/32 ip daddr ${ips.homeServer}/32 tcp dport ${portsStr.stash} accept
|
||||||
|
iifname "${wgInterface}" ip saddr ${subnets.wgGuests} ip daddr ${ips.homeServer}/32 tcp dport { ${portsStr.stash}, ${portsStr.jellyfin}, ${portsStr.audiobookshelf}, ${portsStr.kavita} } accept
|
||||||
|
iifname "${wgInterface}" ip saddr ${subnets.wgGuests} ip daddr ${ips.homeServer}/32 icmp type echo-request accept
|
||||||
|
|
||||||
|
iifname "${wgInterface}" ip saddr ${subnets.wgHomelab} ip daddr ${ips.homeServer}/32 accept
|
||||||
|
|
||||||
|
iifname "${wgInterface}" ip saddr ${subnets.wgFriends} oifname "${externalInterface}" accept
|
||||||
|
iifname "${wgInterface}" ip saddr ${subnets.wgGuests} oifname "${externalInterface}" accept
|
||||||
|
|
||||||
|
ip saddr ${subnets.wgFriends} ip daddr ${subnets.wgHomelab} drop
|
||||||
|
ip saddr ${subnets.wgHomelab} ip daddr ${subnets.wgFriends} drop
|
||||||
|
ip saddr ${subnets.wgGuests} ip daddr ${subnets.wgHomelab} drop
|
||||||
|
ip saddr ${subnets.wgHomelab} ip daddr ${subnets.wgGuests} drop
|
||||||
|
ip saddr ${subnets.wgGuests} ip daddr ${subnets.wgFriends} drop
|
||||||
|
ip saddr ${subnets.wgFriends} ip daddr ${subnets.wgGuests} drop
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
security.sudo-rs.extraRules = [
|
||||||
|
{
|
||||||
|
users = [ "nixremote" ];
|
||||||
|
commands = [
|
||||||
|
{
|
||||||
|
command = "/run/current-system/sw/bin/nixos-rebuild";
|
||||||
|
options = [ "NOPASSWD" ];
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
];
|
||||||
|
systemd.tmpfiles.rules = [
|
||||||
|
"d /var/www/html 2775 deploy www-data -"
|
||||||
|
"d /var/www/html/portfolio 2775 deploy www-data -"
|
||||||
|
"d /var/www/html/blog 2775 deploy www-data -"
|
||||||
|
"d /var/www/html/lidarr-mb-gap 2775 lidarr-reports lidarr-reports -"
|
||||||
|
];
|
||||||
|
services = {
|
||||||
|
smartd.enable = lib.mkForce false;
|
||||||
|
openssh.ports = [ ports.ssh ];
|
||||||
|
};
|
||||||
|
users = {
|
||||||
|
groups = {
|
||||||
|
deploy = { };
|
||||||
|
lidarr-reports = { };
|
||||||
|
www-data = { };
|
||||||
|
};
|
||||||
|
users = {
|
||||||
|
nginx = lib.mkIf config.my.secureHost {
|
||||||
|
extraGroups = [
|
||||||
|
"www-data"
|
||||||
|
"lidarr-reports"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
deploy = {
|
||||||
|
isSystemUser = true;
|
||||||
|
group = "deploy";
|
||||||
|
home = "/var/lib/deploy";
|
||||||
|
createHome = true;
|
||||||
|
shell = pkgs.bashInteractive;
|
||||||
|
extraGroups = [ "www-data" ];
|
||||||
|
openssh.authorizedKeys.keyFiles = [ ../../secrets/ssh/ed25519_deploy.pub ];
|
||||||
|
};
|
||||||
|
lidarr-reports = {
|
||||||
|
isSystemUser = true;
|
||||||
|
group = "lidarr-reports";
|
||||||
|
home = "/var/lib/lidarr-reports";
|
||||||
|
createHome = true;
|
||||||
|
shell = pkgs.bashInteractive;
|
||||||
|
openssh.authorizedKeys.keyFiles = [ ../../secrets/ssh/ed25519_lidarr-reports.pub ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
{
|
{
|
||||||
lib,
|
lib,
|
||||||
|
config,
|
||||||
modulesPath,
|
modulesPath,
|
||||||
...
|
...
|
||||||
}:
|
}:
|
||||||
@@ -9,6 +10,10 @@
|
|||||||
kernelModules = [ ];
|
kernelModules = [ ];
|
||||||
extraModulePackages = [ ];
|
extraModulePackages = [ ];
|
||||||
kernelParams = [ "console=ttyS0,19200n8" ];
|
kernelParams = [ "console=ttyS0,19200n8" ];
|
||||||
|
kernel.sysctl = {
|
||||||
|
"net.ipv4.ip_forward" = 1;
|
||||||
|
"net.ipv4.conf.wg0.rp_filter" = 0;
|
||||||
|
};
|
||||||
initrd.availableKernelModules = [
|
initrd.availableKernelModules = [
|
||||||
"virtio_pci"
|
"virtio_pci"
|
||||||
"virtio_scsi"
|
"virtio_scsi"
|
||||||
@@ -29,13 +34,17 @@
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
fileSystems."/" = {
|
fileSystems."/" = {
|
||||||
device = "/dev/disk/by-uuid/f222513b-ded1-49fa-b591-20ce86a2fe7f";
|
device = lib.mkForce (
|
||||||
|
if config.my.build.baseImage then
|
||||||
|
"/dev/sda"
|
||||||
|
else
|
||||||
|
"/dev/disk/by-uuid/f222513b-ded1-49fa-b591-20ce86a2fe7f"
|
||||||
|
);
|
||||||
fsType = "ext4";
|
fsType = "ext4";
|
||||||
};
|
};
|
||||||
swapDevices = [
|
swapDevices = lib.mkMerge [
|
||||||
{
|
[ { device = "/dev/disk/by-uuid/f1408ea6-59a0-11ed-bc9d-525400000001"; } ]
|
||||||
device = "/dev/disk/by-uuid/f1408ea6-59a0-11ed-bc9d-525400000001";
|
(lib.mkIf config.my.build.baseImage [ { device = "/dev/sdb"; } ])
|
||||||
}
|
|
||||||
];
|
];
|
||||||
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
|
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
|
||||||
}
|
}
|
||||||
|
|||||||
116
hosts/vps/nginx-nextcloud.nix
Normal file
116
hosts/vps/nginx-nextcloud.nix
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
{ config, lib, ... }:
|
||||||
|
let
|
||||||
|
cfg = config.my.servers.nextcloud;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
config = lib.mkIf (cfg.enableProxy && config.my.enableProxy && config.my.secureHost) {
|
||||||
|
services.nginx.virtualHosts.${cfg.host} = {
|
||||||
|
forceSSL = true;
|
||||||
|
enableACME = true;
|
||||||
|
http2 = true;
|
||||||
|
default = true;
|
||||||
|
serverAliases = [ "cloud.rotehaare.art" ];
|
||||||
|
extraConfig = ''
|
||||||
|
index index.php index.html /index.php$request_uri;
|
||||||
|
add_header X-Content-Type-Options nosniff always;
|
||||||
|
add_header X-Robots-Tag "noindex, nofollow" always;
|
||||||
|
add_header X-Permitted-Cross-Domain-Policies none always;
|
||||||
|
add_header X-Frame-Options SAMEORIGIN always;
|
||||||
|
add_header Referrer-Policy no-referrer always;
|
||||||
|
add_header Strict-Transport-Security "max-age=15552000; includeSubDomains" always;
|
||||||
|
'';
|
||||||
|
locations = {
|
||||||
|
"= /robots.txt" = {
|
||||||
|
priority = 100;
|
||||||
|
extraConfig = ''
|
||||||
|
allow all;
|
||||||
|
access_log off;
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
"= /" = {
|
||||||
|
priority = 100;
|
||||||
|
proxyPass = cfg.local;
|
||||||
|
proxyWebsockets = true;
|
||||||
|
extraConfig = ''
|
||||||
|
if ( $http_user_agent ~ ^DavClnt ) {
|
||||||
|
return 302 /remote.php/webdav/$is_args$args;
|
||||||
|
}
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
"= /.well-known/carddav" = {
|
||||||
|
priority = 210;
|
||||||
|
extraConfig = ''
|
||||||
|
return 301 /remote.php/dav/;
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
"= /.well-known/caldav" = {
|
||||||
|
priority = 210;
|
||||||
|
extraConfig = ''
|
||||||
|
return 301 /remote.php/dav/;
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
"~ ^/\\.well-known/(?!acme-challenge|pki-validation)" = {
|
||||||
|
priority = 210;
|
||||||
|
extraConfig = ''
|
||||||
|
return 301 /index.php$request_uri;
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
"^~ /.well-known" = {
|
||||||
|
priority = 210;
|
||||||
|
extraConfig = ''
|
||||||
|
try_files $uri $uri/ =404;
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
"~ ^/(?:build|tests|config|lib|3rdparty|templates|data)(?:$|/)" = {
|
||||||
|
priority = 450;
|
||||||
|
extraConfig = ''
|
||||||
|
return 404;
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
"~ ^/(?:\\.|autotest|occ|issue|indie|db_|console)" = {
|
||||||
|
priority = 450;
|
||||||
|
extraConfig = ''
|
||||||
|
return 404;
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
"~ \\.php(?:$|/)" = {
|
||||||
|
priority = 500;
|
||||||
|
proxyPass = cfg.local;
|
||||||
|
proxyWebsockets = true;
|
||||||
|
extraConfig = ''
|
||||||
|
rewrite ^/(?!index|remote|public|cron|core\/ajax\/update|status|ocs\/v[12]|updater\/.+|ocs-provider\/.+|.+\/richdocumentscode(_arm64)?\/proxy) /index.php$request_uri;
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
"~ \\.(?:css|js|mjs|svg|gif|ico|jpg|jpeg|png|webp|wasm|tflite|map|html|ttf|bcmap|mp4|webm|ogg|flac)$" =
|
||||||
|
{
|
||||||
|
proxyPass = cfg.local;
|
||||||
|
extraConfig = ''
|
||||||
|
expires 6M;
|
||||||
|
access_log off;
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
"~ ^\\/(?:updater|ocs-provider)(?:$|\\/)" = {
|
||||||
|
proxyPass = cfg.local;
|
||||||
|
extraConfig = ''
|
||||||
|
try_files $uri/ =404;
|
||||||
|
index index.php;
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
"/remote" = {
|
||||||
|
priority = 1500;
|
||||||
|
extraConfig = ''
|
||||||
|
return 301 /remote.php$request_uri;
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
"/" = {
|
||||||
|
priority = 1600;
|
||||||
|
proxyPass = cfg.local;
|
||||||
|
proxyWebsockets = true;
|
||||||
|
extraConfig = ''
|
||||||
|
try_files $uri $uri/ /index.php$request_uri;
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
86
hosts/vps/toggles.nix
Normal file
86
hosts/vps/toggles.nix
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
{
|
||||||
|
config,
|
||||||
|
inputs,
|
||||||
|
lib,
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
inherit (inputs.self.lib)
|
||||||
|
enableList
|
||||||
|
mkEnabled
|
||||||
|
mkEnabledWithProxy
|
||||||
|
mkEnabledWithUsers
|
||||||
|
;
|
||||||
|
wgServerIp = config.my.ips.wg-server;
|
||||||
|
mkEnabledProxyIp = inputs.self.lib.mkEnabledProxyIp wgServerIp;
|
||||||
|
mkEnabledProxySocketIp = name: {
|
||||||
|
inherit name;
|
||||||
|
value = {
|
||||||
|
enableProxy = true;
|
||||||
|
enableSocket = true;
|
||||||
|
ip = wgServerIp;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
baseToggles = {
|
||||||
|
services = enableList mkEnabled [
|
||||||
|
"network"
|
||||||
|
"wireguard"
|
||||||
|
];
|
||||||
|
shell = enableList mkEnabledWithUsers [
|
||||||
|
"multimedia"
|
||||||
|
"tools"
|
||||||
|
];
|
||||||
|
dev = enableList mkEnabledWithUsers [
|
||||||
|
"nix"
|
||||||
|
"sh"
|
||||||
|
];
|
||||||
|
apps.dictionaries = {
|
||||||
|
enable = true;
|
||||||
|
users = "jawz";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
secureToggles = {
|
||||||
|
enableProxy = true;
|
||||||
|
enableContainers = true;
|
||||||
|
websites = {
|
||||||
|
portfolio.enableProxy = true;
|
||||||
|
lidarrMbReport.enableProxy = true;
|
||||||
|
};
|
||||||
|
servers =
|
||||||
|
enableList mkEnabledWithProxy [
|
||||||
|
"isso"
|
||||||
|
"microbin"
|
||||||
|
]
|
||||||
|
// enableList mkEnabledProxySocketIp [
|
||||||
|
"audiobookshelf"
|
||||||
|
"collabora"
|
||||||
|
"jellyfin"
|
||||||
|
"nextcloud"
|
||||||
|
"plausible"
|
||||||
|
"plex"
|
||||||
|
]
|
||||||
|
// enableList mkEnabledProxyIp [
|
||||||
|
"atticd"
|
||||||
|
"bazarr"
|
||||||
|
"gitea"
|
||||||
|
"homepage"
|
||||||
|
"kavita"
|
||||||
|
"keycloak"
|
||||||
|
"lidarr"
|
||||||
|
"linkwarden"
|
||||||
|
"maloja"
|
||||||
|
"mealie"
|
||||||
|
"metube"
|
||||||
|
"multi-scrobbler"
|
||||||
|
"oauth2-proxy"
|
||||||
|
"prowlarr"
|
||||||
|
"radarr"
|
||||||
|
"sonarr"
|
||||||
|
"vaultwarden"
|
||||||
|
"yamtrack"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
in
|
||||||
|
lib.mkMerge [
|
||||||
|
baseToggles
|
||||||
|
(lib.mkIf config.my.secureHost secureToggles)
|
||||||
|
]
|
||||||
@@ -48,6 +48,10 @@ let
|
|||||||
type = lib.types.bool;
|
type = lib.types.bool;
|
||||||
default = false;
|
default = false;
|
||||||
};
|
};
|
||||||
|
useDefaultProxy = lib.mkOption {
|
||||||
|
type = lib.types.bool;
|
||||||
|
default = true;
|
||||||
|
};
|
||||||
certPath = lib.mkOption {
|
certPath = lib.mkOption {
|
||||||
type = lib.types.nullOr lib.types.path;
|
type = lib.types.nullOr lib.types.path;
|
||||||
default = null;
|
default = null;
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ in
|
|||||||
++ inputs.self.lib.autoImport ./servers filterNames
|
++ inputs.self.lib.autoImport ./servers filterNames
|
||||||
++ inputs.self.lib.autoImport ./services filterNames
|
++ inputs.self.lib.autoImport ./services filterNames
|
||||||
++ inputs.self.lib.autoImport ./shell filterNames
|
++ inputs.self.lib.autoImport ./shell filterNames
|
||||||
|
++ inputs.self.lib.autoImport ./websites filterNames
|
||||||
++ inputs.self.lib.autoImport ./network filterNames
|
++ inputs.self.lib.autoImport ./network filterNames
|
||||||
++ [
|
++ [
|
||||||
./factories/mkscript.nix
|
./factories/mkscript.nix
|
||||||
@@ -29,7 +30,7 @@ in
|
|||||||
};
|
};
|
||||||
localhost6 = lib.mkOption {
|
localhost6 = lib.mkOption {
|
||||||
type = lib.types.str;
|
type = lib.types.str;
|
||||||
default = "::1";
|
default = "[::1]";
|
||||||
description = "The localhost ipv6 address.";
|
description = "The localhost ipv6 address.";
|
||||||
};
|
};
|
||||||
secureHost = lib.mkOption {
|
secureHost = lib.mkOption {
|
||||||
@@ -52,28 +53,49 @@ in
|
|||||||
vps = "45.79.25.87";
|
vps = "45.79.25.87";
|
||||||
wg-vps = "10.77.0.1";
|
wg-vps = "10.77.0.1";
|
||||||
wg-server = "10.77.0.2";
|
wg-server = "10.77.0.2";
|
||||||
wg-g1 = "10.9.0.2";
|
wg-galaxy = "10.77.0.3";
|
||||||
wg-gs = "10.9.0.0";
|
wg-phone = "10.77.0.4";
|
||||||
|
wg-guest1 = "10.9.0.2";
|
||||||
|
wg-guest2 = "10.9.0.3";
|
||||||
wg-friend1 = "10.8.0.2";
|
wg-friend1 = "10.8.0.2";
|
||||||
wg-friend2 = "10.8.0.3";
|
wg-friend2 = "10.8.0.3";
|
||||||
wg-friend3 = "10.8.0.4";
|
wg-friend3 = "10.8.0.4";
|
||||||
wg-friend4 = "10.8.0.5";
|
wg-friend4 = "10.8.0.5";
|
||||||
wg-friends = "10.8.0.0";
|
wg-friend5 = "10.8.0.6";
|
||||||
};
|
};
|
||||||
description = "Set of IP's for all my computers.";
|
description = "Set of IP's for all my computers.";
|
||||||
};
|
};
|
||||||
|
subnets = lib.mkOption {
|
||||||
|
type = lib.types.attrsOf lib.types.str;
|
||||||
|
default = {
|
||||||
|
wg-homelab = "10.77.0.0/24";
|
||||||
|
wg-friends = "10.8.0.0/24";
|
||||||
|
wg-guests = "10.9.0.0/24";
|
||||||
|
};
|
||||||
|
description = "Set of subnets for WireGuard networks.";
|
||||||
|
};
|
||||||
|
wgInterfaces = lib.mkOption {
|
||||||
|
type = lib.types.attrsOf lib.types.str;
|
||||||
|
default = {
|
||||||
|
wg-homelab = "10.77.0.1/24";
|
||||||
|
wg-friends = "10.8.0.1/24";
|
||||||
|
wg-guests = "10.9.0.1/24";
|
||||||
|
};
|
||||||
|
description = "WireGuard interface IPs for the VPS.";
|
||||||
|
};
|
||||||
interfaces = lib.mkOption {
|
interfaces = lib.mkOption {
|
||||||
type = lib.types.attrsOf lib.types.str;
|
type = lib.types.attrsOf lib.types.str;
|
||||||
default = {
|
default = {
|
||||||
server = "enp0s31f6";
|
server = "enp0s31f6";
|
||||||
miniserver = "enp2s0";
|
miniserver = "enp2s0";
|
||||||
workstation = "enp5s0";
|
workstation = "enp5s0";
|
||||||
|
vps = "eth0";
|
||||||
};
|
};
|
||||||
description = "Set of network interface names for all my computers.";
|
description = "Set of network interface names for all my computers.";
|
||||||
};
|
};
|
||||||
mainServer = lib.mkOption {
|
mainServer = lib.mkOption {
|
||||||
type = lib.types.str;
|
type = lib.types.str;
|
||||||
default = "miniserver";
|
default = "vps";
|
||||||
description = "The hostname of the main server.";
|
description = "The hostname of the main server.";
|
||||||
};
|
};
|
||||||
postgresSocket = lib.mkOption {
|
postgresSocket = lib.mkOption {
|
||||||
@@ -108,6 +130,11 @@ in
|
|||||||
};
|
};
|
||||||
enableContainers = lib.mkEnableOption "container services (Docker/Podman)";
|
enableContainers = lib.mkEnableOption "container services (Docker/Podman)";
|
||||||
enableProxy = lib.mkEnableOption "nginx reverse proxy for services";
|
enableProxy = lib.mkEnableOption "nginx reverse proxy for services";
|
||||||
|
build.baseImage = lib.mkOption {
|
||||||
|
type = lib.types.bool;
|
||||||
|
default = false;
|
||||||
|
description = "Whether to enable base image settings for this host build.";
|
||||||
|
};
|
||||||
toggleUsers = lib.mkOption {
|
toggleUsers = lib.mkOption {
|
||||||
type = lib.types.attrsOf (lib.types.either lib.types.str (lib.types.listOf lib.types.str));
|
type = lib.types.attrsOf (lib.types.either lib.types.str (lib.types.listOf lib.types.str));
|
||||||
default = {
|
default = {
|
||||||
@@ -195,7 +222,6 @@ in
|
|||||||
config.my.servers.go-vod.enable
|
config.my.servers.go-vod.enable
|
||||||
config.my.servers.tranga.enable
|
config.my.servers.tranga.enable
|
||||||
config.my.servers.drpp.enable
|
config.my.servers.drpp.enable
|
||||||
config.my.servers.plex-discord-bot.enable
|
|
||||||
]);
|
]);
|
||||||
message = "Container services are enabled but enableContainers is false";
|
message = "Container services are enabled but enableContainers is false";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ let
|
|||||||
"drpp"
|
"drpp"
|
||||||
"metube"
|
"metube"
|
||||||
"multi-scrobbler"
|
"multi-scrobbler"
|
||||||
"plex-discord-bot"
|
|
||||||
];
|
];
|
||||||
nativeServicesWithOpenFirewall = inputs.self.lib.getServicesWithNativeFirewall config firewallBlacklist;
|
nativeServicesWithOpenFirewall = inputs.self.lib.getServicesWithNativeFirewall config firewallBlacklist;
|
||||||
servicesConfig = lib.listToAttrs (
|
servicesConfig = lib.listToAttrs (
|
||||||
|
|||||||
@@ -5,44 +5,24 @@
|
|||||||
...
|
...
|
||||||
}:
|
}:
|
||||||
let
|
let
|
||||||
proxyReverseServices = [
|
|
||||||
"bazarr"
|
|
||||||
"firefox-syncserver"
|
|
||||||
"flame"
|
|
||||||
"flameSecret"
|
|
||||||
"isso"
|
|
||||||
"kavita"
|
|
||||||
"linkwarden"
|
|
||||||
"maloja"
|
|
||||||
"mealie"
|
|
||||||
"metube"
|
|
||||||
"microbin"
|
|
||||||
"multi-scrobbler"
|
|
||||||
"nix-serve"
|
|
||||||
"plausible"
|
|
||||||
"shiori"
|
|
||||||
"vaultwarden"
|
|
||||||
"yamtrack"
|
|
||||||
];
|
|
||||||
proxyReverseFixServices = [
|
proxyReverseFixServices = [
|
||||||
"atticd"
|
"atticd"
|
||||||
"audiobookshelf"
|
"audiobookshelf"
|
||||||
"gitea"
|
"gitea"
|
||||||
"lidarr"
|
"lidarr"
|
||||||
"ombi"
|
"ombi"
|
||||||
"prowlarr"
|
|
||||||
"radarr"
|
"radarr"
|
||||||
"sonarr"
|
"sonarr"
|
||||||
"stash"
|
|
||||||
];
|
];
|
||||||
proxyReversePrivateServices = [
|
proxyReversePrivateServices = [
|
||||||
"homepage"
|
"homepage"
|
||||||
|
"prowlarr"
|
||||||
|
"stash"
|
||||||
];
|
];
|
||||||
mkServiceConfig =
|
mkServiceConfig =
|
||||||
type: services: lib.listToAttrs (map (name: lib.nameValuePair name { inherit type; }) services);
|
type: services: lib.listToAttrs (map (name: lib.nameValuePair name { inherit type; }) services);
|
||||||
standardProxyServices =
|
standardProxyServices =
|
||||||
(mkServiceConfig "proxyReverse" proxyReverseServices)
|
(mkServiceConfig "proxyReverseFix" proxyReverseFixServices)
|
||||||
// (mkServiceConfig "proxyReverseFix" proxyReverseFixServices)
|
|
||||||
// (mkServiceConfig "proxyReversePrivate" proxyReversePrivateServices);
|
// (mkServiceConfig "proxyReversePrivate" proxyReversePrivateServices);
|
||||||
generateProxyConfig =
|
generateProxyConfig =
|
||||||
serviceName: serviceConfig:
|
serviceName: serviceConfig:
|
||||||
@@ -59,9 +39,21 @@ let
|
|||||||
throw "Unknown proxy type: ${serviceConfig.type}";
|
throw "Unknown proxy type: ${serviceConfig.type}";
|
||||||
in
|
in
|
||||||
lib.nameValuePair cfg.host (lib.mkIf cfg.enableProxy (proxyFunc cfg));
|
lib.nameValuePair cfg.host (lib.mkIf cfg.enableProxy (proxyFunc cfg));
|
||||||
|
standardProxyNames = builtins.attrNames standardProxyServices;
|
||||||
|
customProxyServices =
|
||||||
|
config.my.servers
|
||||||
|
|> lib.filterAttrs (
|
||||||
|
name: srv:
|
||||||
|
(srv.enableProxy or false)
|
||||||
|
&& (srv.useDefaultProxy or true)
|
||||||
|
&& !(builtins.elem name standardProxyNames)
|
||||||
|
)
|
||||||
|
|> lib.mapAttrs (_name: _srv: { type = "proxyReverse"; });
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
config = lib.mkIf config.my.enableProxy {
|
config = lib.mkIf config.my.enableProxy {
|
||||||
services.nginx.virtualHosts = lib.mapAttrs' generateProxyConfig standardProxyServices;
|
services.nginx.virtualHosts = lib.mapAttrs' generateProxyConfig (
|
||||||
|
standardProxyServices // customProxyServices
|
||||||
|
);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ in
|
|||||||
virtualisation.oci-containers.containers = lib.mkIf enable {
|
virtualisation.oci-containers.containers = lib.mkIf enable {
|
||||||
flame = lib.mkIf cfg.enable {
|
flame = lib.mkIf cfg.enable {
|
||||||
autoStart = true;
|
autoStart = true;
|
||||||
image = "pawelmalak/flame";
|
image = "pawelmalak/flame:latest";
|
||||||
ports = [ "${toString cfg.port}:${toString cfg.port}" ];
|
ports = [ "${toString cfg.port}:${toString cfg.port}" ];
|
||||||
volumes = [
|
volumes = [
|
||||||
"${config.my.containerData}/flame:/app/data"
|
"${config.my.containerData}/flame:/app/data"
|
||||||
@@ -36,7 +36,7 @@ in
|
|||||||
};
|
};
|
||||||
flame-nsfw = lib.mkIf cfgS.enable {
|
flame-nsfw = lib.mkIf cfgS.enable {
|
||||||
autoStart = true;
|
autoStart = true;
|
||||||
image = "pawelmalak/flame";
|
image = "pawelmalak/flame:latest";
|
||||||
ports = [ "${toString cfgS.port}:${toString cfg.port}" ];
|
ports = [ "${toString cfgS.port}:${toString cfg.port}" ];
|
||||||
volumes = [ "${config.my.containerData}/flame-nsfw:/app/data" ];
|
volumes = [ "${config.my.containerData}/flame-nsfw:/app/data" ];
|
||||||
environmentFiles = [ config.sops.secrets.flame.path ];
|
environmentFiles = [ config.sops.secrets.flame.path ];
|
||||||
|
|||||||
@@ -9,31 +9,33 @@ let
|
|||||||
in
|
in
|
||||||
{
|
{
|
||||||
options.my.servers.homepage = setup.mkOptions "homepage" "home" 8082;
|
options.my.servers.homepage = setup.mkOptions "homepage" "home" 8082;
|
||||||
config = lib.mkIf config.my.secureHost {
|
config = lib.mkMerge [
|
||||||
sops.secrets = lib.mkIf cfg.enable {
|
(lib.mkIf (cfg.enable && config.my.secureHost) {
|
||||||
homepage.sopsFile = ../../secrets/homepage.yaml;
|
sops.secrets.homepage.sopsFile = ../../secrets/homepage.yaml;
|
||||||
"private-ca/pem" = {
|
services.homepage-dashboard = {
|
||||||
|
inherit (cfg) enable;
|
||||||
|
listenPort = cfg.port;
|
||||||
|
environmentFile = config.sops.secrets.homepage.path;
|
||||||
|
settings = {
|
||||||
|
providers.openweathermap = "{{HOMEPAGE_VAR_OPENWEATHERMAP_API_KEY}}";
|
||||||
|
layout = import ./homepage/layout.nix;
|
||||||
|
};
|
||||||
|
widgets = import ./homepage/widgets.nix;
|
||||||
|
services = import ./homepage/services.nix { inherit lib config; };
|
||||||
|
bookmarks =
|
||||||
|
builtins.readDir ./homepage/bookmarks
|
||||||
|
|> builtins.attrNames
|
||||||
|
|> builtins.filter (file: builtins.match ".*\\.nix" file != null)
|
||||||
|
|> map (file: import ./homepage/bookmarks/${file});
|
||||||
|
};
|
||||||
|
})
|
||||||
|
(lib.mkIf (cfg.enableProxy && config.my.enableProxy && config.my.secureHost) {
|
||||||
|
sops.secrets."private-ca/pem" = {
|
||||||
sopsFile = ../../secrets/certs.yaml;
|
sopsFile = ../../secrets/certs.yaml;
|
||||||
owner = "nginx";
|
owner = "nginx";
|
||||||
group = "nginx";
|
group = "nginx";
|
||||||
};
|
};
|
||||||
};
|
my.servers.homepage.certPath = config.sops.secrets."private-ca/pem".path;
|
||||||
my.servers.homepage.certPath = config.sops.secrets."private-ca/pem".path;
|
})
|
||||||
services.homepage-dashboard = lib.mkIf cfg.enable {
|
];
|
||||||
inherit (cfg) enable;
|
|
||||||
listenPort = cfg.port;
|
|
||||||
environmentFile = config.sops.secrets.homepage.path;
|
|
||||||
settings = {
|
|
||||||
providers.openweathermap = "{{HOMEPAGE_VAR_OPENWEATHERMAP_API_KEY}}";
|
|
||||||
layout = import ./homepage/layout.nix;
|
|
||||||
};
|
|
||||||
widgets = import ./homepage/widgets.nix;
|
|
||||||
services = import ./homepage/services.nix { inherit lib config; };
|
|
||||||
bookmarks =
|
|
||||||
builtins.readDir ./homepage/bookmarks
|
|
||||||
|> builtins.attrNames
|
|
||||||
|> builtins.filter (file: builtins.match ".*\\.nix" file != null)
|
|
||||||
|> map (file: import ./homepage/bookmarks/${file});
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -209,7 +209,7 @@
|
|||||||
icon = "${cfg.name}.png";
|
icon = "${cfg.name}.png";
|
||||||
href = cfg.url;
|
href = cfg.url;
|
||||||
widget = {
|
widget = {
|
||||||
url = "http://${config.my.ips.wg-server}:8081";
|
url = "http://${config.my.ips.wg-server}:${toString cfg.port}";
|
||||||
type = cfg.name;
|
type = cfg.name;
|
||||||
username = "{{HOMEPAGE_VAR_NEXTCLOUD_USERNAME}}";
|
username = "{{HOMEPAGE_VAR_NEXTCLOUD_USERNAME}}";
|
||||||
password = "{{HOMEPAGE_VAR_NEXTCLOUD_PASSWORD}}";
|
password = "{{HOMEPAGE_VAR_NEXTCLOUD_PASSWORD}}";
|
||||||
|
|||||||
@@ -23,22 +23,48 @@ let
|
|||||||
in
|
in
|
||||||
{
|
{
|
||||||
options.my.servers.jellyfin = setup.mkOptions "jellyfin" "flix" 8096;
|
options.my.servers.jellyfin = setup.mkOptions "jellyfin" "flix" 8096;
|
||||||
config = lib.mkIf (cfg.enable && config.my.secureHost) {
|
config = lib.mkMerge [
|
||||||
environment.systemPackages = [
|
(lib.mkIf (cfg.enable && config.my.secureHost) {
|
||||||
pkgs.jellyfin-ffmpeg
|
environment.systemPackages = [
|
||||||
]
|
pkgs.jellyfin-ffmpeg
|
||||||
++ (lib.optional cfg.enableCron [ sub-sync-path ]);
|
]
|
||||||
users.users.jellyfin = {
|
++ (lib.optional cfg.enableCron [ sub-sync-path ]);
|
||||||
uid = 984;
|
users.users.jellyfin = {
|
||||||
group = "piracy";
|
uid = 984;
|
||||||
isSystemUser = true;
|
group = "piracy";
|
||||||
};
|
isSystemUser = true;
|
||||||
services = {
|
};
|
||||||
jellyfin = {
|
services.jellyfin = {
|
||||||
inherit (cfg) enable;
|
inherit (cfg) enable;
|
||||||
group = "piracy";
|
group = "piracy";
|
||||||
};
|
};
|
||||||
nginx = lib.mkIf cfg.enableProxy {
|
systemd = lib.mkIf cfg.enableCron {
|
||||||
|
services.sub-sync = {
|
||||||
|
restartIfChanged = true;
|
||||||
|
description = "syncronizes subtitles downloaded & modified today";
|
||||||
|
wantedBy = [ "default.target" ];
|
||||||
|
path = sub-sync-path;
|
||||||
|
serviceConfig = {
|
||||||
|
Restart = "on-failure";
|
||||||
|
RestartSec = 30;
|
||||||
|
ExecStart = "${sub-sync}/bin/sub-sync all";
|
||||||
|
Type = "simple";
|
||||||
|
User = "root";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
timers.sub-sync = {
|
||||||
|
enable = true;
|
||||||
|
description = "syncronizes subtitles downloaded & modified today";
|
||||||
|
wantedBy = [ "timers.target" ];
|
||||||
|
timerConfig = {
|
||||||
|
OnCalendar = "20:00";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
})
|
||||||
|
(lib.mkIf (cfg.enableProxy && config.my.enableProxy) {
|
||||||
|
my.servers.jellyfin.useDefaultProxy = false;
|
||||||
|
services.nginx = {
|
||||||
appendHttpConfig = ''
|
appendHttpConfig = ''
|
||||||
# JELLYFIN
|
# JELLYFIN
|
||||||
proxy_cache_path /var/cache/nginx/jellyfin levels=1:2 keys_zone=jellyfin:100m max_size=15g inactive=1d use_temp_path=off;
|
proxy_cache_path /var/cache/nginx/jellyfin levels=1:2 keys_zone=jellyfin:100m max_size=15g inactive=1d use_temp_path=off;
|
||||||
@@ -94,29 +120,6 @@ in
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
})
|
||||||
systemd = lib.mkIf cfg.enableCron {
|
];
|
||||||
services.sub-sync = {
|
|
||||||
restartIfChanged = true;
|
|
||||||
description = "syncronizes subtitles downloaded & modified today";
|
|
||||||
wantedBy = [ "default.target" ];
|
|
||||||
path = sub-sync-path;
|
|
||||||
serviceConfig = {
|
|
||||||
Restart = "on-failure";
|
|
||||||
RestartSec = 30;
|
|
||||||
ExecStart = "${sub-sync}/bin/sub-sync all";
|
|
||||||
Type = "simple";
|
|
||||||
User = "root";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
timers.sub-sync = {
|
|
||||||
enable = true;
|
|
||||||
description = "syncronizes subtitles downloaded & modified today";
|
|
||||||
wantedBy = [ "timers.target" ];
|
|
||||||
timerConfig = {
|
|
||||||
OnCalendar = "20:00";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,35 +10,38 @@ let
|
|||||||
in
|
in
|
||||||
{
|
{
|
||||||
options.my.servers.keycloak = setup.mkOptions "keycloak" "auth" 8090;
|
options.my.servers.keycloak = setup.mkOptions "keycloak" "auth" 8090;
|
||||||
config = lib.mkIf (cfg.enable && config.my.secureHost) {
|
config = lib.mkMerge [
|
||||||
sops.secrets.postgres-password.sopsFile = ../../secrets/secrets.yaml;
|
(lib.mkIf (cfg.enable && config.my.secureHost) {
|
||||||
sops.secrets.keycloak = {
|
sops.secrets.postgres-password.sopsFile = ../../secrets/secrets.yaml;
|
||||||
sopsFile = ../../secrets/env.yaml;
|
sops.secrets.keycloak = {
|
||||||
restartUnits = [ "keycloak.service" ];
|
sopsFile = ../../secrets/env.yaml;
|
||||||
};
|
restartUnits = [ "keycloak.service" ];
|
||||||
services.keycloak = {
|
|
||||||
inherit (cfg) enable;
|
|
||||||
database = {
|
|
||||||
type = "postgresql";
|
|
||||||
host = "localhost";
|
|
||||||
createLocally = false;
|
|
||||||
username = "keycloak";
|
|
||||||
name = "keycloak";
|
|
||||||
passwordFile = config.sops.secrets.postgres-password.path;
|
|
||||||
};
|
};
|
||||||
settings = {
|
services.keycloak = {
|
||||||
hostname = cfg.host;
|
inherit (cfg) enable;
|
||||||
hostname-strict = true;
|
database = {
|
||||||
hostname-strict-https = false;
|
type = "postgresql";
|
||||||
http-enabled = true;
|
host = "localhost";
|
||||||
http-port = cfg.port;
|
createLocally = false;
|
||||||
http-host = cfg.ip;
|
username = "keycloak";
|
||||||
proxy-headers = "xforwarded";
|
name = "keycloak";
|
||||||
|
passwordFile = config.sops.secrets.postgres-password.path;
|
||||||
|
};
|
||||||
|
settings = {
|
||||||
|
hostname = cfg.host;
|
||||||
|
hostname-strict = true;
|
||||||
|
hostname-strict-https = false;
|
||||||
|
http-enabled = true;
|
||||||
|
http-port = cfg.port;
|
||||||
|
http-host = cfg.ip;
|
||||||
|
proxy-headers = "xforwarded";
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
systemd.services.keycloak.serviceConfig.EnvironmentFile = config.sops.secrets.keycloak.path;
|
||||||
systemd.services.keycloak.serviceConfig.EnvironmentFile = config.sops.secrets.keycloak.path;
|
})
|
||||||
services.nginx.virtualHosts.${cfg.host} = lib.mkIf (cfg.enableProxy && config.my.enableProxy) (
|
(lib.mkIf (cfg.enableProxy && config.my.enableProxy) {
|
||||||
inputs.self.lib.proxyReverseFix cfg
|
my.servers.keycloak.useDefaultProxy = false;
|
||||||
);
|
services.nginx.virtualHosts.${cfg.host} = inputs.self.lib.proxyReverseFix cfg;
|
||||||
};
|
})
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ in
|
|||||||
options.my.servers.lidarr = setup.mkOptions "lidarr" "music" 8686;
|
options.my.servers.lidarr = setup.mkOptions "lidarr" "music" 8686;
|
||||||
config.virtualisation.oci-containers.containers.lidarr = lib.mkIf cfg.enable {
|
config.virtualisation.oci-containers.containers.lidarr = lib.mkIf cfg.enable {
|
||||||
autoStart = true;
|
autoStart = true;
|
||||||
image = "linuxserver/lidarr:version-3.0.1.4866";
|
image = "linuxserver/lidarr:latest";
|
||||||
ports = [ "${toString cfg.port}:${toString cfg.port}" ];
|
ports = [ "${toString cfg.port}:${toString cfg.port}" ];
|
||||||
environment = {
|
environment = {
|
||||||
TZ = config.my.timeZone;
|
TZ = config.my.timeZone;
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ in
|
|||||||
config = lib.mkIf (cfg.enable && config.my.secureHost) {
|
config = lib.mkIf (cfg.enable && config.my.secureHost) {
|
||||||
sops.secrets.maloja.sopsFile = ../../secrets/env.yaml;
|
sops.secrets.maloja.sopsFile = ../../secrets/env.yaml;
|
||||||
virtualisation.oci-containers.containers.maloja = {
|
virtualisation.oci-containers.containers.maloja = {
|
||||||
image = "krateng/maloja:3.2.4";
|
image = "krateng/maloja:latest";
|
||||||
ports = [ "${toString cfg.port}:${toString cfg.port}" ];
|
ports = [ "${toString cfg.port}:${toString cfg.port}" ];
|
||||||
environmentFiles = [ config.sops.secrets.maloja.path ];
|
environmentFiles = [ config.sops.secrets.maloja.path ];
|
||||||
environment = {
|
environment = {
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ in
|
|||||||
{
|
{
|
||||||
options.my.servers.metube = setup.mkOptions "metube" "bajameesta" 8881;
|
options.my.servers.metube = setup.mkOptions "metube" "bajameesta" 8881;
|
||||||
config.virtualisation.oci-containers.containers.metube = lib.mkIf cfg.enable {
|
config.virtualisation.oci-containers.containers.metube = lib.mkIf cfg.enable {
|
||||||
image = "ghcr.io/alexta69/metube:2026.01.02";
|
image = "ghcr.io/alexta69/metube:latest";
|
||||||
ports = [ "${toString cfg.port}:8081" ];
|
ports = [ "${toString cfg.port}:8081" ];
|
||||||
volumes = [
|
volumes = [
|
||||||
"${config.my.containerData}/metube:/downloads"
|
"${config.my.containerData}/metube:/downloads"
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ in
|
|||||||
config = lib.mkIf (cfg.enable && config.my.secureHost) {
|
config = lib.mkIf (cfg.enable && config.my.secureHost) {
|
||||||
sops.secrets.multi-scrobbler.sopsFile = ../../secrets/env.yaml;
|
sops.secrets.multi-scrobbler.sopsFile = ../../secrets/env.yaml;
|
||||||
virtualisation.oci-containers.containers.multi-scrobbler = {
|
virtualisation.oci-containers.containers.multi-scrobbler = {
|
||||||
image = "foxxmd/multi-scrobbler:0.10.0";
|
image = "foxxmd/multi-scrobbler:latest";
|
||||||
ports = [ "${toString cfg.port}:${toString cfg.port}" ];
|
ports = [ "${toString cfg.port}:${toString cfg.port}" ];
|
||||||
environmentFiles = [ config.sops.secrets.multi-scrobbler.path ];
|
environmentFiles = [ config.sops.secrets.multi-scrobbler.path ];
|
||||||
environment = {
|
environment = {
|
||||||
|
|||||||
@@ -38,281 +38,272 @@ let
|
|||||||
in
|
in
|
||||||
{
|
{
|
||||||
options.my.servers = {
|
options.my.servers = {
|
||||||
nextcloud = setup.mkOptions "nextcloud" "cloud" 80;
|
nextcloud = setup.mkOptions "nextcloud" "cloud" 8081;
|
||||||
collabora = setup.mkOptions "collabora" "collabora" 9980;
|
collabora = setup.mkOptions "collabora" "collabora" 9980;
|
||||||
go-vod.enable = lib.mkEnableOption "Go-VOD video transcoding service";
|
go-vod.enable = lib.mkEnableOption "Go-VOD video transcoding service";
|
||||||
};
|
};
|
||||||
config = lib.mkIf (cfg.enable && config.my.servers.postgres.enable && config.my.secureHost) {
|
config = lib.mkMerge [
|
||||||
sops.secrets.nextcloud-adminpass = {
|
{ my.servers.nextcloud.useDefaultProxy = false; }
|
||||||
owner = config.users.users.nextcloud.name;
|
(lib.mkIf (cfg.enable && config.my.servers.postgres.enable && config.my.secureHost) {
|
||||||
inherit (config.users.users.nextcloud) group;
|
sops.secrets.nextcloud-adminpass = {
|
||||||
};
|
owner = config.users.users.nextcloud.name;
|
||||||
nixpkgs.config.permittedInsecurePackages = [
|
inherit (config.users.users.nextcloud) group;
|
||||||
"nodejs-14.21.3"
|
|
||||||
"openssl-1.1.1v"
|
|
||||||
];
|
|
||||||
users.groups.nextcloud = { inherit gid; };
|
|
||||||
users.users.nextcloud = {
|
|
||||||
inherit uid;
|
|
||||||
isSystemUser = true;
|
|
||||||
group = "nextcloud";
|
|
||||||
extraGroups = [ "render" ];
|
|
||||||
packages = builtins.attrValues {
|
|
||||||
inherit exiftool pytensorflow;
|
|
||||||
inherit (pkgs)
|
|
||||||
ffmpeg
|
|
||||||
mediainfo
|
|
||||||
nodejs
|
|
||||||
perl
|
|
||||||
;
|
|
||||||
};
|
};
|
||||||
};
|
nixpkgs.config.permittedInsecurePackages = [
|
||||||
services = {
|
"nodejs-14.21.3"
|
||||||
nextcloud = {
|
"openssl-1.1.1v"
|
||||||
enable = true;
|
];
|
||||||
https = false; # vps
|
users = {
|
||||||
package = pkgs.nextcloud32;
|
groups.nextcloud = { inherit gid; };
|
||||||
appstoreEnable = true;
|
users.nextcloud = {
|
||||||
configureRedis = true;
|
inherit uid;
|
||||||
extraAppsEnable = true;
|
isSystemUser = true;
|
||||||
enableImagemagick = true;
|
group = "nextcloud";
|
||||||
maxUploadSize = "4096M";
|
extraGroups = [ "render" ];
|
||||||
hostName = cfg.host;
|
packages = builtins.attrValues {
|
||||||
caching = {
|
inherit exiftool pytensorflow;
|
||||||
redis = true;
|
inherit (pkgs)
|
||||||
memcached = true;
|
ffmpeg
|
||||||
apcu = true;
|
mediainfo
|
||||||
|
nodejs
|
||||||
|
perl
|
||||||
|
;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
config = {
|
};
|
||||||
adminpassFile = config.sops.secrets.nextcloud-adminpass.path;
|
services = {
|
||||||
dbtype = "pgsql";
|
nextcloud = {
|
||||||
dbhost = config.my.postgresSocket;
|
enable = true;
|
||||||
dbname = "nextcloud";
|
https = false; # vps
|
||||||
};
|
package = pkgs.nextcloud32;
|
||||||
phpOptions = {
|
appstoreEnable = true;
|
||||||
catch_workers_output = "yes";
|
configureRedis = true;
|
||||||
display_errors = "stderr";
|
extraAppsEnable = true;
|
||||||
error_reporting = "E_ALL & ~E_DEPRECATED & ~E_STRICT";
|
enableImagemagick = true;
|
||||||
expose_php = "Off";
|
maxUploadSize = "4096M";
|
||||||
preview_max_x = 2048;
|
hostName = cfg.host;
|
||||||
preview_max_y = 2048;
|
caching = {
|
||||||
short_open_tag = "Off";
|
redis = true;
|
||||||
"opcache.enable_cli" = "1";
|
memcached = true;
|
||||||
"opcache.fast_shutdown" = "1";
|
apcu = true;
|
||||||
"opcache.interned_strings_buffer" = "16";
|
};
|
||||||
"opcache.jit" = "1255";
|
config = {
|
||||||
"opcache.jit_buffer_size" = "256M";
|
adminpassFile = config.sops.secrets.nextcloud-adminpass.path;
|
||||||
"opcache.max_accelerated_files" = "10000";
|
dbtype = "pgsql";
|
||||||
"opcache.huge_code_pages" = "1";
|
dbhost = config.my.postgresSocket;
|
||||||
"opcache.enable_file_override" = "1";
|
dbname = "nextcloud";
|
||||||
"opcache.memory_consumption" = "256";
|
};
|
||||||
"opcache.revalidate_freq" = "60";
|
phpOptions = {
|
||||||
"opcache.save_comments" = "1";
|
catch_workers_output = "yes";
|
||||||
"opcache.validate_timestamps" = "0";
|
display_errors = "stderr";
|
||||||
"openssl.cafile" = "/etc/ssl/certs/ca-certificates.crt";
|
error_reporting = "E_ALL & ~E_DEPRECATED & ~E_STRICT";
|
||||||
};
|
expose_php = "Off";
|
||||||
settings = {
|
preview_max_x = 2048;
|
||||||
log_type = "file";
|
preview_max_y = 2048;
|
||||||
loglevel = 1;
|
short_open_tag = "Off";
|
||||||
trusted_proxies = [
|
"opcache.enable_cli" = "1";
|
||||||
config.my.localhost
|
"opcache.fast_shutdown" = "1";
|
||||||
config.my.localhost6
|
"opcache.interned_strings_buffer" = "16";
|
||||||
config.my.ips.router
|
"opcache.jit" = "1255";
|
||||||
config.my.ips.wg-vps
|
"opcache.jit_buffer_size" = "256M";
|
||||||
|
"opcache.max_accelerated_files" = "10000";
|
||||||
|
"opcache.huge_code_pages" = "1";
|
||||||
|
"opcache.enable_file_override" = "1";
|
||||||
|
"opcache.memory_consumption" = "256";
|
||||||
|
"opcache.revalidate_freq" = "60";
|
||||||
|
"opcache.save_comments" = "1";
|
||||||
|
"opcache.validate_timestamps" = "0";
|
||||||
|
"openssl.cafile" = "/etc/ssl/certs/ca-certificates.crt";
|
||||||
|
};
|
||||||
|
settings = {
|
||||||
|
log_type = "file";
|
||||||
|
loglevel = 1;
|
||||||
|
trusted_proxies = [
|
||||||
|
config.my.localhost
|
||||||
|
config.my.localhost6
|
||||||
|
config.my.ips.router
|
||||||
|
config.my.ips.wg-vps
|
||||||
|
];
|
||||||
|
trusted_domains = [
|
||||||
|
cfg.host
|
||||||
|
config.my.ips.${config.networking.hostName}
|
||||||
|
"localhost"
|
||||||
|
"cloud.rotehaare.art"
|
||||||
|
];
|
||||||
|
overwriteprotocol = "https";
|
||||||
|
"overwrite.cli.url" = "${cfg.url}";
|
||||||
|
forwarded_for_headers = [ "HTTP_X_FORWARDED_FOR" ];
|
||||||
|
default_phone_region = "MX";
|
||||||
|
allow_local_remote_servers = true;
|
||||||
|
mail_smtpmode = "sendmail";
|
||||||
|
mail_sendmailmode = "pipe";
|
||||||
|
preview_ffmpeg_path = "${pkgs.ffmpeg}/bin/ffmpeg";
|
||||||
|
"memories.exiftool" = "${exiftool}/bin/exiftool";
|
||||||
|
"memories.ffmpeg_path" = "${pkgs.ffmpeg}/bin/ffmpeg";
|
||||||
|
"memories.ffprobe_path" = "${pkgs.ffmpeg}/bin/ffprobe";
|
||||||
|
enabledPreviewProviders = [
|
||||||
|
"OC\\Preview\\AVI"
|
||||||
|
"OC\\Preview\\BMP"
|
||||||
|
"OC\\Preview\\GIF"
|
||||||
|
"OC\\Preview\\HEIC"
|
||||||
|
"OC\\Preview\\Image"
|
||||||
|
"OC\\Preview\\JPEG"
|
||||||
|
"OC\\Preview\\Krita"
|
||||||
|
"OC\\Preview\\MKV"
|
||||||
|
"OC\\Preview\\MP3"
|
||||||
|
"OC\\Preview\\MP4"
|
||||||
|
"OC\\Preview\\MarkDown"
|
||||||
|
"OC\\Preview\\Movie"
|
||||||
|
"OC\\Preview\\OpenDocument"
|
||||||
|
"OC\\Preview\\PNG"
|
||||||
|
"OC\\Preview\\TIFF"
|
||||||
|
"OC\\Preview\\TXT"
|
||||||
|
"OC\\Preview\\XBitmap"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
phpExtraExtensions = all: [
|
||||||
|
all.pdlib
|
||||||
|
all.bz2
|
||||||
];
|
];
|
||||||
trusted_domains = [
|
};
|
||||||
cfg.host
|
nginx.virtualHosts.${cfg.host} = {
|
||||||
config.my.ips.${config.networking.hostName}
|
forceSSL = false;
|
||||||
"localhost"
|
enableACME = false;
|
||||||
|
http2 = false;
|
||||||
|
serverAliases = [
|
||||||
"cloud.rotehaare.art"
|
"cloud.rotehaare.art"
|
||||||
];
|
];
|
||||||
overwriteprotocol = "https";
|
|
||||||
"overwrite.cli.url" = "${cfg.url}";
|
|
||||||
forwarded_for_headers = [ "HTTP_X_FORWARDED_FOR" ];
|
|
||||||
default_phone_region = "MX";
|
|
||||||
allow_local_remote_servers = true;
|
|
||||||
mail_smtpmode = "sendmail";
|
|
||||||
mail_sendmailmode = "pipe";
|
|
||||||
preview_ffmpeg_path = "${pkgs.ffmpeg}/bin/ffmpeg";
|
|
||||||
"memories.exiftool" = "${exiftool}/bin/exiftool";
|
|
||||||
"memories.ffmpeg_path" = "${pkgs.ffmpeg}/bin/ffmpeg";
|
|
||||||
"memories.ffprobe_path" = "${pkgs.ffmpeg}/bin/ffprobe";
|
|
||||||
enabledPreviewProviders = [
|
|
||||||
"OC\\Preview\\AVI"
|
|
||||||
"OC\\Preview\\BMP"
|
|
||||||
"OC\\Preview\\GIF"
|
|
||||||
"OC\\Preview\\HEIC"
|
|
||||||
"OC\\Preview\\Image"
|
|
||||||
"OC\\Preview\\JPEG"
|
|
||||||
"OC\\Preview\\Krita"
|
|
||||||
"OC\\Preview\\MKV"
|
|
||||||
"OC\\Preview\\MP3"
|
|
||||||
"OC\\Preview\\MP4"
|
|
||||||
"OC\\Preview\\MarkDown"
|
|
||||||
"OC\\Preview\\Movie"
|
|
||||||
"OC\\Preview\\OpenDocument"
|
|
||||||
"OC\\Preview\\PNG"
|
|
||||||
"OC\\Preview\\TIFF"
|
|
||||||
"OC\\Preview\\TXT"
|
|
||||||
"OC\\Preview\\XBitmap"
|
|
||||||
];
|
|
||||||
};
|
|
||||||
phpExtraExtensions = all: [
|
|
||||||
all.pdlib
|
|
||||||
all.bz2
|
|
||||||
];
|
|
||||||
};
|
|
||||||
nginx.virtualHosts = {
|
|
||||||
"${cfg.host}" = lib.mkIf cfg.enableProxy {
|
|
||||||
forceSSL = false; # vps
|
|
||||||
enableACME = false; # vps
|
|
||||||
http2 = false; # vps
|
|
||||||
# default = true; #vps
|
|
||||||
#vps
|
|
||||||
listen = [
|
listen = [
|
||||||
{
|
{
|
||||||
addr = config.my.ips.wg-server;
|
addr = config.my.ips.wg-server;
|
||||||
port = 8081;
|
inherit (cfg) port;
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
addr = config.my.localhost;
|
addr = config.my.localhost;
|
||||||
port = 8081;
|
inherit (cfg) port;
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
#vps
|
|
||||||
serverAliases = [ "cloud.rotehaare.art" ];
|
|
||||||
extraConfig = ''
|
|
||||||
add_header X-XSS-Protection "1; mode=block" always;
|
|
||||||
add_header Strict-Transport-Security "max-age=15552000; includeSubDomains" always;
|
|
||||||
add_header X-Content-Type-Options "nosniff" always;
|
|
||||||
add_header X-Frame-Options "SAMEORIGIN" always;
|
|
||||||
add_header Referrer-Policy "no-referrer-when-downgrade" always;
|
|
||||||
add_header X-Permitted-Cross-Domain-Policies "none" always;
|
|
||||||
'';
|
|
||||||
locations = {
|
|
||||||
"/".proxyWebsockets = true;
|
|
||||||
"~ ^/nextcloud/(?:index|remote|public|cron|core/ajax/update|status|ocs/v[12]|updater/.+|oc[ms]-provider/.+|.+/richdocumentscode/proxy).php(?:$|/)" =
|
|
||||||
{ };
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
"${cfgC.host}" = lib.mkIf cfgC.enableProxy {
|
};
|
||||||
forceSSL = true;
|
virtualisation.oci-containers.containers = {
|
||||||
enableACME = true;
|
go-vod = lib.mkIf config.my.servers.go-vod.enable {
|
||||||
http2 = true;
|
autoStart = true;
|
||||||
locations = {
|
image = "radialapps/go-vod:latest";
|
||||||
# static files
|
environment = {
|
||||||
"^~ /browser" = {
|
TZ = config.my.timeZone;
|
||||||
proxyPass = cfgC.local;
|
NEXTCLOUD_HOST = "https://${config.services.nextcloud.hostName}";
|
||||||
extraConfig = commonProxyConfig;
|
NVIDIA_VISIBLE_DEVICES = "all";
|
||||||
};
|
};
|
||||||
# Legacy static files (for compatibility)
|
volumes = [ "ncdata:/var/www/html:ro" ];
|
||||||
"^~ /loleaflet" = {
|
extraOptions = [
|
||||||
proxyPass = cfgC.local;
|
"--device=/dev/dri" # VA-API (omit for NVENC)
|
||||||
extraConfig = commonProxyConfig;
|
];
|
||||||
};
|
};
|
||||||
# WOPI discovery URL
|
collabora = lib.mkIf cfgC.enable {
|
||||||
"^~ /hosting/discovery" = {
|
autoStart = true;
|
||||||
proxyPass = cfgC.local;
|
image = "collabora/code:latest";
|
||||||
extraConfig = commonProxyConfig;
|
ports = [ "${toString cfgC.port}:${toString cfgC.port}" ];
|
||||||
};
|
environment = {
|
||||||
# Capabilities
|
TZ = config.my.timeZone;
|
||||||
"^~ /hosting/capabilities" = {
|
domain = cfg.host;
|
||||||
proxyPass = cfgC.local;
|
aliasgroup1 = "${cfg.url}:443";
|
||||||
extraConfig = commonProxyConfig;
|
aliasgroup2 = "https://cloud.rotehaare.art:443";
|
||||||
};
|
server_name = cfgC.host;
|
||||||
# download, presentation, image upload and websocket
|
dictionaries = "en_CA en_US es_MX es_ES fr_FR it pt_BR ru";
|
||||||
"~ ^/cool" = {
|
extra_params = ''
|
||||||
proxyPass = cfgC.local;
|
--o:ssl.enable=false
|
||||||
extraConfig = commonWebsocketConfig;
|
--o:ssl.termination=true
|
||||||
};
|
--o:remote_font_config.url=${cfg.url}/apps/richdocuments/settings/fonts.json
|
||||||
# Legacy websocket (for compatibility)
|
--o:logging.level=information
|
||||||
"~ ^/lool" = {
|
'';
|
||||||
proxyPass = cfgC.local;
|
DONT_GEN_SSL_CERT = "1";
|
||||||
extraConfig = commonWebsocketConfig;
|
SLEEPFORDEBUGGER = "0";
|
||||||
};
|
};
|
||||||
# Admin Console websocket
|
extraOptions = [
|
||||||
"^~ /cool/adminws" = {
|
"--cap-add"
|
||||||
proxyPass = cfgC.local;
|
"MKNOD"
|
||||||
extraConfig = commonWebsocketConfig;
|
];
|
||||||
};
|
};
|
||||||
# Legacy Admin Console websocket (for compatibility)
|
};
|
||||||
"^~ /lool/adminws" = {
|
systemd = lib.mkIf cfg.enableCron {
|
||||||
proxyPass = cfgC.local;
|
services = {
|
||||||
extraConfig = commonWebsocketConfig;
|
nextcloud-cron.path = [ pkgs.perl ];
|
||||||
|
nextcloud-cronjob =
|
||||||
|
let
|
||||||
|
inherit (inputs.jawz-scripts.packages.x86_64-linux) nextcloud-cronjob;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
description = "Runs various nextcloud-related cronjobs";
|
||||||
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
path = [
|
||||||
|
pkgs.bash
|
||||||
|
nextcloud-cronjob
|
||||||
|
];
|
||||||
|
serviceConfig = {
|
||||||
|
Restart = "on-failure";
|
||||||
|
RestartSec = 30;
|
||||||
|
ExecStart = "${nextcloud-cronjob}/bin/nextcloud-cronjob";
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
};
|
||||||
|
timers.nextcloud-cronjob = {
|
||||||
|
enable = true;
|
||||||
|
description = "Runs various nextcloud-related cronjobs";
|
||||||
|
wantedBy = [ "timers.target" ];
|
||||||
|
timerConfig = {
|
||||||
|
OnCalendar = "*:0/10";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
})
|
||||||
virtualisation.oci-containers.containers = {
|
(lib.mkIf (cfgC.enableProxy && config.my.enableProxy) {
|
||||||
go-vod = lib.mkIf config.my.servers.go-vod.enable {
|
services.nginx.virtualHosts.${cfgC.host} = {
|
||||||
autoStart = true;
|
forceSSL = true;
|
||||||
image = "radialapps/go-vod";
|
enableACME = true;
|
||||||
environment = {
|
http2 = true;
|
||||||
TZ = config.my.timeZone;
|
locations = {
|
||||||
NEXTCLOUD_HOST = "https://${config.services.nextcloud.hostName}";
|
# static files
|
||||||
NVIDIA_VISIBLE_DEVICES = "all";
|
"^~ /browser" = {
|
||||||
};
|
proxyPass = cfgC.local;
|
||||||
volumes = [ "ncdata:/var/www/html:ro" ];
|
extraConfig = commonProxyConfig;
|
||||||
extraOptions = [
|
};
|
||||||
"--device=/dev/dri" # VA-API (omit for NVENC)
|
# Legacy static files (for compatibility)
|
||||||
];
|
"^~ /loleaflet" = {
|
||||||
};
|
proxyPass = cfgC.local;
|
||||||
collabora = lib.mkIf cfgC.enable {
|
extraConfig = commonProxyConfig;
|
||||||
autoStart = true;
|
};
|
||||||
image = "collabora/code:latest";
|
# WOPI discovery URL
|
||||||
ports = [ "9980:9980" ];
|
"^~ /hosting/discovery" = {
|
||||||
environment = {
|
proxyPass = cfgC.local;
|
||||||
TZ = config.my.timeZone;
|
extraConfig = commonProxyConfig;
|
||||||
domain = cfg.host;
|
};
|
||||||
aliasgroup1 = "${cfg.url}:443";
|
# Capabilities
|
||||||
aliasgroup2 = "https://cloud.rotehaare.art:443";
|
"^~ /hosting/capabilities" = {
|
||||||
server_name = cfgC.host;
|
proxyPass = cfgC.local;
|
||||||
dictionaries = "en_CA en_US es_MX es_ES fr_FR it pt_BR ru";
|
extraConfig = commonProxyConfig;
|
||||||
extra_params = ''
|
};
|
||||||
--o:ssl.enable=false
|
# download, presentation, image upload and websocket
|
||||||
--o:ssl.termination=true
|
"~ ^/cool" = {
|
||||||
--o:remote_font_config.url=${cfg.url}/apps/richdocuments/settings/fonts.json
|
proxyPass = cfgC.local;
|
||||||
--o:logging.level=information
|
extraConfig = commonWebsocketConfig;
|
||||||
'';
|
};
|
||||||
DONT_GEN_SSL_CERT = "1";
|
# Legacy websocket (for compatibility)
|
||||||
SLEEPFORDEBUGGER = "0";
|
"~ ^/lool" = {
|
||||||
};
|
proxyPass = cfgC.local;
|
||||||
extraOptions = [
|
extraConfig = commonWebsocketConfig;
|
||||||
"--cap-add"
|
};
|
||||||
"MKNOD"
|
# Admin Console websocket
|
||||||
];
|
"^~ /cool/adminws" = {
|
||||||
};
|
proxyPass = cfgC.local;
|
||||||
};
|
extraConfig = commonWebsocketConfig;
|
||||||
systemd = lib.mkIf cfg.enableCron {
|
};
|
||||||
services = {
|
# Legacy Admin Console websocket (for compatibility)
|
||||||
nextcloud-cron.path = [ pkgs.perl ];
|
"^~ /lool/adminws" = {
|
||||||
nextcloud-cronjob =
|
proxyPass = cfgC.local;
|
||||||
let
|
extraConfig = commonWebsocketConfig;
|
||||||
inherit (inputs.jawz-scripts.packages.x86_64-linux) nextcloud-cronjob;
|
|
||||||
in
|
|
||||||
{
|
|
||||||
description = "Runs various nextcloud-related cronjobs";
|
|
||||||
wantedBy = [ "multi-user.target" ];
|
|
||||||
path = [
|
|
||||||
pkgs.bash
|
|
||||||
nextcloud-cronjob
|
|
||||||
];
|
|
||||||
serviceConfig = {
|
|
||||||
Restart = "on-failure";
|
|
||||||
RestartSec = 30;
|
|
||||||
ExecStart = "${nextcloud-cronjob}/bin/nextcloud-cronjob";
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
};
|
|
||||||
timers.nextcloud-cronjob = {
|
|
||||||
enable = true;
|
|
||||||
description = "Runs various nextcloud-related cronjobs";
|
|
||||||
wantedBy = [ "timers.target" ];
|
|
||||||
timerConfig = {
|
|
||||||
OnCalendar = "*:0/10";
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
})
|
||||||
};
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ in
|
|||||||
secure = true;
|
secure = true;
|
||||||
expire = "168h";
|
expire = "168h";
|
||||||
refresh = "1h";
|
refresh = "1h";
|
||||||
domain = ".lebubu.org";
|
domain = ".${config.my.domain}";
|
||||||
secret = config.sops.secrets.oauth2-proxy-cookie.path;
|
secret = config.sops.secrets.oauth2-proxy-cookie.path;
|
||||||
};
|
};
|
||||||
extraConfig = {
|
extraConfig = {
|
||||||
@@ -53,7 +53,7 @@ in
|
|||||||
session-store-type = "cookie";
|
session-store-type = "cookie";
|
||||||
skip-provider-button = true;
|
skip-provider-button = true;
|
||||||
code-challenge-method = "S256";
|
code-challenge-method = "S256";
|
||||||
whitelist-domain = [ ".lebubu.org" ];
|
whitelist-domain = [ ".${config.my.domain}" ];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,24 +0,0 @@
|
|||||||
{
|
|
||||||
lib,
|
|
||||||
config,
|
|
||||||
...
|
|
||||||
}:
|
|
||||||
let
|
|
||||||
setup = import ../factories/mkserver.nix { inherit lib config; };
|
|
||||||
cfg = config.my.servers.plex-discord-bot;
|
|
||||||
name = "plex-discord-bot";
|
|
||||||
in
|
|
||||||
{
|
|
||||||
options.my.servers.plex-discord-bot = setup.mkOptions name name 0;
|
|
||||||
config.virtualisation.oci-containers.containers.plex-discord-bot = lib.mkIf cfg.enable {
|
|
||||||
image = "ghcr.io/phin05/discord-rich-presence-plex:latest";
|
|
||||||
environment = {
|
|
||||||
DRPP_UID = toString config.users.users.jawz.uid;
|
|
||||||
DRPP_GID = toString config.users.groups.users.gid;
|
|
||||||
};
|
|
||||||
volumes = [
|
|
||||||
"${config.my.containerData}/drpp:/app/data"
|
|
||||||
"/run/user/${toString config.users.users.jawz.uid}:/run/app"
|
|
||||||
];
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -9,51 +9,52 @@ let
|
|||||||
in
|
in
|
||||||
{
|
{
|
||||||
options.my.servers.plex = setup.mkOptions "plex" "plex" 32400;
|
options.my.servers.plex = setup.mkOptions "plex" "plex" 32400;
|
||||||
config = lib.mkIf (cfg.enable && config.my.secureHost) {
|
config = lib.mkMerge [
|
||||||
users.users.plex = {
|
(lib.mkIf (cfg.enable && config.my.secureHost) {
|
||||||
uid = 193;
|
users.users.plex = {
|
||||||
group = "piracy";
|
uid = 193;
|
||||||
isSystemUser = true;
|
group = "piracy";
|
||||||
};
|
isSystemUser = true;
|
||||||
services = {
|
};
|
||||||
plex = {
|
services.plex = {
|
||||||
inherit (cfg) enable;
|
inherit (cfg) enable;
|
||||||
group = "piracy";
|
group = "piracy";
|
||||||
};
|
};
|
||||||
nginx = lib.mkIf cfg.enableProxy {
|
})
|
||||||
virtualHosts."${cfg.host}" = {
|
(lib.mkIf (cfg.enableProxy && config.my.enableProxy) {
|
||||||
forceSSL = true;
|
my.servers.plex.useDefaultProxy = false;
|
||||||
enableACME = true;
|
services.nginx.virtualHosts."${cfg.host}" = {
|
||||||
http2 = true;
|
forceSSL = true;
|
||||||
serverAliases = [
|
enableACME = true;
|
||||||
"plex.rotehaare.art"
|
http2 = true;
|
||||||
];
|
serverAliases = [
|
||||||
extraConfig = ''
|
"plex.rotehaare.art"
|
||||||
# Some players don't reopen a socket and playback stops totally instead of resuming after an extended pause
|
];
|
||||||
send_timeout 100m;
|
extraConfig = ''
|
||||||
# Plex headers
|
# Some players don't reopen a socket and playback stops totally instead of resuming after an extended pause
|
||||||
proxy_set_header X-Plex-Client-Identifier $http_x_plex_client_identifier;
|
send_timeout 100m;
|
||||||
proxy_set_header X-Plex-Device $http_x_plex_device;
|
# Plex headers
|
||||||
proxy_set_header X-Plex-Device-Name $http_x_plex_device_name;
|
proxy_set_header X-Plex-Client-Identifier $http_x_plex_client_identifier;
|
||||||
proxy_set_header X-Plex-Platform $http_x_plex_platform;
|
proxy_set_header X-Plex-Device $http_x_plex_device;
|
||||||
proxy_set_header X-Plex-Platform-Version $http_x_plex_platform_version;
|
proxy_set_header X-Plex-Device-Name $http_x_plex_device_name;
|
||||||
proxy_set_header X-Plex-Product $http_x_plex_product;
|
proxy_set_header X-Plex-Platform $http_x_plex_platform;
|
||||||
proxy_set_header X-Plex-Token $http_x_plex_token;
|
proxy_set_header X-Plex-Platform-Version $http_x_plex_platform_version;
|
||||||
proxy_set_header X-Plex-Version $http_x_plex_version;
|
proxy_set_header X-Plex-Product $http_x_plex_product;
|
||||||
proxy_set_header X-Plex-Nocache $http_x_plex_nocache;
|
proxy_set_header X-Plex-Token $http_x_plex_token;
|
||||||
proxy_set_header X-Plex-Provides $http_x_plex_provides;
|
proxy_set_header X-Plex-Version $http_x_plex_version;
|
||||||
proxy_set_header X-Plex-Device-Vendor $http_x_plex_device_vendor;
|
proxy_set_header X-Plex-Nocache $http_x_plex_nocache;
|
||||||
proxy_set_header X-Plex-Model $http_x_plex_model;
|
proxy_set_header X-Plex-Provides $http_x_plex_provides;
|
||||||
# Buffering off send to the client as soon as the data is received from Plex.
|
proxy_set_header X-Plex-Device-Vendor $http_x_plex_device_vendor;
|
||||||
proxy_redirect off;
|
proxy_set_header X-Plex-Model $http_x_plex_model;
|
||||||
proxy_buffering off;
|
# Buffering off send to the client as soon as the data is received from Plex.
|
||||||
'';
|
proxy_redirect off;
|
||||||
locations."/" = {
|
proxy_buffering off;
|
||||||
proxyPass = cfg.local;
|
'';
|
||||||
proxyWebsockets = true;
|
locations."/" = {
|
||||||
};
|
proxyPass = cfg.local;
|
||||||
|
proxyWebsockets = true;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
})
|
||||||
};
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,22 +0,0 @@
|
|||||||
{
|
|
||||||
config,
|
|
||||||
lib,
|
|
||||||
...
|
|
||||||
}:
|
|
||||||
let
|
|
||||||
setup = import ../factories/mkserver.nix { inherit lib config; };
|
|
||||||
cfg = config.my.websites.portfolio;
|
|
||||||
in
|
|
||||||
{
|
|
||||||
options.my.websites.portfolio = setup.mkOptions "portfolio" "portfolio" 0;
|
|
||||||
config.services.nginx.virtualHosts."danilo-reyes.com" = lib.mkIf cfg.enableProxy {
|
|
||||||
forceSSL = true;
|
|
||||||
enableACME = true;
|
|
||||||
http2 = true;
|
|
||||||
root = "/srv/www/danilo-reyes.com";
|
|
||||||
# index = "index.html";
|
|
||||||
locations."/".extraConfig = ''
|
|
||||||
try_files $uri $uri/ =404;
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -37,6 +37,7 @@ let
|
|||||||
"mealie"
|
"mealie"
|
||||||
"nextcloud"
|
"nextcloud"
|
||||||
"paperless"
|
"paperless"
|
||||||
|
"plausible"
|
||||||
"shiori"
|
"shiori"
|
||||||
"sonarqube"
|
"sonarqube"
|
||||||
"vaultwarden"
|
"vaultwarden"
|
||||||
|
|||||||
@@ -9,19 +9,29 @@ let
|
|||||||
in
|
in
|
||||||
{
|
{
|
||||||
options.my.servers.prowlarr = setup.mkOptions "prowlarr" "indexer" 9696;
|
options.my.servers.prowlarr = setup.mkOptions "prowlarr" "indexer" 9696;
|
||||||
config = lib.mkIf cfg.enable {
|
config = lib.mkMerge [
|
||||||
users.users.prowlarr = {
|
(lib.mkIf cfg.enable {
|
||||||
uid = 987;
|
users.users.prowlarr = {
|
||||||
group = "piracy";
|
uid = 987;
|
||||||
isSystemUser = true;
|
group = "piracy";
|
||||||
};
|
isSystemUser = true;
|
||||||
services = {
|
|
||||||
prowlarr = {
|
|
||||||
inherit (cfg) enable;
|
|
||||||
};
|
};
|
||||||
flaresolverr = {
|
services = {
|
||||||
inherit (cfg) enable;
|
prowlarr = {
|
||||||
|
inherit (cfg) enable;
|
||||||
|
};
|
||||||
|
flaresolverr = {
|
||||||
|
inherit (cfg) enable;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
})
|
||||||
};
|
(lib.mkIf (cfg.enableProxy && config.my.enableProxy && config.my.secureHost) {
|
||||||
|
sops.secrets."private-ca/pem" = {
|
||||||
|
sopsFile = ../../secrets/certs.yaml;
|
||||||
|
owner = "nginx";
|
||||||
|
group = "nginx";
|
||||||
|
};
|
||||||
|
my.servers.prowlarr.certPath = config.sops.secrets."private-ca/pem".path;
|
||||||
|
})
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,46 +29,56 @@ let
|
|||||||
in
|
in
|
||||||
{
|
{
|
||||||
options.my.servers.stash = setup.mkOptions "stash" "xxx" 9999;
|
options.my.servers.stash = setup.mkOptions "stash" "xxx" 9999;
|
||||||
config = lib.mkIf (cfg.enable && config.my.secureHost) {
|
config = lib.mkMerge [
|
||||||
sops.secrets = {
|
(lib.mkIf (cfg.enable && config.my.secureHost) {
|
||||||
"stash/password".sopsFile = ../../secrets/secrets.yaml;
|
sops.secrets = {
|
||||||
"stash/jwt".sopsFile = ../../secrets/secrets.yaml;
|
"stash/password".sopsFile = ../../secrets/secrets.yaml;
|
||||||
"stash/session".sopsFile = ../../secrets/secrets.yaml;
|
"stash/jwt".sopsFile = ../../secrets/secrets.yaml;
|
||||||
};
|
"stash/session".sopsFile = ../../secrets/secrets.yaml;
|
||||||
services.stash = {
|
|
||||||
inherit (cfg) enable;
|
|
||||||
group = "glue";
|
|
||||||
mutableSettings = true;
|
|
||||||
username = "Suing8150";
|
|
||||||
passwordFile = config.sops.secrets."stash/password".path;
|
|
||||||
jwtSecretKeyFile = config.sops.secrets."stash/jwt".path;
|
|
||||||
sessionStoreKeyFile = config.sops.secrets."stash/session".path;
|
|
||||||
settings = {
|
|
||||||
inherit (cfg) port;
|
|
||||||
host = "0.0.0.0";
|
|
||||||
stash = [
|
|
||||||
{
|
|
||||||
path = "/srv/pool/glue/";
|
|
||||||
}
|
|
||||||
];
|
|
||||||
};
|
};
|
||||||
};
|
services.stash = {
|
||||||
systemd.services.stash = {
|
inherit (cfg) enable;
|
||||||
environment = {
|
group = "glue";
|
||||||
PYTHONPATH = "/var/lib/stash/venv/lib/python3.12/site-packages";
|
mutableSettings = true;
|
||||||
LD_LIBRARY_PATH = "${pkgs.stdenv.cc.cc.lib}/lib:${pkgs.glibc}/lib:${pkgs.zlib}/lib:${pkgs.libffi}/lib:${pkgs.openssl}/lib";
|
username = "Suing8150";
|
||||||
|
passwordFile = config.sops.secrets."stash/password".path;
|
||||||
|
jwtSecretKeyFile = config.sops.secrets."stash/jwt".path;
|
||||||
|
sessionStoreKeyFile = config.sops.secrets."stash/session".path;
|
||||||
|
settings = {
|
||||||
|
inherit (cfg) port;
|
||||||
|
host = "0.0.0.0";
|
||||||
|
stash = [
|
||||||
|
{
|
||||||
|
path = "/srv/pool/glue/";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
};
|
};
|
||||||
serviceConfig = {
|
systemd.services.stash = {
|
||||||
PrivateUsers = lib.mkForce false;
|
environment = {
|
||||||
BindReadOnlyPaths = lib.mkForce [ ];
|
PYTHONPATH = "/var/lib/stash/venv/lib/python3.12/site-packages";
|
||||||
BindPaths = lib.mkIf (cfgS.settings != { }) (map (stash: "${stash.path}") cfgS.settings.stash);
|
LD_LIBRARY_PATH = "${pkgs.stdenv.cc.cc.lib}/lib:${pkgs.glibc}/lib:${pkgs.zlib}/lib:${pkgs.libffi}/lib:${pkgs.openssl}/lib";
|
||||||
|
};
|
||||||
|
serviceConfig = {
|
||||||
|
PrivateUsers = lib.mkForce false;
|
||||||
|
BindReadOnlyPaths = lib.mkForce [ ];
|
||||||
|
BindPaths = lib.mkIf (cfgS.settings != { }) (map (stash: "${stash.path}") cfgS.settings.stash);
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
users.users.stash = {
|
||||||
users.users.stash = {
|
uid = 974;
|
||||||
uid = 974;
|
isSystemUser = true;
|
||||||
isSystemUser = true;
|
group = "glue";
|
||||||
group = "glue";
|
packages = [ stashPythonFHS ];
|
||||||
packages = [ stashPythonFHS ];
|
};
|
||||||
};
|
})
|
||||||
};
|
(lib.mkIf (cfg.enableProxy && config.my.enableProxy && config.my.secureHost) {
|
||||||
|
sops.secrets."private-ca/pem" = {
|
||||||
|
sopsFile = ../../secrets/certs.yaml;
|
||||||
|
owner = "nginx";
|
||||||
|
group = "nginx";
|
||||||
|
};
|
||||||
|
my.servers.stash.certPath = config.sops.secrets."private-ca/pem".path;
|
||||||
|
})
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,42 +25,37 @@ in
|
|||||||
synapse = setup.mkOptions "synapse" "pYLemuAfsrzNBaH77xSu" 8008;
|
synapse = setup.mkOptions "synapse" "pYLemuAfsrzNBaH77xSu" 8008;
|
||||||
element = setup.mkOptions "element" "55a608953f6d64c199" 5345;
|
element = setup.mkOptions "element" "55a608953f6d64c199" 5345;
|
||||||
};
|
};
|
||||||
config = lib.mkIf (cfg.enable && config.my.secureHost) {
|
config = lib.mkMerge [
|
||||||
my.servers = {
|
(lib.mkIf (cfg.enable && config.my.secureHost) {
|
||||||
synapse = { inherit domain; };
|
my.servers = {
|
||||||
element = { inherit domain; };
|
synapse = { inherit domain; };
|
||||||
};
|
element = { inherit domain; };
|
||||||
users.groups.matrix-synapse = { inherit gid; };
|
};
|
||||||
users.users.matrix-synapse = {
|
users.groups.matrix-synapse = { inherit gid; };
|
||||||
inherit uid;
|
users.users.matrix-synapse = {
|
||||||
isSystemUser = true;
|
inherit uid;
|
||||||
group = "matrix-synapse";
|
isSystemUser = true;
|
||||||
};
|
|
||||||
sops.secrets = {
|
|
||||||
synapse = {
|
|
||||||
sopsFile = ../../secrets/env.yaml;
|
|
||||||
owner = "matrix-synapse";
|
|
||||||
group = "matrix-synapse";
|
group = "matrix-synapse";
|
||||||
};
|
};
|
||||||
"iqQCY4iAWO-ca/pem" = {
|
sops.secrets = {
|
||||||
sopsFile = ../../secrets/certs.yaml;
|
synapse = {
|
||||||
owner = "nginx";
|
sopsFile = ../../secrets/env.yaml;
|
||||||
group = "nginx";
|
owner = "matrix-synapse";
|
||||||
|
group = "matrix-synapse";
|
||||||
|
};
|
||||||
|
"matrix/key" = {
|
||||||
|
sopsFile = ../../secrets/certs.yaml;
|
||||||
|
owner = "matrix-synapse";
|
||||||
|
group = "matrix-synapse";
|
||||||
|
};
|
||||||
|
"matrix/cert" = {
|
||||||
|
sopsFile = ../../secrets/certs.yaml;
|
||||||
|
owner = "matrix-synapse";
|
||||||
|
group = "matrix-synapse";
|
||||||
|
};
|
||||||
};
|
};
|
||||||
"matrix/key" = {
|
networking.firewall.allowedTCPPorts = lib.mkIf (!cfg.isLocal) [ cfg.port ];
|
||||||
sopsFile = ../../secrets/certs.yaml;
|
services.matrix-synapse = {
|
||||||
owner = "matrix-synapse";
|
|
||||||
group = "matrix-synapse";
|
|
||||||
};
|
|
||||||
"matrix/cert" = {
|
|
||||||
sopsFile = ../../secrets/certs.yaml;
|
|
||||||
owner = "matrix-synapse";
|
|
||||||
group = "matrix-synapse";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
networking.firewall.allowedTCPPorts = lib.mkIf (!cfg.isLocal) [ cfg.port ];
|
|
||||||
services = {
|
|
||||||
matrix-synapse = {
|
|
||||||
inherit (cfg) enable;
|
inherit (cfg) enable;
|
||||||
extraConfigFiles = [
|
extraConfigFiles = [
|
||||||
config.sops.secrets.synapse.path
|
config.sops.secrets.synapse.path
|
||||||
@@ -80,7 +75,6 @@ in
|
|||||||
inherit (cfg) port;
|
inherit (cfg) port;
|
||||||
bind_addresses = [
|
bind_addresses = [
|
||||||
config.my.localhost
|
config.my.localhost
|
||||||
config.my.localhost6
|
|
||||||
config.my.ips.server
|
config.my.ips.server
|
||||||
config.my.ips.wg-server
|
config.my.ips.wg-server
|
||||||
];
|
];
|
||||||
@@ -100,7 +94,18 @@ in
|
|||||||
];
|
];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
nginx.virtualHosts = lib.mkIf cfg.enableProxy {
|
})
|
||||||
|
(lib.mkIf (cfg.enableProxy && config.my.enableProxy) {
|
||||||
|
sops.secrets."iqQCY4iAWO-ca/pem" = {
|
||||||
|
sopsFile = ../../secrets/certs.yaml;
|
||||||
|
owner = "nginx";
|
||||||
|
group = "nginx";
|
||||||
|
};
|
||||||
|
my.servers.synapse = {
|
||||||
|
useDefaultProxy = false;
|
||||||
|
certPath = config.sops.secrets."iqQCY4iAWO-ca/pem".path;
|
||||||
|
};
|
||||||
|
services.nginx.virtualHosts = {
|
||||||
"${cfgE.host}" = {
|
"${cfgE.host}" = {
|
||||||
enableACME = true;
|
enableACME = true;
|
||||||
forceSSL = true;
|
forceSSL = true;
|
||||||
@@ -125,13 +130,8 @@ in
|
|||||||
"/_matrix".proxyPass = "http://[${config.my.localhost6}]:${toString cfg.port}";
|
"/_matrix".proxyPass = "http://[${config.my.localhost6}]:${toString cfg.port}";
|
||||||
"/_synapse/client".proxyPass = "http://[${config.my.localhost6}]:${toString cfg.port}";
|
"/_synapse/client".proxyPass = "http://[${config.my.localhost6}]:${toString cfg.port}";
|
||||||
};
|
};
|
||||||
# extraConfig = ''
|
|
||||||
# ssl_verify_client on;
|
|
||||||
# ssl_client_certificate ${config.sops.secrets."iqQCY4iAWO-ca/pem".path};
|
|
||||||
# error_page 403 /403.html;
|
|
||||||
# '';
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
})
|
||||||
};
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,9 +12,9 @@ in
|
|||||||
config = lib.mkIf (cfg.enable && config.my.servers.postgres.enable && config.my.secureHost) {
|
config = lib.mkIf (cfg.enable && config.my.servers.postgres.enable && config.my.secureHost) {
|
||||||
sops.secrets.yamtrack.sopsFile = ../../secrets/env.yaml;
|
sops.secrets.yamtrack.sopsFile = ../../secrets/env.yaml;
|
||||||
virtualisation.oci-containers.containers = {
|
virtualisation.oci-containers.containers = {
|
||||||
yamtrack-redis.image = "redis:7-alpine";
|
yamtrack-redis.image = "redis:latest";
|
||||||
yamtrack = {
|
yamtrack = {
|
||||||
image = "ghcr.io/fuzzygrim/yamtrack";
|
image = "ghcr.io/fuzzygrim/yamtrack:latest";
|
||||||
ports = [ "${toString cfg.port}:8000" ];
|
ports = [ "${toString cfg.port}:8000" ];
|
||||||
dependsOn = [ "yamtrack-redis" ];
|
dependsOn = [ "yamtrack-redis" ];
|
||||||
environmentFiles = [ config.sops.secrets.yamtrack.path ];
|
environmentFiles = [ config.sops.secrets.yamtrack.path ];
|
||||||
|
|||||||
@@ -1,10 +1,23 @@
|
|||||||
{ config, lib, ... }:
|
{ config, lib, ... }:
|
||||||
|
let
|
||||||
|
stripCidr = cidr: cidr |> lib.splitString "/" |> builtins.head;
|
||||||
|
wgListenIps = config.my.wgInterfaces |> builtins.attrValues;
|
||||||
|
wgListenAddrs = wgListenIps |> builtins.map (ip: "${stripCidr ip}:53");
|
||||||
|
in
|
||||||
{
|
{
|
||||||
options.my.services.network.enable = lib.mkEnableOption "network configuration and services";
|
options.my.services.network.enable = lib.mkEnableOption "network configuration and services";
|
||||||
config = lib.mkIf config.my.services.network.enable {
|
config = lib.mkIf config.my.services.network.enable {
|
||||||
networking = {
|
networking = {
|
||||||
enableIPv6 = true;
|
enableIPv6 = true;
|
||||||
firewall.enable = true;
|
firewall = {
|
||||||
|
enable = true;
|
||||||
|
interfaces = lib.mkIf config.my.services.wireguard.enable {
|
||||||
|
wg0 = {
|
||||||
|
allowedTCPPorts = [ 53 ];
|
||||||
|
allowedUDPPorts = [ 53 ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
dhcpcd.extraConfig = "nohook resolv.conf";
|
dhcpcd.extraConfig = "nohook resolv.conf";
|
||||||
networkmanager = {
|
networkmanager = {
|
||||||
enable = true;
|
enable = true;
|
||||||
@@ -19,6 +32,16 @@
|
|||||||
settings = {
|
settings = {
|
||||||
ipv6_servers = true;
|
ipv6_servers = true;
|
||||||
require_dnssec = true;
|
require_dnssec = true;
|
||||||
|
log_level = 4;
|
||||||
|
listen_addresses = [
|
||||||
|
"${config.my.localhost}:53"
|
||||||
|
"${config.my.localhost6}:53"
|
||||||
|
]
|
||||||
|
++ lib.optionals config.my.services.wireguard.enable wgListenAddrs;
|
||||||
|
query_log = {
|
||||||
|
file = "/var/lib/dnscrypt-proxy/query.log";
|
||||||
|
format = "tsv";
|
||||||
|
};
|
||||||
sources.public-resolvers = {
|
sources.public-resolvers = {
|
||||||
urls = [
|
urls = [
|
||||||
"https://raw.githubusercontent.com/DNSCrypt/dnscrypt-resolvers/master/v3/public-resolvers.md"
|
"https://raw.githubusercontent.com/DNSCrypt/dnscrypt-resolvers/master/v3/public-resolvers.md"
|
||||||
|
|||||||
@@ -1,51 +1,68 @@
|
|||||||
{
|
{
|
||||||
config,
|
config,
|
||||||
lib,
|
lib,
|
||||||
pkgs,
|
|
||||||
...
|
...
|
||||||
}:
|
}:
|
||||||
let
|
let
|
||||||
port = 51820;
|
port = 51820;
|
||||||
interface = config.my.interfaces.${config.networking.hostName};
|
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
options.my.services.wireguard.enable = lib.mkEnableOption "WireGuard VPN configuration";
|
options.my.services.wireguard.enable = lib.mkEnableOption "WireGuard VPN configuration";
|
||||||
config = lib.mkIf (config.my.services.wireguard.enable && config.my.secureHost) {
|
config = lib.mkIf (config.my.services.wireguard.enable && config.my.secureHost) {
|
||||||
sops.secrets."wireguard/private".sopsFile = ../../secrets/wireguard.yaml;
|
sops.secrets."vps/server/private".sopsFile = ../../secrets/wireguard.yaml;
|
||||||
networking = {
|
networking = {
|
||||||
firewall.allowedUDPPorts = [ port ];
|
firewall.allowedUDPPorts = [ port ];
|
||||||
nat = {
|
|
||||||
enable = true;
|
|
||||||
externalInterface = interface;
|
|
||||||
internalInterfaces = [ "wg0" ];
|
|
||||||
};
|
|
||||||
wireguard.interfaces.wg0 = {
|
wireguard.interfaces.wg0 = {
|
||||||
ips = [ "10.100.0.1/24" ];
|
ips = [
|
||||||
|
config.my.wgInterfaces.wg-homelab
|
||||||
|
config.my.wgInterfaces.wg-friends
|
||||||
|
config.my.wgInterfaces.wg-guests
|
||||||
|
];
|
||||||
listenPort = port;
|
listenPort = port;
|
||||||
postSetup = ''
|
postSetup = "";
|
||||||
${pkgs.iptables}/bin/iptables -t nat -A POSTROUTING -s 10.100.0.0/24 -o ${interface} -j MASQUERADE
|
postShutdown = "";
|
||||||
'';
|
privateKeyFile = config.sops.secrets."vps/server/private".path;
|
||||||
postShutdown = ''
|
|
||||||
${pkgs.iptables}/bin/iptables -t nat -D POSTROUTING -s 10.100.0.0/24 -o ${interface} -j MASQUERADE
|
|
||||||
'';
|
|
||||||
privateKeyFile = config.sops.secrets."wireguard/private".path;
|
|
||||||
peers = [
|
peers = [
|
||||||
{
|
{
|
||||||
publicKey = "ciupBjCcIpd3K5vlzNMJC8iiyNqB9xXwkSC6UXPKP3g=";
|
publicKey = "OUiqluRaS4hmGvLJ3csQrnIM3Zzet50gsqtTABaUkH4=";
|
||||||
allowedIPs = [ "10.100.0.2/32" ];
|
allowedIPs = [ "${config.my.ips.wg-server}/32" ];
|
||||||
} # phone
|
}
|
||||||
{
|
{
|
||||||
publicKey = "JgeA1ElDwR7oLmyGn8RzvxiscMBhR8+L+mEjY1Cq7gk=";
|
publicKey = "BwN4uCkMd6eAS5Ugld0oXnA16IhgEEQF8mOJ3+vHliA=";
|
||||||
allowedIPs = [ "10.100.0.3/32" ];
|
allowedIPs = [ "${config.my.ips.wg-galaxy}/32" ];
|
||||||
} # tablet
|
}
|
||||||
{
|
{
|
||||||
publicKey = "giPVRUTLtqPGb57R4foGZMNS0tjIp2ry6lMKYtqHjn4=";
|
publicKey = "R1xUFOuboQf/yy8ShiXqoCPaPcH3Cn0n4PAWB2rgHTs=";
|
||||||
allowedIPs = [ "10.100.0.15/32" ];
|
allowedIPs = [ "${config.my.ips.wg-phone}/32" ];
|
||||||
} # jeancarlos
|
}
|
||||||
{
|
{
|
||||||
publicKey = "92JdW/NExg1tUE4cEyl6Yn+0Eex+iFVA37ahPRhRnRM=";
|
publicKey = "rFgT6TXzRazK6GMazMNGjtOvzAAPST0LvCfN7QXsLho=";
|
||||||
allowedIPs = [ "10.100.0.16/32" ];
|
allowedIPs = [ "${config.my.ips.wg-friend1}/32" ];
|
||||||
} # gorilia
|
}
|
||||||
|
{
|
||||||
|
publicKey = "R1CTx5+CXivMI6ZEmRYsyFUFILhe6Qnub0iEIRvvrEY=";
|
||||||
|
allowedIPs = [ "${config.my.ips.wg-friend2}/32" ];
|
||||||
|
}
|
||||||
|
{
|
||||||
|
publicKey = "ecPNSacD6yVwpnLBs171z0xkw9M1DXKh/Kn70cIBcwA=";
|
||||||
|
allowedIPs = [ "${config.my.ips.wg-friend3}/32" ];
|
||||||
|
}
|
||||||
|
{
|
||||||
|
publicKey = "yg+2miZCrx89znFaUlU/le/7UIPgEAMY74fZfEwz8g4=";
|
||||||
|
allowedIPs = [ "${config.my.ips.wg-friend4}/32" ];
|
||||||
|
}
|
||||||
|
{
|
||||||
|
publicKey = "u4/6ZYO7lUJZ9QmSlFPUaadq25gwDljjhsfgs/p2amc=";
|
||||||
|
allowedIPs = [ "${config.my.ips.wg-friend5}/32" ];
|
||||||
|
}
|
||||||
|
{
|
||||||
|
publicKey = "GawtOvsZ75avelIri5CjGoPXd8AFpi9qlZ6dSsqUISE=";
|
||||||
|
allowedIPs = [ "${config.my.ips.wg-guest1}/32" ];
|
||||||
|
}
|
||||||
|
{
|
||||||
|
publicKey = "NvhUnErIb0/hi+Hui/o5l5Pq4ZysFVIn1VBPsjoTeCk=";
|
||||||
|
allowedIPs = [ "${config.my.ips.wg-guest2}/32" ];
|
||||||
|
}
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
44
modules/websites/lidarr-mb-report.nix
Normal file
44
modules/websites/lidarr-mb-report.nix
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
{
|
||||||
|
lib,
|
||||||
|
config,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
cfg = config.my.websites.lidarrMbReport;
|
||||||
|
mbSecurityHeaders = ''
|
||||||
|
add_header X-Content-Type-Options "nosniff" always;
|
||||||
|
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||||
|
'';
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options.my.websites.lidarrMbReport = {
|
||||||
|
enableProxy = lib.mkEnableOption "lidarr mb report static site";
|
||||||
|
};
|
||||||
|
config = lib.mkIf (cfg.enableProxy && config.my.enableProxy) {
|
||||||
|
services.nginx.virtualHosts."mb-report.lebubu.org" = {
|
||||||
|
forceSSL = true;
|
||||||
|
enableACME = true;
|
||||||
|
root = "/var/www/html/lidarr-mb-gap";
|
||||||
|
locations = {
|
||||||
|
"/" = {
|
||||||
|
extraConfig = ''
|
||||||
|
try_files $uri /missing_albums.html;
|
||||||
|
${mbSecurityHeaders}
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
"~* \\.html$" = {
|
||||||
|
extraConfig = ''
|
||||||
|
add_header Content-Type "text/html; charset=utf-8";
|
||||||
|
${mbSecurityHeaders}
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
"~* \\.json$" = {
|
||||||
|
extraConfig = ''
|
||||||
|
add_header Content-Type "application/json";
|
||||||
|
${mbSecurityHeaders}
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
98
modules/websites/portfolio.nix
Normal file
98
modules/websites/portfolio.nix
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
{
|
||||||
|
lib,
|
||||||
|
config,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
cfg = config.my.websites.portfolio;
|
||||||
|
issoCfg = config.my.servers.isso;
|
||||||
|
hugoSecurityHeaders = ''
|
||||||
|
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||||
|
add_header X-Content-Type-Options "nosniff" always;
|
||||||
|
add_header X-XSS-Protection "1; mode=block" always;
|
||||||
|
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
|
||||||
|
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
|
||||||
|
'';
|
||||||
|
hugoLocations = {
|
||||||
|
"/" = {
|
||||||
|
extraConfig = ''
|
||||||
|
try_files $uri $uri/ /index.html;
|
||||||
|
${hugoSecurityHeaders}
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
"~* \\.html$" = {
|
||||||
|
extraConfig = ''
|
||||||
|
try_files $uri $uri/ /index.html;
|
||||||
|
${hugoSecurityHeaders}
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
"~* \\.(jpg|jpeg|png|gif|ico|css|js|svg|woff|woff2|ttf|xml)$" = {
|
||||||
|
extraConfig = ''
|
||||||
|
expires 1y;
|
||||||
|
add_header Cache-Control "public, max-age=31536000, immutable";
|
||||||
|
${hugoSecurityHeaders}
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
"~ /\\.(?!well-known).*" = {
|
||||||
|
extraConfig = ''
|
||||||
|
return 404;
|
||||||
|
${hugoSecurityHeaders}
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
"= /js/script.js" = {
|
||||||
|
proxyPass = "https://analytics.lebubu.org";
|
||||||
|
extraConfig = ''
|
||||||
|
proxy_set_header Host analytics.lebubu.org;
|
||||||
|
rewrite ^ /js/script.file-downloads.hash.outbound-links.js break;
|
||||||
|
${hugoSecurityHeaders}
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
"= /api/event" = {
|
||||||
|
proxyPass = "https://analytics.lebubu.org";
|
||||||
|
extraConfig = ''
|
||||||
|
proxy_set_header Host analytics.lebubu.org;
|
||||||
|
${hugoSecurityHeaders}
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options.my.websites.portfolio = {
|
||||||
|
enableProxy = lib.mkEnableOption "portfolio and blog static sites";
|
||||||
|
};
|
||||||
|
config = lib.mkIf (cfg.enableProxy && config.my.enableProxy) {
|
||||||
|
services.nginx.virtualHosts = {
|
||||||
|
"www.danilo-reyes.com" = {
|
||||||
|
forceSSL = true;
|
||||||
|
enableACME = true;
|
||||||
|
globalRedirect = "danilo-reyes.com";
|
||||||
|
};
|
||||||
|
"www.blog.danilo-reyes.com" = {
|
||||||
|
forceSSL = true;
|
||||||
|
enableACME = true;
|
||||||
|
globalRedirect = "blog.danilo-reyes.com";
|
||||||
|
};
|
||||||
|
"danilo-reyes.com" = {
|
||||||
|
forceSSL = true;
|
||||||
|
enableACME = true;
|
||||||
|
root = "/var/www/html/portfolio";
|
||||||
|
locations = hugoLocations;
|
||||||
|
};
|
||||||
|
"blog.danilo-reyes.com" = {
|
||||||
|
forceSSL = true;
|
||||||
|
enableACME = true;
|
||||||
|
root = "/var/www/html/blog";
|
||||||
|
locations = hugoLocations // {
|
||||||
|
"^~ /isso" = {
|
||||||
|
proxyPass = "http://${issoCfg.ip}:${toString issoCfg.port}";
|
||||||
|
extraConfig = ''
|
||||||
|
rewrite ^/isso/?(.*)$ /$1 break;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
${hugoSecurityHeaders}
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -196,6 +196,13 @@ in
|
|||||||
inherit ip;
|
inherit ip;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
mkEnabledProxyIp = ip: name: {
|
||||||
|
inherit name;
|
||||||
|
value = {
|
||||||
|
enableProxy = true;
|
||||||
|
inherit ip;
|
||||||
|
};
|
||||||
|
};
|
||||||
enableList = func: list: list |> map func |> builtins.listToAttrs;
|
enableList = func: list: list |> map func |> builtins.listToAttrs;
|
||||||
mkPostgresDependency = config: serviceName: displayName: {
|
mkPostgresDependency = config: serviceName: displayName: {
|
||||||
assertion = config.my.servers.${serviceName}.enable -> config.my.servers.postgres.enable;
|
assertion = config.my.servers.${serviceName}.enable -> config.my.servers.postgres.enable;
|
||||||
@@ -217,7 +224,6 @@ in
|
|||||||
nixworkstation = ../secrets/ssh/ed25519_nixworkstation.pub;
|
nixworkstation = ../secrets/ssh/ed25519_nixworkstation.pub;
|
||||||
nixserver = ../secrets/ssh/ed25519_nixserver.pub;
|
nixserver = ../secrets/ssh/ed25519_nixserver.pub;
|
||||||
nixminiserver = ../secrets/ssh/ed25519_nixminiserver.pub;
|
nixminiserver = ../secrets/ssh/ed25519_nixminiserver.pub;
|
||||||
windows_vm = ../secrets/ssh/ed25519_windows_vm.pub;
|
|
||||||
};
|
};
|
||||||
getSshKeys = keyNames: keyNames |> map (name: inputs.self.lib.sshKeys.${name});
|
getSshKeys = keyNames: keyNames |> map (name: inputs.self.lib.sshKeys.${name});
|
||||||
# Helper functions for multi-user toggle support
|
# Helper functions for multi-user toggle support
|
||||||
|
|||||||
@@ -26,11 +26,36 @@
|
|||||||
exec ${mcpPython}/bin/python -m mcp_server.server
|
exec ${mcpPython}/bin/python -m mcp_server.server
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
vpsLinodeConfig = inputs.self.nixosConfigurations.vps.extendModules {
|
||||||
|
modules = [
|
||||||
|
(
|
||||||
|
{ lib, ... }:
|
||||||
|
{
|
||||||
|
my.secureHost = lib.mkForce false;
|
||||||
|
my.build.baseImage = true;
|
||||||
|
}
|
||||||
|
)
|
||||||
|
];
|
||||||
|
};
|
||||||
|
mcpTests = pkgs.writeShellApplication {
|
||||||
|
name = "mcp-tests";
|
||||||
|
runtimeInputs = with pkgs.python3Packages; [
|
||||||
|
black
|
||||||
|
click
|
||||||
|
mypy
|
||||||
|
pytest
|
||||||
|
ruff
|
||||||
|
];
|
||||||
|
text = ''
|
||||||
|
exec bash ${inputs.self}/scripts/mcp-server/run-tests.sh "$@"
|
||||||
|
'';
|
||||||
|
};
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
packages = (inputs.jawz-scripts.packages.${system} or { }) // {
|
packages = (inputs.jawz-scripts.packages.${system} or { }) // {
|
||||||
emacs-vm = inputs.self.nixosConfigurations.emacs.config.system.build.vm;
|
emacs-vm = inputs.self.nixosConfigurations.emacs.config.system.build.vm;
|
||||||
vps-linode = inputs.self.nixosConfigurations.vps.config.system.build.images.linode;
|
vps-linode = vpsLinodeConfig.config.system.build.images.linode;
|
||||||
|
mcp-tests = mcpTests;
|
||||||
nixos-mcp = nixosMcp;
|
nixos-mcp = nixosMcp;
|
||||||
nixos-mcp-server = mcpServerPkg;
|
nixos-mcp-server = mcpServerPkg;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,6 +6,16 @@ set -euo pipefail
|
|||||||
here="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
here="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
cd "$here"
|
cd "$here"
|
||||||
|
|
||||||
|
cache_root="${TMPDIR:-/tmp}/mcp-tests-cache"
|
||||||
|
mkdir -p "$cache_root"
|
||||||
|
export HOME="${TMPDIR:-/tmp}/mcp-tests-home"
|
||||||
|
mkdir -p "$HOME"
|
||||||
|
export XDG_CACHE_HOME="$cache_root/xdg"
|
||||||
|
export RUFF_CACHE_DIR="$cache_root/ruff"
|
||||||
|
export MYPY_CACHE_DIR="$cache_root/mypy"
|
||||||
|
export PYTEST_ADDOPTS="${PYTEST_ADDOPTS:-} -o cache_dir=$cache_root/pytest"
|
||||||
|
export PYTHONDONTWRITEBYTECODE=1
|
||||||
|
|
||||||
fix=false
|
fix=false
|
||||||
for arg in "$@"; do
|
for arg in "$@"; do
|
||||||
if [ "$arg" = "--fix" ]; then
|
if [ "$arg" = "--fix" ]; then
|
||||||
|
|||||||
@@ -22,38 +22,38 @@ sops:
|
|||||||
- recipient: age1lufn6t35gs4wgevyr2gud4eec7lvkn7pgnnv4tja64ww3hef7gqq8fas37
|
- recipient: age1lufn6t35gs4wgevyr2gud4eec7lvkn7pgnnv4tja64ww3hef7gqq8fas37
|
||||||
enc: |
|
enc: |
|
||||||
-----BEGIN AGE ENCRYPTED FILE-----
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB5VUMzYjZ5WlZtQ05LdnVt
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBXdVVSeEtOTE1XQTBHVW5C
|
||||||
b3V3RmFyM0VZWmh4dC9YZFpsZkRIdC9TRzFrCnBuYnhSaUgwb3JuSUNFSWlwSmVq
|
aUVVbUltOGMrV2l6VGhRQXFnbUR1NVpnYmgwCjEzWXB5SVBtbjBzMkx4OUhkUXll
|
||||||
bEoyQ09XSjNBMks3M2ZYdlh0eDFNYjAKLS0tIERpaGhISDFYd3RCYUV6Y0lmdGNQ
|
b0FkcUl2b0d0YkEwQU9iNFZrcDJTV3MKLS0tIHNrY2JFbVEwNTFaWUdmdFJPZmJI
|
||||||
VTNibTBMN2RuN3doU3lYK1drNjVTVkkKMmRW0NtiYKBcUQ8kKjXcS6KjoPdVfN5d
|
SnhZK1h2ejhQUUNtbzFINUJmNGhiYVkKCMeBiPt80A8/ynEWy2e881y1tVnqANK+
|
||||||
6vczsKTTbUwI0n6T5xrwRdbVIFsP4HisjceQWxJIVBthR0u9dLfXGw==
|
wU9Bn+oRwoudPb1io9LAoTdu7+IQpLByt1phAju8m243nM48hAkipA==
|
||||||
-----END AGE ENCRYPTED FILE-----
|
-----END AGE ENCRYPTED FILE-----
|
||||||
- recipient: age17jlsydpgl35qx5ahc3exu44jt8dfa63chymt6xqp9xx0r6dh347qpg55cz
|
- recipient: age17jlsydpgl35qx5ahc3exu44jt8dfa63chymt6xqp9xx0r6dh347qpg55cz
|
||||||
enc: |
|
enc: |
|
||||||
-----BEGIN AGE ENCRYPTED FILE-----
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBvb1ZtMjV5TjlhMVRwdWNU
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBhUmQzMU9hTlpQbHBQMm9R
|
||||||
ME93Y0xhVGlxdGxmeWtXQ09EN3lORlJpV3k0CkJxdE14YXpwcytjbnZuMWpHVzZ3
|
Y0d5Z0lGTkFvYnc5MkRDbjNTMlUxWmI0U2t3CkdRQzdGTTNjbmprWEdYc0Nkckpr
|
||||||
dVVBYVE0RW1naWVVQ0JRY0NoWG9LZTQKLS0tIG1udE1GbEtTQ2o3bGl0SW9NZmtF
|
Z2xqYkhlcHlQNG0rRFVvVTFLdFQxWVUKLS0tIFNPS1o2UVZobU5xN0U2QnI4dXA5
|
||||||
OFNqTncyaHFUSzBNRzZiSTVBdkhFWVkK2v81N8c8cU1Ig9fQZOn0fltqO+Ej8Wtk
|
WWR1MWNGMVIyTGFBZXFyZlhwM09qakUK8Q26phHWY9zN5j6ZxB7+kmSgmcukfgiv
|
||||||
D0nMQv2fbWp6YlyE17VYPgmhdEY6+Zstve6PlBG86iQE3LTAfjG3Uw==
|
qAAzIGdgsvnUiFZCEJHD1D686C+ZxvakD4p9sA/zEIyeIBtKCq3lIA==
|
||||||
-----END AGE ENCRYPTED FILE-----
|
-----END AGE ENCRYPTED FILE-----
|
||||||
- recipient: age15hx530yrqmhm80vsjmffyg9deq9gssj7hl5rsqdnsn3dwegj9qusv4sjf5
|
- recipient: age15hx530yrqmhm80vsjmffyg9deq9gssj7hl5rsqdnsn3dwegj9qusv4sjf5
|
||||||
enc: |
|
enc: |
|
||||||
-----BEGIN AGE ENCRYPTED FILE-----
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBJSVdUWXRUa2tHVGczelhu
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBHR05GcVViZTY1Q0NaR1RG
|
||||||
UWk5RFl6azRJTkdxZGxvbWlnSDc3K3NlNlgwCjBRZEVta3RuNW1DZmo4RXJyTTNk
|
SWZzYjlxQUttS0tXc1ZDbDljbXczd1gxL25ZCkM3TVJ5NzlIdUx1dTc5d3R4U3BY
|
||||||
cnpxTDRGL0kwQXJmc29LNE0wV01hUGsKLS0tIGgyTWZrOHVNTGExRWtYMzJ1aXhp
|
RVBLazRRZ2F0anJZRXl3bFdsbXJhdVkKLS0tIFVXN21DQmtqZ2hIRi9FM2dtTTlw
|
||||||
cURNZXBtbnp2OUZDZDZKeEMrZlN0TEEKznlmLKFHYDm/hv3EPcHjT0A8r06GL7if
|
aHQvOUt5UGhhR2ZXMVFOOEtKMDRtZmcK0ZX5pF08o+HLztgL1/LocDGIcOGPKqXe
|
||||||
tbuJei8aWWg+uuvCBTZjHqmPUyNR1ixt84vxy1HlwXVu3dYHcG0Wug==
|
9bRBkUtr5QuIxQEYj6NenhkeIxRoPxK9Re/Vsqpphv4NqKpzyOujcw==
|
||||||
-----END AGE ENCRYPTED FILE-----
|
-----END AGE ENCRYPTED FILE-----
|
||||||
- recipient: age1ml3smrs5mwz4ds84gk0eyss86nwsmp07qh0npxsuae7lfwwpsghssavytw
|
- recipient: age1v2ahkl759cftpcdq4mla2cvmgz4jlnmgj7qtgc9732zxrfvxf3lq76zjpr
|
||||||
enc: |
|
enc: |
|
||||||
-----BEGIN AGE ENCRYPTED FILE-----
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBZYnlaTkd3LzFRbldWL3RZ
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBYcmxldjA2UllycEFyTnpW
|
||||||
N2ZneVIzMnRpVVNHVWRMdXJLdjQwSWFKS2pFCmlQZUZMbG03VFVuUXcxZ3NRWjVH
|
c2dIa1NXYVJSU1lwb0EvckxQQ0J4ckhSeDFBCnBTZGRYUzdSK08xeWFmaUM3SEZ3
|
||||||
SHVPYzk5NGpkeUVSU1BmQnNuaWZnZFUKLS0tIFdQZEU1YnhHZWRIajNYWTYxMEwr
|
bXJSUG1OVEU1T0Z5VGRqYUloa3k0RmsKLS0tIHdRcXE3Q2lLZTRvL3ZCSnZtSk1K
|
||||||
UVBjaDFtSWs1b29DR0R2WS9pSGh3OEkKmG34ldBy4s9nj3ng/HQr+gN0LHJCOPJ8
|
TU5Iby9qamRIcEUwc2dTdERFVmNreTAKh55E4KbM6WeFhVx3KDI/pYq+1vCNwDj6
|
||||||
EWhh7cTLSF9AmZKP0sBsj7I4hHhZlOn85bvTM9RDiRVOSz8VrObXHA==
|
6zfXWJvyD9Icn2ZgqpK30wyJ/R/DzmpTDR8AtujXHT6/Uikn7M1fig==
|
||||||
-----END AGE ENCRYPTED FILE-----
|
-----END AGE ENCRYPTED FILE-----
|
||||||
lastmodified: "2025-09-21T20:28:29Z"
|
lastmodified: "2025-09-21T20:28:29Z"
|
||||||
mac: ENC[AES256_GCM,data:e267Kxv1Pyun/VOcLepBDBEKN6uSf8/iuY8KQ8u4xK58wsWkMdSDVcDKvO/iKF/Tj9hj+lZapkaKmp5SdeX+gjpyWiZi6QmUuKsCs0jlkV2NydLtZZt9vkmY/LCguIBRMmhDgidrNcfoghTxDDK5lng5H+2MBs0r2zLID65pHUQ=,iv:tr4YFdBltnsD4uTt+0NCam7r1QzhOmdoEbfz5/+JGPI=,tag:R2dDWTC1qrwPI9ghaf1FEw==,type:str]
|
mac: ENC[AES256_GCM,data:e267Kxv1Pyun/VOcLepBDBEKN6uSf8/iuY8KQ8u4xK58wsWkMdSDVcDKvO/iKF/Tj9hj+lZapkaKmp5SdeX+gjpyWiZi6QmUuKsCs0jlkV2NydLtZZt9vkmY/LCguIBRMmhDgidrNcfoghTxDDK5lng5H+2MBs0r2zLID65pHUQ=,iv:tr4YFdBltnsD4uTt+0NCam7r1QzhOmdoEbfz5/+JGPI=,tag:R2dDWTC1qrwPI9ghaf1FEw==,type:str]
|
||||||
|
|||||||
@@ -21,38 +21,38 @@ sops:
|
|||||||
- recipient: age1lufn6t35gs4wgevyr2gud4eec7lvkn7pgnnv4tja64ww3hef7gqq8fas37
|
- recipient: age1lufn6t35gs4wgevyr2gud4eec7lvkn7pgnnv4tja64ww3hef7gqq8fas37
|
||||||
enc: |
|
enc: |
|
||||||
-----BEGIN AGE ENCRYPTED FILE-----
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBjcWpKQ3RqSVdxcDllajc2
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBhV0xoZEhJYlZIUDdOSlVv
|
||||||
UzVtdmxBWmJ2QkI3SGhYRlRadGJYaDU3UVN3CkpQYkxhVm5ZQ2djbldYL2VmQWsv
|
RGlOQmdSSDROaVY4L2xHSEt3cVVpc3MrRkg0CktGQ204UDNYcXN6NDJqTXFpdmxQ
|
||||||
SEJmam0zMzlJSFpHS3JZWVorUmh5ZDgKLS0tIFdWdU44VlRDZllCYXRTQzNyajRy
|
RUYrWDNCZzlObWNyYStWQlRqQ0VJQzQKLS0tIEY4bG41R1k4NDlabGhoUEl4VitI
|
||||||
cDJqNzA3ektRWll6SkFsVnFMd1FBUEEK0j9X4lYcFaj4MnVh4jnNwrTg2Sl5TTdZ
|
YmYvaDNWRzRlMkdUdVBxM2lwd0N1bXcKp1iUENgs/0RL6PN7b/mwbBdIPuDFfWM4
|
||||||
uFvTdE4ZNtZsh3nKmj+v2J3JM8dDUtw2NSooqpoqEvCYdDqwK1kDXQ==
|
9gXuoW7FiS5MYGdUY5Ub8WlSfA6iUww+t6FB/rBhK9TDXOfIKRYmgg==
|
||||||
-----END AGE ENCRYPTED FILE-----
|
-----END AGE ENCRYPTED FILE-----
|
||||||
- recipient: age17jlsydpgl35qx5ahc3exu44jt8dfa63chymt6xqp9xx0r6dh347qpg55cz
|
- recipient: age17jlsydpgl35qx5ahc3exu44jt8dfa63chymt6xqp9xx0r6dh347qpg55cz
|
||||||
enc: |
|
enc: |
|
||||||
-----BEGIN AGE ENCRYPTED FILE-----
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBGSXlITkpxcHZqR0kzMlFY
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBWNEtaUndrMGszbHpuYWtx
|
||||||
TStOVitPSm0zTURZcE92NkM2ak8xcVF6OVZBCkRRbkpBNW9yek9rWFlOa1pLSk0r
|
RTRCc29YcmQwYWFKeTFtaHBpM1NjR1R1dGxjCklsTFVXSmp0OUxVUzVYeFJnSWI5
|
||||||
ViszS3pMNFhLQlcwdW83R1hhTUJLT0UKLS0tIG9NTm5tNzlidlJmejdoOUkvUE9X
|
M2hEM0pjTXowbGZsQ0tHdnJYdkxjTncKLS0tIG0zUEJ6ZnNOVmd5UWF1K013N1JT
|
||||||
RzV2MUFEMnlHVmp3UmgvNmJKSDFrWHcKQ7y2W0PFLs/I6Tb0J/M91+toDP8XmgWh
|
dk1HeEJ3bkVUWnhIakt4eEdNUi9aSkkK6Ug6dwtSEpzMpgKvozR8BO0ir1YeRBQd
|
||||||
LYuNc9lkjTs+ylIWuMTwtXdceI+kK8hJlELT47FyKl755DzuB1ufAg==
|
jDtkNhpc32P5uZtx/kv74vIXgOT7KCSb03b7mSIl13J2IeHQDZTPBg==
|
||||||
-----END AGE ENCRYPTED FILE-----
|
-----END AGE ENCRYPTED FILE-----
|
||||||
- recipient: age15hx530yrqmhm80vsjmffyg9deq9gssj7hl5rsqdnsn3dwegj9qusv4sjf5
|
- recipient: age15hx530yrqmhm80vsjmffyg9deq9gssj7hl5rsqdnsn3dwegj9qusv4sjf5
|
||||||
enc: |
|
enc: |
|
||||||
-----BEGIN AGE ENCRYPTED FILE-----
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAzYXRodjgzR1hoR2VDNHd6
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBWMm5NRmg5Y1NYNDA4MkpR
|
||||||
NGJ3SXpqRmJKVlY5aTI0R0hBLzFGQ0VSYmpVCi9BakFwRGlXd1ZPbWpHY2h6RUo0
|
M2RPM2JLZ2NMQm9uNURNc0lSVnJaMHE1MG1JCi9CY3U5V1pnQjhPOXl5NlN0eTJW
|
||||||
VGl0T0d1LzdaZGNOZ0pDekZxVVBWUlEKLS0tIEdtVDZlN2FrcFhEU2pTMUdiZ3NH
|
d3dXQ3hMRGNjWlBQZDAzc0ZDWWUzNm8KLS0tIHMxQWdvY0F4Mk9zaElhMlhManFu
|
||||||
d3ZSMGdkNzNaczBYOHFuZWJmcEM4MXMK6ayh37HUhOYPryv2Y2WlE1U0CX7qZF89
|
TXNGcFZRd1hPdW1wWFpPRklScGZqVXMKwHv5CDSdlaGlXqFKoK9motAWNVMzerXy
|
||||||
PzvHQZYcbZ2gsRW2f1uU2VoJp/6XnSipD7fCjma3iNovoPlu2+A0yw==
|
6K2KVn3tmlAiBzkwuEVVa4jafQjd0t3J6bPx047DP6fPZVNLMElctQ==
|
||||||
-----END AGE ENCRYPTED FILE-----
|
-----END AGE ENCRYPTED FILE-----
|
||||||
- recipient: age1ml3smrs5mwz4ds84gk0eyss86nwsmp07qh0npxsuae7lfwwpsghssavytw
|
- recipient: age1v2ahkl759cftpcdq4mla2cvmgz4jlnmgj7qtgc9732zxrfvxf3lq76zjpr
|
||||||
enc: |
|
enc: |
|
||||||
-----BEGIN AGE ENCRYPTED FILE-----
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB4aUk0WVhTUTZXOVRqam9O
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBIaTNnYlFsdFBJeGFwdUNl
|
||||||
WkVNd1FKdDA1SWpwWndHbmVBRlNaSWI4aEVnCnJTTDNYTkRtNkR5cUl0SURVQWxh
|
MVc4OXorOVhJcDBTWDZaT3pQbTg0UFlGNkhrCnVST0IrTnliREpmS0dPMzVDZUJm
|
||||||
d0c5cEhJVTZ2YXdLdHFQRk9KN04vcW8KLS0tIGF3Rmp2Z0pwM0x1WnpKaVBiUE5x
|
aUg4SnhtNlcydFYyTkp3N0xaSzVCREEKLS0tIE9tZmZLUERnSzY0aDdkTnY0SXJz
|
||||||
MVBONDBmQjI2enNIVFFQT1hyYm45YXMK2NXWvm8G+Yrvw1NAC6AiDaxA9UftuqYe
|
UDltYm4zalM2VmxmQjJRSVQ0YWpuM3cKsYQOOppHVJT2tbQQ/jXy4NcUX6aWjQxT
|
||||||
ZB7QpfkdCT3vS52lBgcEJrM1TbaVX2868trk5kB4gjqVMPVPYxcGHg==
|
Y/I40tBrkwnzVFpVvf6COS+oC6/yRISwWJYYvia9xVfC5+kss9cFIw==
|
||||||
-----END AGE ENCRYPTED FILE-----
|
-----END AGE ENCRYPTED FILE-----
|
||||||
lastmodified: "2026-02-02T03:55:24Z"
|
lastmodified: "2026-02-02T03:55:24Z"
|
||||||
mac: ENC[AES256_GCM,data:+NN+RgkHAIox1IgUuC2ACHneRBzgn5FzsujpbPtmw1IecxeKMMXM7Wa1ZziSkWJSjjDCcBoanox57e+BoNWN5WhWuMdCed04AKcknfKlHAtHrKhoLCsi1sZnsQX7xBmTsA5qHD8788EWfIgPk4gToXkq5KkEfvEWLvalClRK7tY=,iv:kGyw9hk6vp5iu0iMHaCLgVqdcv1gNUBqBhZbRSCa4Ks=,tag:FdKL/5ZraejphDIE2ig8GQ==,type:str]
|
mac: ENC[AES256_GCM,data:+NN+RgkHAIox1IgUuC2ACHneRBzgn5FzsujpbPtmw1IecxeKMMXM7Wa1ZziSkWJSjjDCcBoanox57e+BoNWN5WhWuMdCed04AKcknfKlHAtHrKhoLCsi1sZnsQX7xBmTsA5qHD8788EWfIgPk4gToXkq5KkEfvEWLvalClRK7tY=,iv:kGyw9hk6vp5iu0iMHaCLgVqdcv1gNUBqBhZbRSCa4Ks=,tag:FdKL/5ZraejphDIE2ig8GQ==,type:str]
|
||||||
|
|||||||
@@ -5,38 +5,38 @@ sops:
|
|||||||
- recipient: age1lufn6t35gs4wgevyr2gud4eec7lvkn7pgnnv4tja64ww3hef7gqq8fas37
|
- recipient: age1lufn6t35gs4wgevyr2gud4eec7lvkn7pgnnv4tja64ww3hef7gqq8fas37
|
||||||
enc: |
|
enc: |
|
||||||
-----BEGIN AGE ENCRYPTED FILE-----
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB4R05sUnl0UFd3T3pzRm1y
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBsNWNIek9yVUJIYlhSQmVZ
|
||||||
U3piNzlpTmpZeEhkeWJxRkMzRzBWRW9LQmtBCjRHTVg5ZlozUnpsVjhIK05xYjlz
|
NENoS2dvRDVENit2bnVNQmJ3TVZGWTkwTFh3CnViekVsaTVFZ29iQXNXUTNPS2U0
|
||||||
c2dwbWVKWVNXWFhTWEtlUUFjVUw2RkkKLS0tIElaNXN2ZmROdHd4bWljM3FyMEh6
|
Z2JsWWN4T2tUckVJU0tpNWFaYXpGVTAKLS0tIFowcE1tZDdPREY0ZGVzYS8xNFFp
|
||||||
Szg3WTdrVlFmSUJ1S05xNXY5RlM1V1UK7YETep9hn49UqRUjbRv6oGFUT/8lRgXx
|
elN4TnZjZUtGOGZ1c3FiU0h4YytLTmMK9wXfpIgMcPD4FpO5CNIXnJc0wJliB35g
|
||||||
5O5eGB1X8kPCY8zXiGWSzfo6X8O5659vWIvqjoY8nZxekgvsISS/WA==
|
v4wiDb4zU4VFfWzdimSXjgZrI/ZIqB4Bx/PPi6SPhuT4oQ6LSH5sKw==
|
||||||
-----END AGE ENCRYPTED FILE-----
|
-----END AGE ENCRYPTED FILE-----
|
||||||
- recipient: age17jlsydpgl35qx5ahc3exu44jt8dfa63chymt6xqp9xx0r6dh347qpg55cz
|
- recipient: age17jlsydpgl35qx5ahc3exu44jt8dfa63chymt6xqp9xx0r6dh347qpg55cz
|
||||||
enc: |
|
enc: |
|
||||||
-----BEGIN AGE ENCRYPTED FILE-----
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBpT1oraHpJb0NUQndMZW9l
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBtSmsyR0N6SWMvQVl2RzRZ
|
||||||
YWZrOVBqOG1KME5PYS9YVE4zd2VQb0hRN1dJCmVqSzhkbU5DVmc4MFVnSnVYTi9V
|
TUpXbm9oTmswdXViczVBOFBLL0lhUmV4WXhvCkdqRG5pcStUVDE4S3FSL0R2TTlG
|
||||||
RUR2UDNEK3JGOEFUWVoraGtqQVFFWkUKLS0tIDVRdU8rV3diVXNUQSsrKzlBdmFN
|
Sm1aZTcrejRHdzU2dFozSUZucHFtUHcKLS0tIDJTSmU1dVhSeUxMSnd1NGlkR1RC
|
||||||
Q0x5QXdaOXRMc211TUhqTndQOXR6ODAKtJYiAeVTYPOpS+GykBDOLx1g3VloFo2P
|
cVVIcy9QRFArUkdIM05neG5aM2EyM0UKSCIv0iU/X9bVoQCRxcQXwMbr0GE7MGkb
|
||||||
fDIkOCrINnAU4y07KPhGBxCV3/2cvOPhIgsd02XqxfZPCEU/cYdCgQ==
|
pn420gXMiLFBE8OOhkHg7EEjuR3n9iB3f+pTgN5v6UkxZBmZ2Xr4yg==
|
||||||
-----END AGE ENCRYPTED FILE-----
|
-----END AGE ENCRYPTED FILE-----
|
||||||
- recipient: age15hx530yrqmhm80vsjmffyg9deq9gssj7hl5rsqdnsn3dwegj9qusv4sjf5
|
- recipient: age15hx530yrqmhm80vsjmffyg9deq9gssj7hl5rsqdnsn3dwegj9qusv4sjf5
|
||||||
enc: |
|
enc: |
|
||||||
-----BEGIN AGE ENCRYPTED FILE-----
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB4dTIzeCttSGNVNmhPejdW
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBlL3picmtSZE8vS0Jod1RV
|
||||||
ZFkySng2T2ZYdkRrRGRXQVpER1NJMW5XN2xVCkc0VTdsbXdLUkg5d29zZ3VmY0hH
|
aGNlK2t6b3RTZWRPUmNCZXN0KytSRnRuMHdNCkd1VjU3NEhBOE9jN3gvblM2NnZF
|
||||||
U1cybHNob3VkdzRWbGt1bFhNeW9XN0EKLS0tIDdoc2cyaEIybjBHOU5tdVRsTWFZ
|
TDRhVGh1Y21YM2J4WTZtenFHSDBBNncKLS0tIHZqcnpRbldWN2cxT1I0Qzgva3R5
|
||||||
TmdZTGNDOFovMDVPakF0WTdHaUpHeFUKl0ub1OOylE2JGJNpeReebiOaVdxbd0wv
|
a2lsbG5SUFgvZXlHWUhOc0xQS1dxaHMKDMGQujRa0s4kjrQod11mn0otxO2Zl/bv
|
||||||
nvJD7tYYXI666Pi31OHttWhsHR+xkL8TU9Dd6uDs4QxIRQfwy/VxcA==
|
kHG8ufANpJS5RfKNLMhAK4piUtr1o97471MSGA0ebZAUSK01fQBNfA==
|
||||||
-----END AGE ENCRYPTED FILE-----
|
-----END AGE ENCRYPTED FILE-----
|
||||||
- recipient: age1ml3smrs5mwz4ds84gk0eyss86nwsmp07qh0npxsuae7lfwwpsghssavytw
|
- recipient: age1v2ahkl759cftpcdq4mla2cvmgz4jlnmgj7qtgc9732zxrfvxf3lq76zjpr
|
||||||
enc: |
|
enc: |
|
||||||
-----BEGIN AGE ENCRYPTED FILE-----
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBGUldWbldqTTEwMGF5RVFV
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA4WlgxL2xMR1p4eG1Ca3FM
|
||||||
ZkZ0Y24ycU1hVDlaTnpGQW1SeFlzaXc2a1FrCmtrUkxLcjNsVHNXemd1cWJJdXI5
|
c1JOcDBhaHNlUjRNZjFhVm9CdkN6NEdqdGtnCnUwUFBtLzYvM1kvd1NVREdwL3B5
|
||||||
bDFxUThzSFptNWtXMlNqM09aeklUMTgKLS0tIHR5KzE3dStMTXlhUWhtUWUwSkY0
|
MVc1UEdaSTlhWXFadSsvbHNTaUZKcDgKLS0tIDc3RDg3ZUkvRVFvRWhBWDFHZjV1
|
||||||
ZldyVmtRVGppQ0d0SnN5Tld4cEtmQ28K1Yij+7OxQUpEsPt/GTnP+dhEErBH1HuL
|
NGlweEtoVGdqT2J3UzNGaGt2RFM5eE0KUCFvcv39dFM0Vm6uDuntsnZyMq+LHfJW
|
||||||
pBFXqHLAwpqiEiiNhYnb0KVWeQnIqDo9WUnrbPavcWSrSkmCsszgxQ==
|
Sts7AJwVIGTmOolImqoVTeKFYJZu5oeKZZNsEG+gvIZptxaR0jPtow==
|
||||||
-----END AGE ENCRYPTED FILE-----
|
-----END AGE ENCRYPTED FILE-----
|
||||||
lastmodified: "2025-10-10T05:09:54Z"
|
lastmodified: "2025-10-10T05:09:54Z"
|
||||||
mac: ENC[AES256_GCM,data:N/BwfrwWcnot36Kn6RFZjjpUIluzq5Upy5iVVV4XSs+/0PYdlZGytjoAB+E3gXyPsLZ93UqI0A9/5KbfXBuR2oY2F7iKsu5puzgyYWa0Gl2z9YcPnyDnk1dj7Ne77xJlqR9YquGzFKF8QdqFXFA9cdE3b/1usTFhP26oxofMXs0=,iv:Iz/LzS8yeKQgDiGchYdKNymBeekhopJtBWaQGOwRZlE=,tag:hMRwxJlKR21W7otW01GmGw==,type:str]
|
mac: ENC[AES256_GCM,data:N/BwfrwWcnot36Kn6RFZjjpUIluzq5Upy5iVVV4XSs+/0PYdlZGytjoAB+E3gXyPsLZ93UqI0A9/5KbfXBuR2oY2F7iKsu5puzgyYWa0Gl2z9YcPnyDnk1dj7Ne77xJlqR9YquGzFKF8QdqFXFA9cdE3b/1usTFhP26oxofMXs0=,iv:Iz/LzS8yeKQgDiGchYdKNymBeekhopJtBWaQGOwRZlE=,tag:hMRwxJlKR21W7otW01GmGw==,type:str]
|
||||||
|
|||||||
@@ -4,38 +4,38 @@ sops:
|
|||||||
- recipient: age1lufn6t35gs4wgevyr2gud4eec7lvkn7pgnnv4tja64ww3hef7gqq8fas37
|
- recipient: age1lufn6t35gs4wgevyr2gud4eec7lvkn7pgnnv4tja64ww3hef7gqq8fas37
|
||||||
enc: |
|
enc: |
|
||||||
-----BEGIN AGE ENCRYPTED FILE-----
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBDa3NpNG5tenhqWVQ5RFAv
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBWaEZKVnhKR3ZnalQ1dWZy
|
||||||
bjhhRWJWK0NFQVk0cGVpcVJGS3BFeWlSQWc0Ci9IT05mQTVWbmk3SFFpWE9KUnh6
|
THJ1Qi9EK2NHQXUxYlg1NWZqd1VuRUY0a1FNCkg0UmphaktuWklNVE85c1RuT3FV
|
||||||
bHhCSktlbzVUQm1lOHp3cVpiSHU3MDgKLS0tIGg1UU4vVVo0SXRwMjJsVUZEZkFC
|
bUwrVzIxQ2JhZmNqdnR2SUlIcGNiazgKLS0tIEVDZk4rV2tTT0pLd3I2RXVIZ0pC
|
||||||
TC9Eb2JaVUFDSWRMYm5jR1BBa2lEamMK4V77WUVbMXcsw83FFdL2Rk30oR4cAkqQ
|
RWJCbkxHMm1DRFJKZkRhRldiRFVFd2cKQvohCMbXDJzOKzfAN72/1S4CXj5d0bbK
|
||||||
kc8Z0+5kNJFUFilFb54dnWTOh27K7KZvU1qIdhG3X9fuMIHSuPnyTw==
|
Ge+V8Ew9S4+UR39iLtQzs7lNYYCtDxNnayEm0V8LlVkgeEj2HnS0bA==
|
||||||
-----END AGE ENCRYPTED FILE-----
|
-----END AGE ENCRYPTED FILE-----
|
||||||
- recipient: age17jlsydpgl35qx5ahc3exu44jt8dfa63chymt6xqp9xx0r6dh347qpg55cz
|
- recipient: age17jlsydpgl35qx5ahc3exu44jt8dfa63chymt6xqp9xx0r6dh347qpg55cz
|
||||||
enc: |
|
enc: |
|
||||||
-----BEGIN AGE ENCRYPTED FILE-----
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBDRVdYVTY3QzR2MFJPVW9j
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBheTV2MllUaFMzTWpiRnFw
|
||||||
SUtmTldMRCs0dTlJcGFoMDVnaWMyNlF4OHlnCjg0OFFrOERKRFZVMm9NREhBOGRs
|
a3hHeFhyNHZGek9tWVhySnVLUzNDVzV2VUIwCitWLzJ1ZG4rVHRwZElOdTdXMmdX
|
||||||
dTEwc0NZUk9hOEtvNVJDRXl0TDhCaTQKLS0tIE5OWm5CNzc5SW9IdGFud1N6Vm1D
|
ZG9RVktFQ0VvdDViSi92YzRIZXJYOHMKLS0tIDZkRHYrMmtyZUh6bE5KaldvMDY2
|
||||||
djhzM29HK0FIdXIvaGIrRXlOMisxaTgKVCAiniAmfqJuwwiUpcGAvoyqnUEZ9gOS
|
MitQdXpDTkNxME1pV1BML1ZRRG1NaWsKUBHmQa1io7qNp+xkEmYsn7Q6XSpQ/566
|
||||||
SyhXMzv2cbomuOb0NiALRkd2up/uX0TVuz9wuBQvYYjJhqpFuSnbRg==
|
KYVB7GMSyp5YYsJv1vA8tLHnavLDam4zMZ1t24dgk8pWOZpJ4T6T4g==
|
||||||
-----END AGE ENCRYPTED FILE-----
|
-----END AGE ENCRYPTED FILE-----
|
||||||
- recipient: age15hx530yrqmhm80vsjmffyg9deq9gssj7hl5rsqdnsn3dwegj9qusv4sjf5
|
- recipient: age15hx530yrqmhm80vsjmffyg9deq9gssj7hl5rsqdnsn3dwegj9qusv4sjf5
|
||||||
enc: |
|
enc: |
|
||||||
-----BEGIN AGE ENCRYPTED FILE-----
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB1UlM2MC90SEM5elBtNmpm
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBkanhPTzY4WEhVLzFvbjhR
|
||||||
cnpTQVF0VEpLUDJPZkEyeGNuYnl0cEY4M0U0CjhOY25BcERjOThkbkVhNnJtaVpv
|
emVmVmZIRmVDelpONmM1SUhuT2Zadi84aW5ZClRaVGwrbjlGYkZiOVR0V2RuVWR3
|
||||||
N00zOUZPWnNYaEtYMzlXZk56dGVPeGsKLS0tIDVDcGY5cG1ETHk4eXRFN1hVOXhV
|
UVBlNG9RUlpUZlJ6VFhKRHQ3T1VMRVkKLS0tIGpUTzM5MVA1LzdsNW5IMGlZcitR
|
||||||
Y2xncFJuNUs5ZkhLSjJyc2pzdDZxbEkKn/8BtUXPQ0OdR35ZwiHWFB0AqaDtAlG7
|
aG9pR0RRd1NBalhweEdGNnNCVHFvb0UKIiFruo9rV/VD6XykanHIpbI6G6D3cGG/
|
||||||
N4Z7iztqiscuxn8G8VVVFdkQLBY3JcrXhxPYWK4xtJeEtpIMhegxeQ==
|
ZGSxH1HD9qIVYDQ5LpBfUy/dZxRnpTiBJ7Veg/Siemz7kmChlmVDQA==
|
||||||
-----END AGE ENCRYPTED FILE-----
|
-----END AGE ENCRYPTED FILE-----
|
||||||
- recipient: age1ml3smrs5mwz4ds84gk0eyss86nwsmp07qh0npxsuae7lfwwpsghssavytw
|
- recipient: age1v2ahkl759cftpcdq4mla2cvmgz4jlnmgj7qtgc9732zxrfvxf3lq76zjpr
|
||||||
enc: |
|
enc: |
|
||||||
-----BEGIN AGE ENCRYPTED FILE-----
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBzd1VYanVSZTd5elNlZ2NC
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA4UWRTR2x6aWJEZ1p4aE1O
|
||||||
bnd0V0VZVmtrVDhBbW5KUHJDMUkrekVZeldRCjBNY3g1SkVKUzhRL2xsbjloUERi
|
RGpSM0xVRVJpTDhVa0dhTWwwTVp1Z0NkTW1RCkxRVUhkMklZUVhDSEE0bkZHaFlu
|
||||||
UXM4T1A0a1V2eEFlQWlTQ2tDdFdaZ1kKLS0tIFFtNDZzbzYyaE5UT3R4eDJzNnU1
|
UGRvYURuMWwzOUtoTzV2V05kblRpNnMKLS0tIEgrYXBpYysvenRGMENNcTY5L0dk
|
||||||
RG9UbWM4YTVHcFpKblQwemNScDVteVEKA6fibq6Ozwrz/tg9Hrx4bH9LCadmW5fR
|
a1pWRTkzSkZGaTVtbVc4VjdHdkpneGMKq+3Pd2dOJAnC/PKEYijWbk1vQSes3ykt
|
||||||
IkFalgD7nqew8KwS0keyKFk93i2p6sTDZPy2/t+WryMXBIc/y0iQ5Q==
|
1A88VIO2o/isCLr9643SVkZQ4WbISA4xvInG+peEdbja0oZNRQNU3w==
|
||||||
-----END AGE ENCRYPTED FILE-----
|
-----END AGE ENCRYPTED FILE-----
|
||||||
lastmodified: "2026-02-01T22:31:04Z"
|
lastmodified: "2026-02-01T22:31:04Z"
|
||||||
mac: ENC[AES256_GCM,data:gtTuLmgVd5t1Eic+ld6x3pmAlv2+SVf4OgUICu78DJ9L1YCtmJ+LsqIoHFueMdQAmubPA8c4xYsHWCDu2dbrUDUs/79BF2u4P9lbNkJx5cco8bnPdy2tmkhcLwb0HwRduVIbgcm0wzYKUMd76Y0ChxdCddkrkk+PjXkUE7OBNg8=,iv:Eqhoc6GjB1NOnIIeRIdVoQNQm51DguH3vEX4zRUgeBE=,tag:V25oIemZpdJDMRFcZkH4bA==,type:str]
|
mac: ENC[AES256_GCM,data:gtTuLmgVd5t1Eic+ld6x3pmAlv2+SVf4OgUICu78DJ9L1YCtmJ+LsqIoHFueMdQAmubPA8c4xYsHWCDu2dbrUDUs/79BF2u4P9lbNkJx5cco8bnPdy2tmkhcLwb0HwRduVIbgcm0wzYKUMd76Y0ChxdCddkrkk+PjXkUE7OBNg8=,iv:Eqhoc6GjB1NOnIIeRIdVoQNQm51DguH3vEX4zRUgeBE=,tag:V25oIemZpdJDMRFcZkH4bA==,type:str]
|
||||||
|
|||||||
@@ -51,38 +51,38 @@ sops:
|
|||||||
- recipient: age1lufn6t35gs4wgevyr2gud4eec7lvkn7pgnnv4tja64ww3hef7gqq8fas37
|
- recipient: age1lufn6t35gs4wgevyr2gud4eec7lvkn7pgnnv4tja64ww3hef7gqq8fas37
|
||||||
enc: |
|
enc: |
|
||||||
-----BEGIN AGE ENCRYPTED FILE-----
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBWRGVscDJsU0ErZ1VBRzVq
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBnVVQ3NG9lM0l0MzlYaVA4
|
||||||
dE5aNUZvcmhVRHVjYUJFT09hdDd0UzhIS3hNCkRFRlphRXBTd3VFTE81RjJRaE5w
|
dEpOajlJeUxYTmJlMXJJMlN3UnRwZHEyaHlVCmYya09LMU5UQ29pQ3JCeWJnRkI4
|
||||||
bzJSaCtsT0QwMkx2WDVyZ0FzeFphWk0KLS0tIGN5M0QyWmQ4Y3lCU0FXaU9vL0hv
|
ZEpzRk93WUhXR25QK2c0UjRlTnZld0UKLS0tIHZPUGthU0tBTVNzRmFmVUxnSkda
|
||||||
MEp1ekxTdWp2b2g4dFd3OVNkUlZBMGMKzNGSzYgQsNW6HEvzTWmo73GShAAv/g8+
|
RUVNLzM0QUZLRFRCOFpjTXY4eHprUWcKK0+r6kWEw+gC8P+afVvw31SY63PTKb1C
|
||||||
h3/6n/ObqlKsjDyVFgiOYop3LWfwPMzmOhx4S0wsOHit0UxdyoJwWA==
|
D1KCOugRHnNT+xOELiVg9jjFW5lTJc4U2OBe/IpsGBujleXrWKwpvQ==
|
||||||
-----END AGE ENCRYPTED FILE-----
|
-----END AGE ENCRYPTED FILE-----
|
||||||
- recipient: age17jlsydpgl35qx5ahc3exu44jt8dfa63chymt6xqp9xx0r6dh347qpg55cz
|
- recipient: age17jlsydpgl35qx5ahc3exu44jt8dfa63chymt6xqp9xx0r6dh347qpg55cz
|
||||||
enc: |
|
enc: |
|
||||||
-----BEGIN AGE ENCRYPTED FILE-----
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBLUGFaaHFtVWl1cG1XdlRT
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBZY0JpODFjNkhQbnFHNlJU
|
||||||
TUh0MHZTa0JhdDFSTVJZOWJBd0F0SWI0N2tNCkdnaG5DcXdDT3dqRVJDcjlsZ3Fz
|
V0s0YWZSTC9OTWdXR2h3S0FkM21CN3NocURVCjM5TWNNYzhkUW5jcHVuSW40ejJs
|
||||||
ZFFaeTB4UTBQRVYzcldndm1RSjhCTzQKLS0tIDJySFIvbGpBd0l4RzYwVUd1MWpF
|
MkgydVlpejhzWFlMZHNGMzdqaEpPcFEKLS0tIHdzQ3UySDFpeWhVMDk0dmswTW9N
|
||||||
ZHhxdERrd3VNUGpTTlZUM25RYzJwSjAKG2DZUyomWm8Nxn6mPDKbBh1YsEUr642a
|
U1M3aXlqSHQwaG1DZysza25KZVRDU2sKp6kZa/6/Or9zdLTfFf/lKWcoHDz4v6p7
|
||||||
nGYxmuRVBVINbOB3gBPwgLeD+S2Vlm4vrC/u2761fTgm8KFLC+txpQ==
|
UEAA3twa1VXAk7dqmDmp0Szngu8y7iF9BE5fS1nb5n+rUa9DrwWvng==
|
||||||
-----END AGE ENCRYPTED FILE-----
|
-----END AGE ENCRYPTED FILE-----
|
||||||
- recipient: age15hx530yrqmhm80vsjmffyg9deq9gssj7hl5rsqdnsn3dwegj9qusv4sjf5
|
- recipient: age15hx530yrqmhm80vsjmffyg9deq9gssj7hl5rsqdnsn3dwegj9qusv4sjf5
|
||||||
enc: |
|
enc: |
|
||||||
-----BEGIN AGE ENCRYPTED FILE-----
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBXTnc0N3JWUGk3cWV0QXNK
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBrU2Y1RC9aczBVWjZoS1Vp
|
||||||
L0ZWM0I0NVlVbTdsZmdRall2V3FUTllidlJnCjQwbFJ1TjVNQjl3NURQenBDZVhy
|
VFp4VmtkME0vNVpnZXRYQmxmdHhZb01MelY4CjhHcnpVenVFd0YxOGJmT3pOOEF0
|
||||||
QXEybkIvc0RnV1dNL1Rhem9GajhzY2cKLS0tIFk0Nm9JK2ZvenJsYVF2RUJLVzVL
|
a1VBNGpSNSttblF3b3ljRDI2NjAwbzgKLS0tIEdiYWpucEY5N0JVN215ZWNDZmkr
|
||||||
bzFWRnFjd01wbDVrQnhlb3NYampEVEkKWl3/oymEX/TdMHyxE8mOopIwu4Kots27
|
SlJJaUFzaGdwdjhwdjJUWG1TdnZIWHMKGvQWCQNr83Z0CP5jGHc2wvqOIUdGC7+2
|
||||||
teyBmo6aVTAQ1zSxGDszI6kgK6PC3Z/WqaMaoJilGI6k8vCkOT3oMw==
|
8buS4XK22o7EotL4bbKsEw5dgWQIBRXH+9XCq56RIUYR0T/T9UW0ew==
|
||||||
-----END AGE ENCRYPTED FILE-----
|
-----END AGE ENCRYPTED FILE-----
|
||||||
- recipient: age1ml3smrs5mwz4ds84gk0eyss86nwsmp07qh0npxsuae7lfwwpsghssavytw
|
- recipient: age1v2ahkl759cftpcdq4mla2cvmgz4jlnmgj7qtgc9732zxrfvxf3lq76zjpr
|
||||||
enc: |
|
enc: |
|
||||||
-----BEGIN AGE ENCRYPTED FILE-----
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBtaG1Ea0ZyZ1IrRGxEaUdw
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBnblN2TkRudTZCN2Vvcldr
|
||||||
TzlMRE84ZDBXRTNFWHcwNE81MDZlYStTZWdFCmxLbUxORFNVVHRGYXV4bDRvV1Ra
|
UDdBNXYyaUJVdkRDL1Zlb21vK1NlTnJyV1MwClFRMWpQYmo5amRWMTRCTlYySTRY
|
||||||
Rzg0YnpkaDJ3alhxalFFck10MjF4MG8KLS0tIDgwSEhReERtZHZ3U2RWcnFaaHlI
|
YStldXhHdnR4RUcvNFpVbUZPMGpFQU0KLS0tIEV4MGZ3YUJjOVdLNDF6RFhIOGs1
|
||||||
UmQzNEJVVTVPRHFqVlAraTR2bHNOdmsKKCVCzZ10sEA7rGRCUxbpYlaR6Y2jZvho
|
bmtmNDJ5OFlQYlZTWmQ5S2FmdEZ3clEKYRQ7nuP3G63vwyhW0wLQISrkiY98F3jx
|
||||||
THbZe5MHY1a44L2XQSZe3I+1qOVBWVSL10KYTjJIBTxoeBtjlQJAVQ==
|
7c9qMd2eGVvrOQr5M2OEPcjKexBa9Qt6O5t+dABrTmXCa42B251zWg==
|
||||||
-----END AGE ENCRYPTED FILE-----
|
-----END AGE ENCRYPTED FILE-----
|
||||||
lastmodified: "2026-02-03T21:56:09Z"
|
lastmodified: "2026-02-03T21:56:09Z"
|
||||||
mac: ENC[AES256_GCM,data:Bnjo3TFYoGbtB8HF1i+ZQLlfeBMOjq14lu8oLRqcZ6Fx5Am0uuh+/PHClWZ/JX5suC0Kb81+aBHg2QTsLoB6zdUrRpaqa0CUxTDoGw8tpo8m6zLWvSggpYLAuRgTYqBZ0lVK1QxAi9+qVJQ5AIhYwSPrf2oq/Mpq4tFGUoG/tzM=,iv:8JqAeBVYnZM8A+CPAlKN+6SDty0XQ4AKEBJLGV8Q738=,tag:CQXE5QsfJMiI7UQoCfE3dQ==,type:str]
|
mac: ENC[AES256_GCM,data:Bnjo3TFYoGbtB8HF1i+ZQLlfeBMOjq14lu8oLRqcZ6Fx5Am0uuh+/PHClWZ/JX5suC0Kb81+aBHg2QTsLoB6zdUrRpaqa0CUxTDoGw8tpo8m6zLWvSggpYLAuRgTYqBZ0lVK1QxAi9+qVJQ5AIhYwSPrf2oq/Mpq4tFGUoG/tzM=,iv:8JqAeBVYnZM8A+CPAlKN+6SDty0XQ4AKEBJLGV8Q738=,tag:CQXE5QsfJMiI7UQoCfE3dQ==,type:str]
|
||||||
|
|||||||
@@ -19,38 +19,38 @@ sops:
|
|||||||
- recipient: age1lufn6t35gs4wgevyr2gud4eec7lvkn7pgnnv4tja64ww3hef7gqq8fas37
|
- recipient: age1lufn6t35gs4wgevyr2gud4eec7lvkn7pgnnv4tja64ww3hef7gqq8fas37
|
||||||
enc: |
|
enc: |
|
||||||
-----BEGIN AGE ENCRYPTED FILE-----
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBQTUEycms3ZkdMd3hpcXJz
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBCWm1RTldOekErU3pxcEpP
|
||||||
R2pZZEc5STZ3dUdYbUdsSGJaRWI5TWNMK1RRCjVxR1pzY0ZVUmcwSjJFYktteWoz
|
dWducG4vTGpuYkhlZDY2a1lLemRFaW9uNFRNCnNtRExLbVBXUVBXRjhhMW1NcjR2
|
||||||
YmlaVkFPRnZha3h5ckV1TVQyVWZKdGMKLS0tIFgvdWF5VEJwTTcwdXZ6SDRMU3BL
|
dkR1MFBPdDhPMldaYzk2V2pYQjZWeHMKLS0tIFc3RDhLVXdtaC82RUpPWnVGdjc3
|
||||||
V2x6NlhyY0pmUVBsYmZITjArdjJRbEkKvzsJxs5EHR0uumwhZ36MhKuMS+WkogXU
|
d2JyM043WFJSL0grR0FheldHdWFSTXMKxf4LZ1sKH+HKKCT4w8AmKk+DtVoSobtn
|
||||||
nSVRQoc5TClzYwShY1ltHK+LCl0DlB4xFoMiO4GWwH1TySKe/ywpUQ==
|
20acQeJsbuAng+/DIQccPSp//3+3YkfsBRfSGg90vQPNKzxxNmrY1Q==
|
||||||
-----END AGE ENCRYPTED FILE-----
|
-----END AGE ENCRYPTED FILE-----
|
||||||
- recipient: age17jlsydpgl35qx5ahc3exu44jt8dfa63chymt6xqp9xx0r6dh347qpg55cz
|
- recipient: age17jlsydpgl35qx5ahc3exu44jt8dfa63chymt6xqp9xx0r6dh347qpg55cz
|
||||||
enc: |
|
enc: |
|
||||||
-----BEGIN AGE ENCRYPTED FILE-----
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBnQytNaUs1M0hiYi8vdDUx
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBmSTA4Yk5TbmIyVzJWb3h1
|
||||||
V1NtZ3VGNFVjRHRWUzliR3M3Q2Z6K3RWSHo4ClQ1RE1PeHJ4REpubVJHb0lJcGJ2
|
bEFsb0l0Uk5jVDdvL0hMdlBFUzBJdTA3bEZvCmRMRmdyM3lieDVGVDZ0bjRpSngz
|
||||||
SEFvT2YvNWhMc2lneWR5NmRYc2pzVE0KLS0tIGxkRWRRRTNtVDUzVXh2L0lEa3RK
|
c0FqNjRWN29zdzFsRnhtcEhUeEtwb1EKLS0tIEkwcVdpWmhKZEVZM052WU16aXZi
|
||||||
YjFSUDJHUjFUeVBFbUlKOS8ya1ZhMW8KssRH3/XT1iCVgV+6Sh25Axp0c96aHtVX
|
UjFxUlExazVhc1hkcmZuT0ZadG1pTmcKADLIwbz9KlPgTrs3kxeWEgKsfh9K9Qyp
|
||||||
/HXN3AwTm0GJZCQnZsVIIPtoCzhUZSza+bzGZIZODYtgtCIxtdzVSw==
|
+PSLBc8OjORDBBqqRcFJ3D9paiqppegGAPKaZ9INCXVoWke+wEOL3g==
|
||||||
-----END AGE ENCRYPTED FILE-----
|
-----END AGE ENCRYPTED FILE-----
|
||||||
- recipient: age15hx530yrqmhm80vsjmffyg9deq9gssj7hl5rsqdnsn3dwegj9qusv4sjf5
|
- recipient: age15hx530yrqmhm80vsjmffyg9deq9gssj7hl5rsqdnsn3dwegj9qusv4sjf5
|
||||||
enc: |
|
enc: |
|
||||||
-----BEGIN AGE ENCRYPTED FILE-----
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBIbkZpZFJCY21IRkJjNkRB
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBSM2h6Rkp3T1c3UTJXVGEw
|
||||||
UEdEVlZhRWhRb1ZDMjJtMmpkUmpnY3ZvMGlFCnBLcHlkMWNyMy8wenYwT2pmRTZL
|
ZHJJNndrUGdtVnZNOWJobzZFb2U5d09LekJzCm5mSUd6V05BUUZpMm9US2JhRUNP
|
||||||
dWtiWFlaR1FrL21HQTFZM2N3a3BHYW8KLS0tIFlYZWVHb0VEeDU5NnRjbDk5M2po
|
YnZ4U1RBSUdMaHJnd2ZGNVFyT2hKeWMKLS0tIGpzUHlVU0JMbitmSzNjOWdaRFFI
|
||||||
K0xRRFhua09DRE04WUd6NlZuQldFbEEK2OgiawCbCtbrk8l45QdjVu8+VNWbrl4i
|
YlJoZUVoQWFHMEg4Umo3WDZHUVppQncKL8HtEF3+uI/qm8K/u7V7IlEv8Lt0QwQv
|
||||||
3U9iwek30JkQSZaWBXaCZlWLvbKNjIMpwTtxDOhxmu4DUh3Hx6In/g==
|
SPzuq89L/aT7hK3LyB88B2pvAKE2Z1Kj/3Z3depQfujIQsulpIg1lA==
|
||||||
-----END AGE ENCRYPTED FILE-----
|
-----END AGE ENCRYPTED FILE-----
|
||||||
- recipient: age1ml3smrs5mwz4ds84gk0eyss86nwsmp07qh0npxsuae7lfwwpsghssavytw
|
- recipient: age1v2ahkl759cftpcdq4mla2cvmgz4jlnmgj7qtgc9732zxrfvxf3lq76zjpr
|
||||||
enc: |
|
enc: |
|
||||||
-----BEGIN AGE ENCRYPTED FILE-----
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAySjBmaC9rREpUQ3BvWWNU
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBiNWFNMXVsdWJKYXJaMCtM
|
||||||
MWEvM3ZGb2RXZ0dMdWxLRTJCR2VSdyt5VUhBCjBvL3MxZ3pTaFQ4aGdZVnAxUmd3
|
NHRVeVV0d0dXQjhwTk9ZbkpINUxUTkNISXpzCjI3blp0bkZiM1pVcDBYaVkwaUVQ
|
||||||
YUtoZkhEV01TU0drRUdDaFZ5M2tZLzAKLS0tIHpBL3NwV2NhN0QwcHdwbFpQWlZn
|
ZTRicDhmdXpybzI1SjZSdDAyYmR1eEUKLS0tIDdUTGdvQzFXMDBMemJUMTc3MURD
|
||||||
eUNjc2RPOUxLTGowTlRqN3lEdjRLU2cKTTEXmHyhnL/hZGDr8ONrmzdU6Or5xkKY
|
S3FxRUI2eEg3bGs3Rjh2YXhiMnQ5eGcKAHlMDXwb1uULH+lLuWW4dMxofXSbKRMt
|
||||||
GHADDt+LCg8njcZom39Aj4kpCx+f7HlV65glKwr37vZ0sL9KE+O9+w==
|
Ce/mfgDwqERw8h2yotOoSkNSFBQ2kPLu3/NeTsVAfbdSMyp/T3aJ5A==
|
||||||
-----END AGE ENCRYPTED FILE-----
|
-----END AGE ENCRYPTED FILE-----
|
||||||
lastmodified: "2026-01-16T15:38:39Z"
|
lastmodified: "2026-01-16T15:38:39Z"
|
||||||
mac: ENC[AES256_GCM,data:4xaoGvLq1UIdozNqQ7v+pORVPDCk+FZRsCRvZ3C5AZOwSaM+UfDYZcI32AI0K80yFyhVIrrjqylykvXghbpQGAju3mv7+7Tbn5p2gqXrB/m1FuyVe/ftw7SSn8FTGL14cdHuPPkQTvV/u7z1IfX4YAOEGqtWiEfOe4YoWT3xc3A=,iv:dygbKjQ0ljgBPyk2aEIa/Mpbs/At+UzuhYy8Sndx/nk=,tag:jYbROlRxeDxqF1YqrBGL8A==,type:str]
|
mac: ENC[AES256_GCM,data:4xaoGvLq1UIdozNqQ7v+pORVPDCk+FZRsCRvZ3C5AZOwSaM+UfDYZcI32AI0K80yFyhVIrrjqylykvXghbpQGAju3mv7+7Tbn5p2gqXrB/m1FuyVe/ftw7SSn8FTGL14cdHuPPkQTvV/u7z1IfX4YAOEGqtWiEfOe4YoWT3xc3A=,iv:dygbKjQ0ljgBPyk2aEIa/Mpbs/At+UzuhYy8Sndx/nk=,tag:jYbROlRxeDxqF1YqrBGL8A==,type:str]
|
||||||
|
|||||||
1
secrets/ssh/ed25519_deploy.pub
Normal file
1
secrets/ssh/ed25519_deploy.pub
Normal file
@@ -0,0 +1 @@
|
|||||||
|
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIG6scNSRnOprOvqm5DSTSMORvh9c5z0S1GzX1D7u+gMw deploy@portfolio
|
||||||
1
secrets/ssh/ed25519_lidarr-reports.pub
Normal file
1
secrets/ssh/ed25519_lidarr-reports.pub
Normal file
@@ -0,0 +1 @@
|
|||||||
|
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKbCQ/f117hL7Z02Vog1RCaOVUi95beYf//Qppnqf2Ha lidarr-reports@lidarr-reports
|
||||||
@@ -5,6 +5,7 @@ vps:
|
|||||||
server:
|
server:
|
||||||
private: ENC[AES256_GCM,data:wrP/069tuQs3ObYE8Q0MNVxe3+4vZ2HIImoIdZpj1uPgdBknboX1wmANv/k=,iv:FJL5KumHos8PoXra+BB2Uc6YedsF6MD3wWyuugXzJ+E=,tag:nVuTrW2P7JvnWnv6H1SmdQ==,type:str]
|
private: ENC[AES256_GCM,data:wrP/069tuQs3ObYE8Q0MNVxe3+4vZ2HIImoIdZpj1uPgdBknboX1wmANv/k=,iv:FJL5KumHos8PoXra+BB2Uc6YedsF6MD3wWyuugXzJ+E=,tag:nVuTrW2P7JvnWnv6H1SmdQ==,type:str]
|
||||||
public: ENC[AES256_GCM,data:YnKOf9725v9FkzdNPDVf/iinMbY/YWn6ksqEz+mpB4KHVlOvpbV6vLSKRcs=,iv:aWQNy6mT4sxVbzaXKgRzZ9XVsiBCRsOlLORRqC+uiKE=,tag:mLWv6mr3VVfw0J5BrqByXg==,type:str]
|
public: ENC[AES256_GCM,data:YnKOf9725v9FkzdNPDVf/iinMbY/YWn6ksqEz+mpB4KHVlOvpbV6vLSKRcs=,iv:aWQNy6mT4sxVbzaXKgRzZ9XVsiBCRsOlLORRqC+uiKE=,tag:mLWv6mr3VVfw0J5BrqByXg==,type:str]
|
||||||
|
#ENC[AES256_GCM,data:u5SEQfK0Hw==,iv:+qr9WmOzQowZ/JyN1KoWhoyHA2132fmmZzIQy7o5y6k=,tag:9TPVeQgoo2nWQ9dhuYULGw==,type:comment]
|
||||||
home:
|
home:
|
||||||
private: ENC[AES256_GCM,data:YZ0jvBzkMv8Bwc9u3LDJzwSqQvPj8wPUxTIeBFiLYVQQIBjm8aS1dTYuPvo=,iv:mXuW7TVERxOMmGIit3a7Spmbk/EgYuGkO66AWJUnMF0=,tag:xM7C3F3JCiud/A9yPD5ydQ==,type:str]
|
private: ENC[AES256_GCM,data:YZ0jvBzkMv8Bwc9u3LDJzwSqQvPj8wPUxTIeBFiLYVQQIBjm8aS1dTYuPvo=,iv:mXuW7TVERxOMmGIit3a7Spmbk/EgYuGkO66AWJUnMF0=,tag:xM7C3F3JCiud/A9yPD5ydQ==,type:str]
|
||||||
public: ENC[AES256_GCM,data:DcwAHhHjIxFqRL5h7p/0nkFnWiI/iqR8Fws6AuFaxjgUHKYd/6l3D6q/O/0=,iv:bBJ0bsKRiGQUSlRmHqeLQWkOIUNfG5VVpuV6MOtKZO0=,tag:harMG6GDIfclmSq3D36bTw==,type:str]
|
public: ENC[AES256_GCM,data:DcwAHhHjIxFqRL5h7p/0nkFnWiI/iqR8Fws6AuFaxjgUHKYd/6l3D6q/O/0=,iv:bBJ0bsKRiGQUSlRmHqeLQWkOIUNfG5VVpuV6MOtKZO0=,tag:harMG6GDIfclmSq3D36bTw==,type:str]
|
||||||
@@ -13,40 +14,40 @@ sops:
|
|||||||
- recipient: age1lufn6t35gs4wgevyr2gud4eec7lvkn7pgnnv4tja64ww3hef7gqq8fas37
|
- recipient: age1lufn6t35gs4wgevyr2gud4eec7lvkn7pgnnv4tja64ww3hef7gqq8fas37
|
||||||
enc: |
|
enc: |
|
||||||
-----BEGIN AGE ENCRYPTED FILE-----
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBlemJmbnAwUHZHT3ozdWxH
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBXN25MZVQzTmora3o3YjJs
|
||||||
Njh1ZFUvVW8zcVV6SGxrVW1IWW9ZUFBaTEh3CnJsMnFnM0d5YnBKWE5CT2Flang0
|
aUptdGxJY2YxZU5XSjBjczFnTFVVdHVsRkM0CkNFN2JoelQva1ZucUxNNUJsVk9z
|
||||||
TkNZb0xCY2c4Qk1kdXRkRXcvOU1TSW8KLS0tIE1VdGEraW03bnV4VEc5c0ZheFJ0
|
cVZVU0MxL2Y3b2dNRnhJSzZrSVlaRWcKLS0tIG1vTHB1dHNWa0RLR1BRV0hFUVdx
|
||||||
MFJpVTlvTGJ0YXBKSnFFbXhEUEwwSmMKxOtHLbRw5e6dRW4jvqFLsl6UzKZ+mvfR
|
blY1QTNhUGpKZ3EzRHNadStxaCtLb1kKtyXKpZGLtrUo1HE26IWhv8245Bjcwcqe
|
||||||
hwKJ4KEbXuCqwtPQEWk/pF0i4vzrgUP1Cp1Y7BxGGyK9ufyV/CCQIg==
|
IR2WGv7qtnpWZoaFv76LNN7YY1JViy2k2AY+TdLmFQr0Vh2n5+tH1A==
|
||||||
-----END AGE ENCRYPTED FILE-----
|
-----END AGE ENCRYPTED FILE-----
|
||||||
- recipient: age17jlsydpgl35qx5ahc3exu44jt8dfa63chymt6xqp9xx0r6dh347qpg55cz
|
- recipient: age17jlsydpgl35qx5ahc3exu44jt8dfa63chymt6xqp9xx0r6dh347qpg55cz
|
||||||
enc: |
|
enc: |
|
||||||
-----BEGIN AGE ENCRYPTED FILE-----
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA5cnE5VENCMUxxOVZUdC9X
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBZbHVXV2hVTWZlemdCQmI0
|
||||||
QWFMRytGamhaWENZY1Q4STR5L0Jsdk90SlUwCis4ekFWYmMwN2dESXMrVFNIamFG
|
SGwwK0hDYjNkWGJMMTVuWnNMVW1Ebldsd0RRCmNoN2dZN2JiSEpzUzNwcjU3eFVv
|
||||||
RzhET2ZGdGN6b1V1ZHkyOCtDNzBWVjQKLS0tIEF1NGdoU2lqYVdIN3hwRk13SFpP
|
QmNnWERpQVByYlRqUDQrWEF4bkRPQm8KLS0tIHNOaDhQZExuOVJIVXZGQVdFeGhQ
|
||||||
RHNOeDBlSHFpays2VkRuR2RxaGpYZ1EKwxZfRZthZHVuJe3D5pamCSxYo3hyaaVc
|
QXRJRFlZWXJUVW9nVDhOaUFacjFlSzgKYSs6Woc/lAr2ECcrqoMCAwvIbXTpbtTr
|
||||||
I0UvMDMgcDRZuEzV9g1ZEYnaVXg5InyOO0dDZuCYX/HZqTLPiaOIxg==
|
J4ljY3BRCdSzHEMS9IFV2j9nGu8sUrHRsO7V/Kc8i+XmTGZP76LRJA==
|
||||||
-----END AGE ENCRYPTED FILE-----
|
-----END AGE ENCRYPTED FILE-----
|
||||||
- recipient: age15hx530yrqmhm80vsjmffyg9deq9gssj7hl5rsqdnsn3dwegj9qusv4sjf5
|
- recipient: age15hx530yrqmhm80vsjmffyg9deq9gssj7hl5rsqdnsn3dwegj9qusv4sjf5
|
||||||
enc: |
|
enc: |
|
||||||
-----BEGIN AGE ENCRYPTED FILE-----
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBGaUhOcHV2TkYrZWxnOCtI
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBlRGwzbm9rY3hXeHZReWZW
|
||||||
TzF1RVpFY3pSa1Y2MmJjVlpKcWZnWGtOOTJ3CmRnTUpyRms2aUtvS1ZvVXFsb0ZQ
|
VngyN3ZlZDJWekExNkNBdCtLT1J6d0ZYU0hrCi9RRWNYUyt3OGh1ZVNCNWI1U3Vn
|
||||||
U0RiYXM3S0RKQjVwL2hqYllhZENUdmsKLS0tIDNTRHR2ZU1VTzdNNXRDU0xkcTRM
|
dnByZnVpYmV1RndKQUxHbUtrS0F5L3cKLS0tIDJONlcvMllKOWRxdE5ZWUZmaFEx
|
||||||
ckowd2p5bitGYVhMNU9Qc0NUeFFJV3MKPKT1/06/fKpWPOMsRaU/fpyVUf7onWGB
|
S1JCM2x1WDF0Z3c0ODZNb3FKOGNhMlUKGP8P/PUcMM1c4VzXLjLNp/zThu8JCiyQ
|
||||||
0P22NBzP1i5caqSrFnVVeyuhgYxabC4oUKVmjU5QIj1R8Rqh7gworw==
|
iHdz0LBSAha/m23b316z72yg3YD5q+/qDP8KczAv1SG+VvgHDKxpCg==
|
||||||
-----END AGE ENCRYPTED FILE-----
|
-----END AGE ENCRYPTED FILE-----
|
||||||
- recipient: age1ml3smrs5mwz4ds84gk0eyss86nwsmp07qh0npxsuae7lfwwpsghssavytw
|
- recipient: age1v2ahkl759cftpcdq4mla2cvmgz4jlnmgj7qtgc9732zxrfvxf3lq76zjpr
|
||||||
enc: |
|
enc: |
|
||||||
-----BEGIN AGE ENCRYPTED FILE-----
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB3SHdHTDhKQzFUQVdqM0hW
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA5Z3hyOGFJTlpxOWFQaWsx
|
||||||
Tm9QdVozaHViQVRuTExhV1BpdWYvY012enk0CmhjODlUN0FkNldGRG94bVFSTVBv
|
WE9oYmJaaWxURjdmRlVJMUcxaUZKWDZCS1VJCmxWV01DRloyM3lLemJYc2FxcUdH
|
||||||
QUNWZmszRStZN24vZWhnajhIcWdXVDgKLS0tIG9ueVZsT29KRE1iM2oreWtGWGVC
|
M1NZRGxjVUVEUExTWjFaazhRaDdCUXcKLS0tIFF6NW4vSGJSWjN3NHFlOXRUYXhM
|
||||||
SG40OS8wMHlKNmxQa0VScHQrU2NmT2sKt9xw/8jsgnV1cZndqYNiHvIf8VdEJYCl
|
NXZzQmlneDNEb1UvR2NGK0kyY1lsa1kK7IQmyuVxa2hmic4yTeiAcxN41RvMcIDV
|
||||||
UUJ1KPz9mvUx3ny+rK50FSD61U8PHEZm2UC0w+/qkZwRtCx21Ku6dw==
|
Pofrhu7q8VvB/Cxb7FjVs3Ed5Hdz9xQ60mXUKsnJV/rIssm9wx4cfg==
|
||||||
-----END AGE ENCRYPTED FILE-----
|
-----END AGE ENCRYPTED FILE-----
|
||||||
lastmodified: "2025-09-08T00:14:52Z"
|
lastmodified: "2026-02-04T18:37:11Z"
|
||||||
mac: ENC[AES256_GCM,data:O2herKRy4k9ZMuPzzPF5QlBC2isXdRoIsbYLJ/6X7esxtxxgNuAljx4SCR6UMT7pl3G2E33cnnBEkuAIy6SMXOaZNfOuAEJXaCwpRwCXu26lrcTf6n7UdP36GWfIRsR4utD5/vv66ch6MqmQWkW7E5zydy5dOv+BJ4XS/50OUQs=,iv:TscYNQaeI+mBxyobxI1O4wUzRtA27pvjXz27kqMJhA0=,tag:zx/xrYAWJCxYz5HRTKzYfQ==,type:str]
|
mac: ENC[AES256_GCM,data:AlrMK34dWDm5hfVwnQnzk3l8NIRbiVV6KHa6io9S9l07WvC3TYLTOJS6xOi4pkEz6sqQ7IpZU7RRdosxuQp50NmMEt2QYawTHFZIgzFYeKRbl5N5LCu9afC6yTtvG/sT7uenTMhh2qT1JBwebJiUdM9zNVUzWlW5d1SdxrHgIbs=,iv:dvqsDaC+trhY1kheYUEOEwHfCDz0Mu7N0LpfjnKko5g=,tag:tuqyK8vuwSrk1kf+Vi7MKg==,type:str]
|
||||||
unencrypted_suffix: _unencrypted
|
unencrypted_suffix: _unencrypted
|
||||||
version: 3.10.2
|
version: 3.11.0
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
|
|
||||||
### Reference Map
|
### Reference Map
|
||||||
- **Role**: Index mapping core concerns to repo paths for navigation and validation.
|
- **Role**: Index mapping core concerns to repo paths for navigation and validation.
|
||||||
- **Key Fields**: category (apps, dev, scripts, servers, services, shell, network, users, nix, patches), hosts list (emacs, server, workstation, miniserver, galaxy), secrets files, proxy rules, auto-import rules, stylix/schemes, audit checklist entries, navigation links to constitution/playbooks.
|
- **Key Fields**: category (apps, dev, scripts, servers, services, shell, network, users, nix), root paths (patches), hosts list (emacs, server, workstation, miniserver, galaxy, vps), secrets files, proxy rules, auto-import rules, stylix/schemes, audit checklist entries, navigation links to constitution/playbooks.
|
||||||
- **Relationships**: Anchors citations used by Constitution and Playbooks.
|
- **Relationships**: Anchors citations used by Constitution and Playbooks.
|
||||||
|
|
||||||
## Constraints and States
|
## Constraints and States
|
||||||
|
|||||||
@@ -29,3 +29,8 @@
|
|||||||
- **Decision**: Gate SOPS configuration behind `config.my.secureHost` so non-secure hosts skip secret loading.
|
- **Decision**: Gate SOPS configuration behind `config.my.secureHost` so non-secure hosts skip secret loading.
|
||||||
- **Rationale**: Aligns `config/base.nix` behavior with the constitution’s secureHost rules and avoids secret dependency on non-secure hosts.
|
- **Rationale**: Aligns `config/base.nix` behavior with the constitution’s secureHost rules and avoids secret dependency on non-secure hosts.
|
||||||
- **Alternatives considered**: (a) Leave SOPS enabled on all hosts (rejected: violates secureHost contract); (b) Duplicate SOPS logic per host (rejected: increases drift risk).
|
- **Alternatives considered**: (a) Leave SOPS enabled on all hosts (rejected: violates secureHost contract); (b) Duplicate SOPS logic per host (rejected: increases drift risk).
|
||||||
|
|
||||||
|
## Decision 7 (2026-02-07): Module categories and patches location; active hosts update
|
||||||
|
- **Decision**: Treat `patches/` as a root-level directory (not a module category) and update active hosts to include `vps`.
|
||||||
|
- **Rationale**: Repo structure places patches at the root and hosts include `vps`; documentation must reflect actual paths and host inventory.
|
||||||
|
- **Alternatives considered**: (a) Move `patches/` under `modules/` (rejected: would change repo layout); (b) Keep `vps` undocumented (rejected: causes host list drift).
|
||||||
|
|||||||
@@ -89,6 +89,6 @@ An AI or contributor can update the constitution and use-case docs when repo rul
|
|||||||
### Measurable Outcomes
|
### Measurable Outcomes
|
||||||
|
|
||||||
- **SC-001**: An AI with only these docs can describe the correct steps and file locations to add a new server module in under 2 minutes of reading time, matching existing patterns.
|
- **SC-001**: An AI with only these docs can describe the correct steps and file locations to add a new server module in under 2 minutes of reading time, matching existing patterns.
|
||||||
- **SC-002**: The constitution explicitly enumerates 100% of current module categories (apps, dev, scripts, servers, services, shell, network, users, nix, patches) and active hosts (emacs, server, workstation) with their roles.
|
- **SC-002**: The constitution explicitly enumerates 100% of current module categories (apps, dev, scripts, servers, services, shell, network, users, nix), documents the root `patches/` directory, and lists active hosts (emacs, server, workstation, miniserver, galaxy, vps) with their roles.
|
||||||
- **SC-003**: Guidance includes the full secrets file map (certs/env/gallery/homepage/keys/wireguard/secrets) and secureHost behavior with no omissions when audited against the repository.
|
- **SC-003**: Guidance includes the full secrets file map (certs/env/gallery/homepage/keys/wireguard/secrets) and secureHost behavior with no omissions when audited against the repository.
|
||||||
- **SC-004**: Playbook locations and required fields are discoverable via the documented index in ≤2 navigation steps from the top of the spec.
|
- **SC-004**: Playbook locations and required fields are discoverable via the documented index in ≤2 navigation steps from the top of the spec.
|
||||||
|
|||||||
34
specs/004-vps-migration/checklists/requirements.md
Normal file
34
specs/004-vps-migration/checklists/requirements.md
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
# Specification Quality Checklist: VPS Migration
|
||||||
|
|
||||||
|
**Purpose**: Validate specification completeness and quality before proceeding to planning
|
||||||
|
**Created**: 2026-02-04
|
||||||
|
**Feature**: /home/jawz/Development/NixOS/specs/004-vps-migration/spec.md
|
||||||
|
|
||||||
|
## Content Quality
|
||||||
|
|
||||||
|
- [x] No implementation details (languages, frameworks, APIs)
|
||||||
|
- [x] Focused on user value and business needs
|
||||||
|
- [x] Written for non-technical stakeholders
|
||||||
|
- [x] All mandatory sections completed
|
||||||
|
|
||||||
|
## Requirement Completeness
|
||||||
|
|
||||||
|
- [x] No [NEEDS CLARIFICATION] markers remain
|
||||||
|
- [x] Requirements are testable and unambiguous
|
||||||
|
- [x] Success criteria are measurable
|
||||||
|
- [x] Success criteria are technology-agnostic (no implementation details)
|
||||||
|
- [x] All acceptance scenarios are defined
|
||||||
|
- [x] Edge cases are identified
|
||||||
|
- [x] Scope is clearly bounded
|
||||||
|
- [x] Dependencies and assumptions identified
|
||||||
|
|
||||||
|
## Feature Readiness
|
||||||
|
|
||||||
|
- [x] All functional requirements have clear acceptance criteria
|
||||||
|
- [x] User scenarios cover primary flows
|
||||||
|
- [x] Feature meets measurable outcomes defined in Success Criteria
|
||||||
|
- [x] No implementation details leak into specification
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- All checks passed on first review.
|
||||||
38
specs/004-vps-migration/contracts/openapi.yaml
Normal file
38
specs/004-vps-migration/contracts/openapi.yaml
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
openapi: 3.0.3
|
||||||
|
info:
|
||||||
|
title: VPS Migration Verification API
|
||||||
|
version: 0.1.0
|
||||||
|
description: |
|
||||||
|
Optional verification endpoints for migration validation. These describe
|
||||||
|
checks that can be automated; if no API is implemented, treat as a checklist.
|
||||||
|
paths:
|
||||||
|
/verify/proxy:
|
||||||
|
get:
|
||||||
|
summary: Verify reverse proxy routing to host services
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: Proxy mappings resolve to services on host server
|
||||||
|
/verify/firewall:
|
||||||
|
get:
|
||||||
|
summary: Verify nftables parity against the iptables reference
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: Firewall flows match expected allow/deny behavior
|
||||||
|
/verify/vpn:
|
||||||
|
get:
|
||||||
|
summary: Verify VPN peer connectivity and address assignment
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: All peers connect with correct addresses
|
||||||
|
/verify/ssh:
|
||||||
|
get:
|
||||||
|
summary: Verify SSH access for authorized principals
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: Authorized keys allow expected access only
|
||||||
|
/verify/analytics:
|
||||||
|
get:
|
||||||
|
summary: Verify analytics data migrated successfully
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: Historical analytics data present on new server
|
||||||
41
specs/004-vps-migration/data-model.md
Normal file
41
specs/004-vps-migration/data-model.md
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
# Data Model: VPS Migration
|
||||||
|
|
||||||
|
## Host
|
||||||
|
|
||||||
|
- **Fields**: name, role (primary/secondary), publicIp, vpnEndpoint, services[], proxyMappings[], firewallRuleSet
|
||||||
|
- **Rules**: Exactly one primary host for reverse proxying.
|
||||||
|
|
||||||
|
## Service
|
||||||
|
|
||||||
|
- **Fields**: name, enabled, runsOnHost, proxyEnabled, domains[]
|
||||||
|
- **Rules**: Services remain on host server; proxyEnabled true on VPS for all enabled services.
|
||||||
|
|
||||||
|
## ProxyMapping
|
||||||
|
|
||||||
|
- **Fields**: domain, targetService, tlsRequired
|
||||||
|
- **Rules**: domain must be unique across mappings; domain must match service definitions.
|
||||||
|
|
||||||
|
## FirewallRuleSet
|
||||||
|
|
||||||
|
- **Fields**: sourceFile (iptables reference), rules[], appliedHost
|
||||||
|
- **Rules**: Ruleset must be applied as-is; no translation allowed.
|
||||||
|
|
||||||
|
## VPNPeer
|
||||||
|
|
||||||
|
- **Fields**: name, publicKeyRef, allowedIps[]
|
||||||
|
- **Rules**: allowedIps must be unique across peers; publicKeyRef must resolve via secrets system.
|
||||||
|
|
||||||
|
## VPNInterface
|
||||||
|
|
||||||
|
- **Fields**: addressRanges[], listenPort, privateKeyRef
|
||||||
|
- **Rules**: privateKeyRef stored in secrets system; listenPort exposed on VPS.
|
||||||
|
|
||||||
|
## ServiceUser
|
||||||
|
|
||||||
|
- **Fields**: username, group, authorizedKeys[]
|
||||||
|
- **Rules**: deploy uses ed25519_deploy.pub; lidarr-reports uses ed25519_lidarr-reports.pub.
|
||||||
|
|
||||||
|
## MigrationChecklistItem
|
||||||
|
|
||||||
|
- **Fields**: task, verificationStep, status
|
||||||
|
- **Rules**: each migration task must have a verification step.
|
||||||
52
specs/004-vps-migration/plan.md
Normal file
52
specs/004-vps-migration/plan.md
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
# Implementation Plan: VPS Migration
|
||||||
|
|
||||||
|
**Branch**: `004-vps-migration` | **Date**: 2026-02-04 | **Spec**: /home/jawz/Development/NixOS/specs/004-vps-migration/spec.md
|
||||||
|
**Input**: Feature specification from `/specs/004-vps-migration/spec.md`
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
Migrate VPS responsibilities to the new NixOS host by making it the primary reverse-proxy host (nginx only), mirroring the existing iptables ruleset via nftables/NixOS equivalents, enabling wireguard with secret-managed keys, and restoring SSH/service-user access, while keeping all services running on the host server. Provide validation steps, review historical configs for gaps, and document analytics data migration.
|
||||||
|
|
||||||
|
## Technical Context
|
||||||
|
|
||||||
|
**Language/Version**: Nix (flakes; nixpkgs 25.11)
|
||||||
|
**Primary Dependencies**: NixOS modules, sops-nix, nginx, wireguard, openssh, nftables (iptables reference)
|
||||||
|
**Storage**: Files (configuration and secrets)
|
||||||
|
**Testing**: Manual validation steps (no automated test harness)
|
||||||
|
**Target Platform**: Linux server (NixOS)
|
||||||
|
**Project Type**: configuration repo
|
||||||
|
**Performance Goals**: N/A (configuration change)
|
||||||
|
**Constraints**: Services remain on host server; VPS only terminates proxy and exposes wireguard port; nftables parity required
|
||||||
|
**Scale/Scope**: Single VPS + host server, small set of VPN peers and admin SSH principals
|
||||||
|
|
||||||
|
## Constitution Check
|
||||||
|
|
||||||
|
No enforceable constitution rules are defined (placeholders only). Gate passes by default.
|
||||||
|
|
||||||
|
Post-design check: unchanged (no enforceable gates found).
|
||||||
|
|
||||||
|
## Project Structure
|
||||||
|
|
||||||
|
### Documentation (this feature)
|
||||||
|
|
||||||
|
```text
|
||||||
|
specs/004-vps-migration/
|
||||||
|
├── plan.md
|
||||||
|
├── research.md
|
||||||
|
├── data-model.md
|
||||||
|
├── quickstart.md
|
||||||
|
├── contracts/
|
||||||
|
└── tasks.md
|
||||||
|
```
|
||||||
|
|
||||||
|
### Source Code (repository root)
|
||||||
|
|
||||||
|
```text
|
||||||
|
hosts/
|
||||||
|
modules/
|
||||||
|
secrets/
|
||||||
|
iptables (reference ruleset)
|
||||||
|
scripts/
|
||||||
|
```
|
||||||
|
|
||||||
|
**Structure Decision**: Use the existing NixOS configuration layout (`hosts/`, `modules/`, `secrets/`) and the root `iptables` ruleset file as the reference for nftables parity.
|
||||||
107
specs/004-vps-migration/quickstart.md
Normal file
107
specs/004-vps-migration/quickstart.md
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
# Quickstart: VPS Migration
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
- Access to this repo and the new VPS host configuration
|
||||||
|
- Existing iptables ruleset file available at repo root (reference for nftables parity): `iptables`
|
||||||
|
- VPN keys present in the secrets system
|
||||||
|
- SSH public keys present in `secrets/ssh/`
|
||||||
|
|
||||||
|
## Steps
|
||||||
|
|
||||||
|
1. Review the spec and clarifications:
|
||||||
|
- `/home/jawz/Development/NixOS/specs/004-vps-migration/spec.md`
|
||||||
|
|
||||||
|
2. Ensure secrets are available:
|
||||||
|
- VPN private/public keys are stored in the secrets system
|
||||||
|
- `secrets/ssh/ed25519_deploy.pub` and `secrets/ssh/ed25519_lidarr-reports.pub` exist
|
||||||
|
|
||||||
|
3. Update host configuration:
|
||||||
|
- Set new VPS as primary reverse proxy host
|
||||||
|
- Enable proxying for all enabled services (services remain on host server)
|
||||||
|
- Apply nftables/NixOS firewall rules derived from the iptables reference
|
||||||
|
- Enable wireguard on VPS and expose port
|
||||||
|
- Add service users and admin SSH keys
|
||||||
|
- Update VPS public IP to `45.79.25.87` in SSH configuration
|
||||||
|
- Update host server VPN client to target the new VPS
|
||||||
|
|
||||||
|
4. Provide and review legacy proxy config snapshot:
|
||||||
|
- Supply caddy files for subdomain comparison
|
||||||
|
- Treat caddy as migration input only; nginx is the only proxy target for NixOS runtime
|
||||||
|
|
||||||
|
## Caddy vs Nix Subdomain Comparison (from provided caddy/ directory)
|
||||||
|
|
||||||
|
**Caddy-only domains (present in caddy, not found in current Nix server hosts):**
|
||||||
|
- danilo-reyes.com
|
||||||
|
- www.danilo-reyes.com
|
||||||
|
- blog.danilo-reyes.com
|
||||||
|
- www.blog.danilo-reyes.com
|
||||||
|
- mb-report.lebubu.org
|
||||||
|
- torrent.lebubu.org
|
||||||
|
|
||||||
|
**Nix-only domains (present in Nix server hosts, not in caddy config):**
|
||||||
|
- auth-proxy.lebubu.org
|
||||||
|
- comments.danilo-reyes.com
|
||||||
|
- flix.rotehaare.art
|
||||||
|
- 55a608953f6d64c199.lebubu.org
|
||||||
|
- pYLemuAfsrzNBaH77xSu.lebubu.org
|
||||||
|
- bookmarks.lebubu.org
|
||||||
|
- drpp.lebubu.org
|
||||||
|
- portfolio.lebubu.org
|
||||||
|
- qampqwn4wprhqny8h8zj.lebubu.org
|
||||||
|
- requests.lebubu.org
|
||||||
|
- start.lebubu.org
|
||||||
|
- sync.lebubu.org
|
||||||
|
- tranga.lebubu.org
|
||||||
|
|
||||||
|
**Notes:**
|
||||||
|
- `auth-proxy.lebubu.org` appears only in `15-private.caddyfile__` (not imported by Caddy), so it is currently inactive in caddy.
|
||||||
|
- `danilo-reyes.com` and `blog.danilo-reyes.com` are handled as static sites in caddy; Nix has `my.websites.portfolio` and `isso` which may need mapping to these domains.
|
||||||
|
- `mb-report.lebubu.org` and `torrent.lebubu.org` are present in caddy but no matching Nix server host was found.
|
||||||
|
|
||||||
|
5. Migrate analytics data:
|
||||||
|
- Identify the analytics system (e.g., Plausible) and its data store location or database
|
||||||
|
- Freeze writes during export (stop the analytics service or enable maintenance mode)
|
||||||
|
- Export analytics data from the existing server (db dump or data directory archive)
|
||||||
|
- Transfer the export to the new server using the secure path already used for secrets/config
|
||||||
|
- Import the data on the new server and restart the analytics service
|
||||||
|
- Validate historical data is present (date range coverage, dashboard counts, and sample events)
|
||||||
|
|
||||||
|
6. Run verification steps for each task (per spec FR-012).
|
||||||
|
|
||||||
|
## Clarification Candidates From History Review
|
||||||
|
|
||||||
|
- `opentracker` was installed and enabled (`systemctl enable --now opentracker`) with firewall rules for TCP/UDP `6969`; confirm if tracker service is still required on NixOS.
|
||||||
|
- `ip6tables` was enabled on Fedora (`systemctl enable ip6tables`); confirm if equivalent IPv6 policy is required on VPS.
|
||||||
|
- `net.ipv4.conf.wg0.rp_filter=0` was set during forwarding troubleshooting; confirm if this sysctl needs to be persisted on VPS.
|
||||||
|
- Fedora-specific SELinux SSH port handling (`semanage ssh_port_t`) appears in history; confirm it can remain excluded on NixOS.
|
||||||
|
|
||||||
|
## Verification Steps
|
||||||
|
|
||||||
|
- **T001**: `test -f ./iptables && test -f ./secrets/ssh/ed25519_deploy.pub && test -f ./secrets/ssh/ed25519_lidarr-reports.pub && test -f ./secrets/wireguard.yaml`
|
||||||
|
- **T002**: verify this section exists in `/home/jawz/Development/NixOS/specs/004-vps-migration/quickstart.md`
|
||||||
|
- **T003**: `rg -n "mainServer|enableProxy" hosts/server/toggles.nix modules/modules.nix`
|
||||||
|
- **T004**: `rg -n "wireguard|wg0|services.wireguard" modules/services/wireguard.nix hosts/vps/configuration.nix`
|
||||||
|
- **T005**: `rg -n "vps|45.79.25.87|programs.ssh" config/jawz.nix modules/modules.nix`
|
||||||
|
- **T006**: `rg -n "/etc/caddy/Caddyfile.d" sudo_hist jawz_hist`
|
||||||
|
- **T007**: `rg -n 'mainServer = "vps"' hosts/server/toggles.nix modules/modules.nix`
|
||||||
|
- **T008**: `rg -n "enableProxy = true" hosts/vps/toggles.nix hosts/vps/configuration.nix hosts/server/toggles.nix`
|
||||||
|
- **T009**: ensure Caddy vs Nix comparison section remains in this file
|
||||||
|
- **T010**: `rg -n "iqQCY4iAWO-ca/pem|certPath|proxyReversePrivate" modules/network/nginx.nix modules/servers`
|
||||||
|
- **T011**: `rg -n "nftables|forwardPorts|vps-snat" hosts/vps/configuration.nix`
|
||||||
|
- **T012**: `rg -n "services.wireguard.enable = true" hosts/vps/configuration.nix`
|
||||||
|
- **T013**: confirm `wireguard/private` exists in `secrets/wireguard.yaml`
|
||||||
|
- **T014**: `rg -n "10.77.0.1/24|10.8.0.1/24|10.9.0.1/24|AllowedIPs|allowedIPs" modules/services/wireguard.nix`
|
||||||
|
- **T015**: `rg -n "users\\.deploy|users\\.lidarr-reports|ed25519_deploy|ed25519_lidarr-reports" hosts/vps/configuration.nix`
|
||||||
|
- **T016**: `rg -n "workstation|server|deacero|galaxy" hosts/vps/configuration.nix`
|
||||||
|
- **T017**: `rg -n "ports = \\[ 3456 \\]|PermitRootLogin = \"no\"" hosts/vps/configuration.nix`
|
||||||
|
- **T018**: `rg -n "sudo-rs\\.extraRules|nixos-rebuild|nixremote" hosts/vps/configuration.nix`
|
||||||
|
- **T019**: `rg -n "nixworkstation" hosts/vps/configuration.nix`
|
||||||
|
- **T020**: `rg -n "45\\.33\\.0\\.228" modules/modules.nix config/jawz.nix`
|
||||||
|
- **T021**: `rg -n "endpoint = .*my\\.ips\\.vps" hosts/server/configuration.nix`
|
||||||
|
- **T022**: verify "Clarification Candidates From History Review" section exists in this file
|
||||||
|
- **T023**: `rg -n "Migrate analytics data|Export analytics|Import.*analytics|Validate historical data" /home/jawz/Development/NixOS/specs/004-vps-migration/quickstart.md`
|
||||||
|
- **T024**: verify each task from T001-T026 has a corresponding verification line in this section
|
||||||
|
- **T025**: `rg -n "caddy|Caddy" README.org docs || true` and confirm no active-proxy references remain outside legacy migration notes
|
||||||
|
- **T026**: `rg -n "T0[0-2][0-9]" /home/jawz/Development/NixOS/specs/004-vps-migration/tasks.md` and confirm each task mentions at least one concrete path
|
||||||
|
- **T027**: `rg -n "modules/websites|danilo-reyes.com|blog.danilo-reyes.com|mb-report.lebubu.org" modules/websites hosts/vps/toggles.nix`
|
||||||
31
specs/004-vps-migration/research.md
Normal file
31
specs/004-vps-migration/research.md
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
# Research: VPS Migration
|
||||||
|
|
||||||
|
## Decision 1: Reverse proxy role
|
||||||
|
|
||||||
|
- **Decision**: New VPS runs nginx as the primary reverse proxy; services remain on the host server.
|
||||||
|
- **Rationale**: Matches the clarified scope and minimizes service migration risk while restoring proxy functionality.
|
||||||
|
- **Alternatives considered**: Migrating services to VPS; keeping old proxy (caddy) on Fedora VPS.
|
||||||
|
|
||||||
|
## Decision 2: Firewall parity
|
||||||
|
|
||||||
|
- **Decision**: Use the existing iptables ruleset as the source of truth and implement equivalent nftables/NixOS rules on the new VPS.
|
||||||
|
- **Rationale**: Ensures exact behavioral parity for complex routing and hot-swap behavior.
|
||||||
|
- **Alternatives considered**: Translating to another firewall system; partial translation with mixed rules.
|
||||||
|
|
||||||
|
## Decision 3: VPN key handling
|
||||||
|
|
||||||
|
- **Decision**: Store VPN keys only in the existing secrets system; no plaintext keys in config.
|
||||||
|
- **Rationale**: Preserves confidentiality and aligns with encrypted secrets workflow.
|
||||||
|
- **Alternatives considered**: Plaintext inline keys; separate unmanaged secrets store.
|
||||||
|
|
||||||
|
## Decision 4: Admin SSH principals
|
||||||
|
|
||||||
|
- **Decision**: Limit admin SSH authorized_keys entries to workstation, server, deacero, and galaxy.
|
||||||
|
- **Rationale**: Keeps access scope bounded to explicitly requested principals.
|
||||||
|
- **Alternatives considered**: Auto-adding other hosts found in config; adding only after confirmation.
|
||||||
|
|
||||||
|
## Decision 5: Analytics (Plausible) migration
|
||||||
|
|
||||||
|
- **Decision**: Migrate existing analytics data to the new server.
|
||||||
|
- **Rationale**: Preserves historical reporting and continuity of metrics.
|
||||||
|
- **Alternatives considered**: Fresh start with no history; read-only legacy instance for history.
|
||||||
177
specs/004-vps-migration/spec.md
Normal file
177
specs/004-vps-migration/spec.md
Normal file
@@ -0,0 +1,177 @@
|
|||||||
|
# Feature Specification: VPS Migration
|
||||||
|
|
||||||
|
**Feature Branch**: `004-vps-migration`
|
||||||
|
**Created**: 2026-02-04
|
||||||
|
**Status**: Draft
|
||||||
|
**Input**: User description: "start feature branch 004, the git fetch command will fail, so force 004. Feature 003 added a new hosts vps, as a linode host, I want to now fully migrate my existing fedora vps to this new nixos vps. to do so I want to bring in the configurations fedora vps has. 1. right now the nginx logic of my servers is disabled, because I let the fedora vps handle the reverse proxy through caddy. But I dont want that caddy logic, on nixos I want to let nginx take care of the reverse proxies, plus the logic is already backed in, there is a isLocal logic to the factory, and I dont remember exactly the name of the code. but there is some flag under the my. options that specifies the mainHost, the constitution mentions that mainHost is the host handling nginx and because the vps will be it, then main host needs to become vps, I think before it was miniserver. This change means, that all the currently enabled servers on the toggles.nix from the host server, should have the enableProxy flag on vps (double check the logic) this should make it so, that nginx runs on vps, and the servers run on server. 2. Add a step to ask me for the caddy files, just to check that the subdomains caddy handles for each server match the subdomains on the servers/.*nix files. 3. I use iptables on the fedora vps, and the nixos vps, well I dont mind you using another firewall but there are some complex firewall rules that I need them to work 100% as the original vps, the rules will be on a file named iptables (treat this as the reference ruleset for nftables parity), this is perhaps the most important step, otherwise the complex network configuration this vps has wont be able to hot swap and serve my servers to the world.
|
||||||
|
|
||||||
|
4. modify the existing wireguard.nix module, doublecheck that isnt toggled anywhere, toggle it on vps and add this configuration to it
|
||||||
|
[Interface]
|
||||||
|
#DNS = 10.77.0.1
|
||||||
|
Address = 10.77.0.1/24, 10.8.0.1/24, 10.9.0.1/24
|
||||||
|
ListenPort = 51820
|
||||||
|
PrivateKey = aDQHN3DfAGEFjVHRKIJ34CJKPcKx7HdYzkEbRNBNWGw=
|
||||||
|
|
||||||
|
# me
|
||||||
|
[Peer]
|
||||||
|
PublicKey = OUiqluRaS4hmGvLJ3csQrnIM3Zzet50gsqtTABaUkH4=
|
||||||
|
AllowedIPs = 10.77.0.2/32
|
||||||
|
|
||||||
|
# friends
|
||||||
|
[Peer] # 7351
|
||||||
|
PublicKey = rFgT6TXzRazK6GMazMNGjtOvzAAPST0LvCfN7QXsLho=
|
||||||
|
AllowedIPs = 10.8.0.2/32
|
||||||
|
[Peer]
|
||||||
|
PublicKey = R1CTx5+CXivMI6ZEmRYsyFUFILhe6Qnub0iEIRvvrEY=
|
||||||
|
AllowedIPs = 10.8.0.3/32
|
||||||
|
[Peer]
|
||||||
|
PublicKey = ecPNSacD6yVwpnLBs171z0xkw9M1DXKh/Kn70cIBcwA=
|
||||||
|
AllowedIPs = 10.8.0.4/32
|
||||||
|
[Peer]
|
||||||
|
PublicKey = yg+2miZCrx89znFaUlU/le/7UIPgEAMY74fZfEwz8g4=
|
||||||
|
AllowedIPs = 10.8.0.5/32
|
||||||
|
|
||||||
|
# # gooners
|
||||||
|
# [Peer]
|
||||||
|
# PublicKey = GawtOvsZ75avelIri5CjGoPXd8AFpi9qlZ6dSsqUISE=
|
||||||
|
# AllowedIPs = 10.77.0.2/32, 10.9.0.2/32
|
||||||
|
|
||||||
|
can I use sops to encrypt the public and private keys? if so, on modules.nix you will see that the ips on that wireguard config correspond to wg-friend1...n when you get to this step pause and tell me to create the sops secrets for these public keys.
|
||||||
|
|
||||||
|
5. I have two cicds on this server
|
||||||
|
drwxrwxr-x. 11 deploy www-data 4096 Dec 26 20:47 blog
|
||||||
|
drwxr-xr-x. 2 lidarr-reports lidarr-reports 4096 Nov 11 17:52 lidarr-mb-gap
|
||||||
|
drwxrwxr-x. 12 deploy www-data 4096 Dec 26 21:01 portfolio
|
||||||
|
I need you to create the service users and groups for deploy and lidarr-reports.
|
||||||
|
in those, I need you to add ./secrets/ssh/ed25519_deploy.pub to authorized_keys for the user deploy
|
||||||
|
and for lidarr-reports ed25519_lidarr-reports.pub
|
||||||
|
|
||||||
|
6. similar to every other host, add ssh login authorized_keys for workstation, server, deacero, galaxy and check if Im missing one. Because this will replace the ssh vps on the ssh config, you need to replace the existing vps ip with 45.79.25.87. 7. change the configuration on the host server, so that its wireguard session, connects to this server (i think will ve done automagically when the ip changes right?) 8. Ive added sudo_hist and jawz_hist, which are a dump of the histfile of this server, just check if there is a configuration that Im missing, something I did on there that I missed, and add it to the clarification list, so when I run clarify I tell you if I want that or not, granted lots of those commands are trial and error, so I think I have everything. 9. I have setup a plausible server, write the steps necesary to migrate it, I dont know.
|
||||||
|
|
||||||
|
10. add verification steps for every task we did, when youre done and"
|
||||||
|
|
||||||
|
## Clarifications
|
||||||
|
|
||||||
|
### Session 2026-02-04
|
||||||
|
|
||||||
|
- Q: Are any services being migrated to the new VPS, and what does enableProxy do? → A: No services are migrated; enableProxy only configures nginx on the VPS, wireguard exposes the port, and services continue running on the host server.
|
||||||
|
- Q: How should the analytics service be migrated? → A: Migrate existing analytics data to the new server.
|
||||||
|
- Q: How should firewall parity be achieved on the new VPS? → A: Use the existing iptables ruleset as the source of truth and implement equivalent nftables/NixOS firewall rules; document any intentional deviations.
|
||||||
|
- Q: Where should VPN keys be stored? → A: Preserve keys only in the existing secrets system.
|
||||||
|
- Q: Which admin hosts should receive SSH authorized_keys entries? → A: Only the listed hosts (workstation, server, deacero, galaxy).
|
||||||
|
|
||||||
|
## User Scenarios & Testing *(mandatory)*
|
||||||
|
|
||||||
|
### User Story 1 - Migrate VPS as Primary Host (Priority: P1)
|
||||||
|
|
||||||
|
As an operator, I want the new VPS to become the primary host for reverse proxying and networking while services continue running on the host server, so public traffic and internal tunnels continue working after the migration.
|
||||||
|
|
||||||
|
**Why this priority**: This is the core migration goal and failure would cause outages.
|
||||||
|
|
||||||
|
**Independent Test**: Can be fully tested by switching the primary host role to the new VPS and verifying proxy and tunnel connectivity without depending on the other stories.
|
||||||
|
|
||||||
|
**Acceptance Scenarios**:
|
||||||
|
|
||||||
|
1. **Given** the new VPS is designated as the primary host, **When** proxying is enabled, **Then** public endpoints resolve through the new VPS while services remain on the host server.
|
||||||
|
2. **Given** the previous VPS is no longer handling proxying, **When** traffic is routed through the new VPS, **Then** no service loses external access.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### User Story 2 - Preserve Firewall Behavior (Priority: P1)
|
||||||
|
|
||||||
|
As an operator, I want the firewall behavior on the new VPS to match the existing VPS so that all current network paths continue to function.
|
||||||
|
|
||||||
|
**Why this priority**: Firewall parity is critical to avoid breaking complex routing and hot-swap behavior.
|
||||||
|
|
||||||
|
**Independent Test**: Can be fully tested by comparing allowed/blocked traffic and confirming all required network paths remain functional.
|
||||||
|
|
||||||
|
**Acceptance Scenarios**:
|
||||||
|
|
||||||
|
1. **Given** the firewall rules are applied to the new VPS, **When** all known inbound and outbound paths are exercised, **Then** they behave identically to the existing VPS.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### User Story 3 - Restore Secure Access and VPN Peers (Priority: P2)
|
||||||
|
|
||||||
|
As an operator, I want VPN peers and SSH access to be configured on the new VPS so administration and CI/CD access remain available.
|
||||||
|
|
||||||
|
**Why this priority**: Secure access is required for operating and deploying services.
|
||||||
|
|
||||||
|
**Independent Test**: Can be fully tested by connecting each VPN peer and verifying SSH access for each authorized user.
|
||||||
|
|
||||||
|
**Acceptance Scenarios**:
|
||||||
|
|
||||||
|
1. **Given** the VPN configuration is enabled on the new VPS, **When** each peer connects, **Then** each peer receives the correct addresses and can reach intended resources.
|
||||||
|
2. **Given** service users and admin users are created on the new VPS, **When** their authorized keys are used, **Then** SSH access succeeds with the expected permissions.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### User Story 4 - Capture Migration Gaps and Validation (Priority: P3)
|
||||||
|
|
||||||
|
As an operator, I want a checklist of potential missing configuration from existing server history and clear verification steps so the migration is safe and complete.
|
||||||
|
|
||||||
|
**Why this priority**: This reduces risk of overlooked manual changes and provides confidence during cutover.
|
||||||
|
|
||||||
|
**Independent Test**: Can be fully tested by running the verification steps and confirming no missing items remain.
|
||||||
|
|
||||||
|
**Acceptance Scenarios**:
|
||||||
|
|
||||||
|
1. **Given** historical command logs are reviewed, **When** likely missing configurations are identified, **Then** they are listed as clarifications for user confirmation.
|
||||||
|
2. **Given** verification steps are provided for each task, **When** the operator executes them, **Then** each migration task can be validated.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Edge Cases
|
||||||
|
|
||||||
|
- What happens when a subdomain mapping differs between the previous proxy configuration and the current service definitions?
|
||||||
|
- How does the system handle a firewall rule that is ambiguous or conflicts with existing policy?
|
||||||
|
- What happens if an SSH key file is missing or invalid for a service user?
|
||||||
|
|
||||||
|
## Requirements *(mandatory)*
|
||||||
|
|
||||||
|
### Functional Requirements
|
||||||
|
|
||||||
|
- **FR-001**: The system MUST designate the new VPS as the primary host for reverse proxying and ensure all enabled services are routed through it without relocating the services.
|
||||||
|
- **FR-002**: The system MUST ensure proxy configuration is enabled for all services currently enabled on the host server so traffic flows through the new VPS while services remain on the host server.
|
||||||
|
- **FR-003**: The system MUST request existing proxy configuration files for verification and flag any subdomain mismatches against current service definitions.
|
||||||
|
- **FR-004**: The system MUST mirror the existing iptables behavior on the new VPS using nftables/NixOS firewall rules and document any intentional deviations from the source ruleset.
|
||||||
|
- **FR-005**: The system MUST enable the VPN configuration on the new VPS with the specified peer addresses and ensure each peer is uniquely identified.
|
||||||
|
- **FR-006**: The system MUST support encrypting sensitive VPN keys and pause for user-provided secret material when required.
|
||||||
|
- **FR-015**: The system MUST store VPN keys only in the existing secrets system and must not place them in plaintext configuration.
|
||||||
|
- **FR-007**: The system MUST create service users and groups for deployment workflows and grant SSH access via specified public keys.
|
||||||
|
- **FR-008**: The system MUST configure SSH access for all standard admin hosts and update the VPS connection target to the new public IP.
|
||||||
|
- **FR-016**: The system MUST grant SSH access only to workstation, server, deacero, and galaxy admin hosts.
|
||||||
|
- **FR-017**: The system MUST configure SSHD to use port 3456 and disable root/password authentication to match the existing VPS security posture.
|
||||||
|
- **FR-018**: The system MUST harden remote rebuild access by using a non-root SSH user with least-privilege access for rebuild operations.
|
||||||
|
- **FR-009**: The system MUST update dependent host configurations so existing VPN client connections target the new VPS.
|
||||||
|
- **FR-010**: The system MUST review provided history logs and produce a clarification list of potential missing configurations.
|
||||||
|
- **FR-011**: The system MUST document migration steps for the analytics service and include them in the migration plan.
|
||||||
|
- **FR-013**: The system MUST include analytics data migration as part of the analytics service migration steps.
|
||||||
|
- **FR-012**: The system MUST provide verification steps for each migration task performed.
|
||||||
|
|
||||||
|
### Key Entities *(include if feature involves data)*
|
||||||
|
|
||||||
|
- **Host**: A server instance that can be assigned primary or secondary roles and hosts services.
|
||||||
|
- **Service**: A deployable workload with external endpoints and internal configuration.
|
||||||
|
- **Proxy Mapping**: The set of subdomains and routing rules that map public traffic to services.
|
||||||
|
- **Firewall Rule Set**: The collection of allowed and blocked network flows required for the VPS.
|
||||||
|
- **VPN Peer**: A client identity with assigned addresses and access constraints.
|
||||||
|
- **SSH Key**: A public key used for authenticated access to a user account.
|
||||||
|
- **Migration Checklist**: A list of tasks and verification steps that confirm readiness.
|
||||||
|
|
||||||
|
## Success Criteria *(mandatory)*
|
||||||
|
|
||||||
|
### Measurable Outcomes
|
||||||
|
|
||||||
|
- **SC-001**: 100% of services previously reachable via the old VPS are reachable via the new VPS after cutover.
|
||||||
|
- **SC-002**: All documented firewall flows (inbound and outbound) pass or block with the same outcomes as the old VPS.
|
||||||
|
- **SC-003**: 100% of configured VPN peers can connect and reach required internal addresses.
|
||||||
|
- **SC-004**: 100% of authorized SSH users can authenticate using their specified keys.
|
||||||
|
- **SC-005**: Migration verification steps can be completed in a single run without unresolved failures.
|
||||||
|
|
||||||
|
## Assumptions
|
||||||
|
|
||||||
|
- The existing proxy configuration files will be provided by the user for comparison.
|
||||||
|
- The firewall rules from the existing VPS are authoritative and should be mirrored on the new VPS, even if implemented via nftables equivalents.
|
||||||
|
- The list of standard admin hosts for SSH access is complete unless the review identifies an omission.
|
||||||
|
- The analytics service migration steps are documentation-only and do not require immediate cutover.
|
||||||
93
specs/004-vps-migration/tasks.md
Normal file
93
specs/004-vps-migration/tasks.md
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
# Tasks: VPS Migration
|
||||||
|
|
||||||
|
**Branch**: `004-vps-migration`
|
||||||
|
**Date**: 2026-02-04
|
||||||
|
**Spec**: /home/jawz/Development/NixOS/specs/004-vps-migration/spec.md
|
||||||
|
**Plan**: /home/jawz/Development/NixOS/specs/004-vps-migration/plan.md
|
||||||
|
|
||||||
|
## Implementation Strategy
|
||||||
|
|
||||||
|
Deliver MVP as User Story 1 (primary host reverse proxy + keep services on host server). Then complete firewall parity (US2), secure access (US3), and migration gap review + verification (US4).
|
||||||
|
|
||||||
|
## Phase 1: Setup
|
||||||
|
|
||||||
|
- [x] T001 Confirm baseline files exist: iptables (reference ruleset), secrets/ssh/ed25519_deploy.pub, secrets/ssh/ed25519_lidarr-reports.pub, secrets system entries for VPN keys
|
||||||
|
- [x] T002 Create working checklist placeholder for verification steps in /home/jawz/Development/NixOS/specs/004-vps-migration/tasks.md (this file)
|
||||||
|
|
||||||
|
## Phase 2: Foundational
|
||||||
|
|
||||||
|
- [x] T003 [P] Review mainServer and enableProxy options in hosts/server/toggles.nix and modules/modules.nix
|
||||||
|
- [x] T004 [P] Review wireguard module in modules/services/wireguard.nix and VPS host config in hosts/vps/configuration.nix
|
||||||
|
- [x] T005 [P] Review SSH host/IP settings in config/jawz.nix and modules/modules.nix for vps IP updates
|
||||||
|
- [x] T006 [P] Review caddy file list references in ./jawz_hist and ./sudo_hist to prepare subdomain comparison inputs
|
||||||
|
|
||||||
|
## Phase 3: User Story 1 (P1) - Primary VPS reverse proxy
|
||||||
|
|
||||||
|
**Story goal**: New VPS is primary reverse-proxy host (nginx only) while services remain on host server.
|
||||||
|
|
||||||
|
**Independent test criteria**: Proxy mappings resolve through VPS to host server services without relocating services.
|
||||||
|
|
||||||
|
- [x] T007 [US1] Set mainServer to \"vps\" in hosts/server/toggles.nix
|
||||||
|
- [x] T008 [US1] Enable proxying on VPS by setting my.enableProxy = true in hosts/vps/configuration.nix and ensure services in hosts/server/toggles.nix have enableProxy = true
|
||||||
|
- [x] T009 [US1] Capture provided caddy config files (e.g., /etc/caddy/Caddyfile.d/*) and compare subdomains to modules/servers/*.nix domain definitions; document mismatches in specs/004-vps-migration/quickstart.md
|
||||||
|
- [x] T010 [US1] Add shared client certificate handling from modules/servers/synapse.nix into the factory or shared module and apply it to mTLS-protected sites (use secrets/certs.yaml for client CA)
|
||||||
|
|
||||||
|
## Phase 4: User Story 2 (P1) - Firewall parity
|
||||||
|
|
||||||
|
**Story goal**: Firewall behavior on new VPS matches old VPS by implementing nftables/NixOS rules derived from the iptables reference.
|
||||||
|
|
||||||
|
**Independent test criteria**: Known inbound/outbound flows match existing VPS behavior.
|
||||||
|
|
||||||
|
- [x] T011 [US2] Apply firewall parity to VPS configuration using nftables/NixOS rules derived from the repo root iptables reference and document any intentional deviations
|
||||||
|
|
||||||
|
## Phase 5: User Story 3 (P2) - Secure access and VPN peers
|
||||||
|
|
||||||
|
**Story goal**: Wireguard enabled on VPS with secrets-managed keys; SSH access for service users and admin hosts.
|
||||||
|
|
||||||
|
**Independent test criteria**: VPN peers connect with correct addresses; SSH keys authenticate as expected.
|
||||||
|
|
||||||
|
- [x] T012 [US3] Enable wireguard module on VPS in hosts/vps/configuration.nix (my.services.wireguard.enable = true) and ensure listen port exposed
|
||||||
|
- [x] T013 [US3] Add sops secrets entries for wireguard keys in secrets/wireguard.yaml and confirm user-provided key material
|
||||||
|
- [x] T014 [US3] Update wireguard peer configuration in modules/services/wireguard.nix using sops secrets refs for public/private keys (no plaintext)
|
||||||
|
- [x] T015 [US3] Add service users and groups deploy and lidarr-reports with authorized_keys in hosts/vps/configuration.nix using secrets/ssh/ed25519_deploy.pub and secrets/ssh/ed25519_lidarr-reports.pub
|
||||||
|
- [x] T016 [US3] Add admin SSH authorized_keys for workstation, server, deacero, galaxy in hosts/vps/configuration.nix
|
||||||
|
- [x] T017 [US3] Configure sshd port and auth settings in hosts/vps/configuration.nix to match: Port 3456, PermitRootLogin no, PasswordAuthentication no
|
||||||
|
- [x] T018 [US3] Harden remote rebuild access by switching to a non-root SSH user for rebuilds (nixremote) and requiring sudo for nixos-rebuild in hosts/vps/configuration.nix and modules/users/nixremote.nix
|
||||||
|
- [x] T019 [US3] Restrict SSH access for remote rebuilds by limiting allowed users/keys for nixremote (update inputs.self.lib.getSshKeys list in hosts/vps/configuration.nix)
|
||||||
|
- [x] T020 [US3] Update VPS IP to 45.79.25.87 in modules/modules.nix and config/jawz.nix SSH host entry
|
||||||
|
- [x] T021 [US3] Update host server wireguard client configuration in hosts/server/configuration.nix to target the new VPS endpoint
|
||||||
|
|
||||||
|
## Phase 6: User Story 4 (P3) - Migration gaps and verification
|
||||||
|
|
||||||
|
**Story goal**: Identify missing configuration from history logs and provide verification steps for every task.
|
||||||
|
|
||||||
|
**Independent test criteria**: Clarification list exists and each task has a verification step.
|
||||||
|
|
||||||
|
- [x] T022 [US4] Review sudo_hist and jawz_hist for missing configuration; record clarification list in specs/004-vps-migration/quickstart.md
|
||||||
|
- [x] T023 [US4] Document analytics data migration steps (export, import, validate) in specs/004-vps-migration/quickstart.md
|
||||||
|
- [x] T024 [US4] Add verification steps for each task in specs/004-vps-migration/quickstart.md
|
||||||
|
|
||||||
|
## Phase 7: Polish & Cross-Cutting Concerns
|
||||||
|
|
||||||
|
- [x] T025 [P] Update references to old VPS proxy logic (caddy) to ensure nginx is the only runtime proxy in README.org and docs/*.md
|
||||||
|
- [x] T026 [P] Validate all task descriptions include explicit file paths in specs/004-vps-migration/tasks.md and update mismatches
|
||||||
|
- [x] T027 [P] Move static site vhosts (portfolio/blog and mb-report) into modules/websites and enable them via host toggles
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
- US1 → US2 → US3 → US4
|
||||||
|
|
||||||
|
## Parallel Execution Examples
|
||||||
|
|
||||||
|
- US1: T007, T008, T009 can proceed once T003 and T006 are reviewed.
|
||||||
|
- US2: T011 can proceed once the iptables reference ruleset location is identified.
|
||||||
|
- US3: T012, T016, T017, T018, and T020 can proceed after T004 and T005 review; T013 depends on user-provided secrets.
|
||||||
|
- US4: T022, T023, T024 can proceed independently once logs are reviewed and quickstart.md is open.
|
||||||
|
|
||||||
|
## Validation
|
||||||
|
|
||||||
|
- All tasks use the required checklist format with IDs, story labels, and explicit file paths.
|
||||||
|
|
||||||
|
## Verification Steps (Placeholder)
|
||||||
|
|
||||||
|
- To be filled during T024 with per-task verification steps.
|
||||||
Reference in New Issue
Block a user