Compare commits
1 Commits
docs
..
fastback-ssh
| Author | SHA1 | Date | |
|---|---|---|---|
| ab25c07f69 |
+18
-26
@@ -6,11 +6,11 @@ on:
|
||||
|
||||
jobs:
|
||||
check:
|
||||
name: Check, build and cache nixfiles
|
||||
runs-on: ubuntu-26.04
|
||||
name: Check, build and cache Nix flake
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: cachix/install-nix-action@v31
|
||||
- uses: actions/checkout@v4
|
||||
- uses: cachix/install-nix-action@v23
|
||||
with:
|
||||
# Gitea will supply a token in GITHUB_TOKEN, which this action will
|
||||
# try to pass to Nix when downloading from GitHub
|
||||
@@ -18,30 +18,22 @@ jobs:
|
||||
extra_nix_config: |
|
||||
# Make sure we're using sandbox
|
||||
sandbox-fallback = false
|
||||
# Big C++ projects fill up memory...
|
||||
cores = 6
|
||||
|
||||
extra-substituters = https://nix-cache.nul.ie
|
||||
extra-trusted-public-keys = nix-cache.nul.ie-1:BzH5yMfF4HbzY1C977XzOxoPhEc9Zbu39ftPkUbH+m4=
|
||||
extra-substituters = https://nix-cache.nul.ie/main
|
||||
extra-trusted-public-keys = main:mMChkG8LwXrFirVfudqjSHasK1jV31OVElYD3eImYl8=
|
||||
- name: Set up attic
|
||||
run: |
|
||||
nix run .#nixpkgs.mine.x86_64-linux.attic-client -- \
|
||||
login --set-default colony https://nix-cache.nul.ie "${{ secrets.NIX_CACHE_TOKEN }}"
|
||||
|
||||
- name: Check flake
|
||||
run: nix flake check --no-build
|
||||
|
||||
- name: Build (and cache) the world
|
||||
run: nix flake check
|
||||
- name: Build the world
|
||||
id: build
|
||||
env:
|
||||
HARMONIA_SSH_KEY: ${{ secrets.HARMONIA_SSH_KEY }}
|
||||
run: |
|
||||
nix eval --json --apply "builtins.attrNames" .#ci.x86_64-linux | jq -cr '.[]' | while read job; do
|
||||
echo "::group::Build $job"
|
||||
nix build --no-link .#ci.x86_64-linux."$job"
|
||||
echo "::endgroup::"
|
||||
|
||||
echo "::group::Cache $job"
|
||||
ci/push-to-cache.sh "$(nix eval --raw .#ci.x86_64-linux."$job")"
|
||||
echo "::endgroup::"
|
||||
done
|
||||
|
||||
echo "Building and caching CI derivation"
|
||||
nix build --no-link .#ciDrv.x86_64-linux
|
||||
UPDATE_PROFILE=1 ci/push-to-cache.sh "$(nix eval --raw .#ciDrv.x86_64-linux)"
|
||||
path=$(nix build --no-link .#ci.x86_64-linux --json | jq -r .[0].outputs.out)
|
||||
echo "path=$path" >> "$GITHUB_OUTPUT"
|
||||
- name: Push to cache
|
||||
run: |
|
||||
nix run .#nixpkgs.mine.x86_64-linux.attic-client -- \
|
||||
push main ${{ steps.build.outputs.path }}
|
||||
|
||||
@@ -14,20 +14,22 @@ jobs:
|
||||
uses: https://github.com/actions/setup-go@v4
|
||||
with:
|
||||
go-version: '>=1.20.1'
|
||||
- uses: cachix/install-nix-action@v27
|
||||
- uses: cachix/install-nix-action@v23
|
||||
with:
|
||||
github_access_token: ${{ secrets.GH_PULL_TOKEN }}
|
||||
extra_nix_config: |
|
||||
# Make sure we're using sandbox
|
||||
sandbox-fallback = false
|
||||
|
||||
extra-substituters = https://nix-cache.nul.ie
|
||||
extra-trusted-public-keys = nix-cache.nul.ie-1:BzH5yMfF4HbzY1C977XzOxoPhEc9Zbu39ftPkUbH+m4=
|
||||
|
||||
- name: Set up vars
|
||||
extra-substituters = https://nix-cache.nul.ie/main
|
||||
extra-trusted-public-keys = main:mMChkG8LwXrFirVfudqjSHasK1jV31OVElYD3eImYl8=
|
||||
- name: Set up attic
|
||||
id: setup
|
||||
run: |
|
||||
nix run .#nixpkgs.mine.x86_64-linux.attic-client -- \
|
||||
login --set-default colony https://nix-cache.nul.ie "${{ secrets.NIX_CACHE_TOKEN }}"
|
||||
echo "short_rev=$(git rev-parse --short HEAD)" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Build installer ISO
|
||||
run: |
|
||||
nix build .#nixfiles.config.nixos.systems.installer.configuration.config.my.buildAs.iso
|
||||
@@ -37,13 +39,12 @@ jobs:
|
||||
run: |
|
||||
nix build .#nixfiles.config.nixos.systems.installer.configuration.config.my.buildAs.netbootArchive
|
||||
ln -s "$(readlink result)" \
|
||||
jackos-installer-netboot-${{ steps.setup.outputs.short_rev }}.tar.zst
|
||||
jackos-installer-netboot-${{ steps.setup.outputs.short_rev }}.tar
|
||||
|
||||
- name: Create release
|
||||
uses: https://gitea.com/actions/release-action@main
|
||||
with:
|
||||
title: Latest installer
|
||||
api_key: '${{ secrets.RELEASE_TOKEN }}'
|
||||
files: |
|
||||
jackos-installer-${{ steps.setup.outputs.short_rev }}.iso
|
||||
jackos-installer-netboot-${{ steps.setup.outputs.short_rev }}.tar.zst
|
||||
jackos-installer-netboot-${{ steps.setup.outputs.short_rev }}.tar
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKXRXkYnBf2opIjN+bXE7HmhUpa4hyXJUGmBT+MRccT4 harmonia
|
||||
@@ -1,118 +0,0 @@
|
||||
# AGENTS.md
|
||||
|
||||
This file provides guidance to coding agents when working with code in this repository.
|
||||
|
||||
## Overview
|
||||
|
||||
Personal Nix flake managing NixOS systems and home-manager configurations for a set of
|
||||
machines ("boxes": servers, home machines, routers). It is built around a **custom module system** layered
|
||||
on top of NixOS/home-manager, not the stock flake `nixosConfigurations` pattern.
|
||||
|
||||
## Commands
|
||||
|
||||
This repo provides a `numtide/devshell` (entered via `direnv` / `use flake`). The shell defines
|
||||
named commands — prefer them over raw `nix` invocations. Run a bare command name with no args to
|
||||
see its help, or browse `devshell/commands.nix` / `devshell/install.nix` / `devshell/vm-tasks.nix`.
|
||||
|
||||
Common ones:
|
||||
- `fmt` — format Nix with `nixpkgs-fmt` (the canonical formatter here).
|
||||
- `build-system <host> [nix args]` — build a NixOS system's `toplevel`.
|
||||
- `build-n-switch <args>` — wraps `doas nixos-rebuild --flake .`.
|
||||
- `build-home <name>` / `home-switch` — build / switch a home-manager config.
|
||||
- `run-vm <host>` — build & boot a system as a dev VM (installs `.keys/dev.key` into the VM).
|
||||
- `build-iso` / `build-kexec` / `build-netboot <host>` — alternate build outputs via `config.my.buildAs.*`.
|
||||
- `check-system <host> [nix args]` — evaluate a system (catches eval errors without a full build).
|
||||
**Prefer this over `build-system` to validate a config change** — evaluation surfaces module/option
|
||||
errors quickly and cheaply; only do a full build when you specifically need the built artifact.
|
||||
- `deploy <host>` and `deploy-multi <hosts...>` — deploy-rs deployment (uses `.keys/deploy.key`, `--skip-checks`).
|
||||
Pass the flake-qualified node, e.g. `deploy .#git`. The deploy node name is **always** the system
|
||||
name (`deploy-rs.nix` keys nodes directly off `nixos.systems` / `home-manager.homes`); a system is
|
||||
only a deploy target when `config.my.deploy.enable` is true (defaults true; auto-disabled for dev
|
||||
VMs and containers).
|
||||
- `ssh-machine <name> [cmd]` — SSH to a NixOS system or home-manager config by name. Resolves the
|
||||
target and ssh options (identity, port) from its deploy-rs node, so it needs `my.deploy.enable`
|
||||
(same gate as `deploy`).
|
||||
- `ragenix` — edit age secrets using `.keys/dev.key` as identity (see Secrets).
|
||||
- `repl` — `nix repl .#`.
|
||||
- `update-nixpkgs` / `update-home-manager` — bump pinned inputs.
|
||||
|
||||
Check everything (what CI runs): `nix flake check --no-build`.
|
||||
CI builds each attr of `.#ci.x86_64-linux` (systems, homes, packages, shell) and pushes to the
|
||||
Harmonia binary cache; see `.gitea/workflows/ci.yaml` and `ci/push-to-cache.sh`.
|
||||
|
||||
## Architecture
|
||||
|
||||
### The custom module system
|
||||
`flake.nix` does **not** call `nixosSystem` per host directly. Instead it `evalModules` over
|
||||
`./nixos`, `./home-manager`, `./deploy-rs.nix`, and the per-host files listed in the `configs`
|
||||
list in `flake.nix`. That evaluation produces a top-level config (`self.nixfiles`) from which the
|
||||
real flake outputs are derived:
|
||||
- `nixos.systems.<name>` → `nixosConfigurations.<name>`
|
||||
- `home-manager.homes.<name>` → `homeConfigurations.<name>`
|
||||
- `nixos.modules` / `home-manager.modules` → `nixosModules` / `homeModules`
|
||||
- `deploy-rs.rendered` → `deploy`
|
||||
|
||||
`nixos/default.nix` and `home-manager/default.nix` define the `systemOpts` / `homeOpts` submodules
|
||||
and the `mkSystem` / `mkHome` functions that actually invoke `eval-config.nix` /
|
||||
`homeManagerConfiguration`. **To add a new host:** create a box file that sets
|
||||
`nixos.systems.<name> = { ... }`, then add its path to the `configs` list in `flake.nix`.
|
||||
|
||||
### Multiple nixpkgs channels
|
||||
Four pkgs sets are threaded everywhere as `pkgsFlakes` / `pkgs'` (and `hmFlakes` for home-manager):
|
||||
`unstable`, `stable`, `mine` (a personal nixpkgs fork), `mine-stable`. Each system/home picks its
|
||||
channel via `nixpkgs` / `home-manager` / `hmNixpkgs` options (e.g. `nixpkgs = "mine-stable"`).
|
||||
Modules receive `pkgs'` = an attrset of all channels for the current system.
|
||||
|
||||
### `lib.my` and the `my` option namespace
|
||||
`lib/default.nix` extends `lib` with a `my` attrset (helpers like `mkOpt'`, `mkBoolOpt'`,
|
||||
`mkDefault'`, `inlineModule'`, `mkDefaultSystemsPkgs`, `homeStateVersion`). It also pulls in:
|
||||
- `lib.my.net` — network/CIDR helpers from the `libnetRepo` input. Used heavily for IP math.
|
||||
- `lib.my.c` — shared constants from `lib/constants.nix` (UIDs/GIDs, kernel package selection,
|
||||
nginx snippets, per-network domains/prefixes, etc.). Reuse these rather than hardcoding.
|
||||
- `lib.my.dns` — DNS helpers (`lib/dns.nix`).
|
||||
|
||||
Custom modules add options under the `my.*` namespace (e.g. `my.secrets`, `my.build`,
|
||||
`my.tmproot`, `my.server`). Use `mkOpt'`/`mkBoolOpt'` for option declarations to match style.
|
||||
|
||||
### Modules and module lists
|
||||
Module sets are registered in `nixos/modules/_list.nix` and `home-manager/modules/_list.nix`
|
||||
(name → path), which become `nixos.modules` / `home-manager.modules` and are applied to every
|
||||
system/home. To add a shared module, drop the file in `nixos/modules/` (or `home-manager/modules/`)
|
||||
and add an entry to the relevant `_list.nix`.
|
||||
|
||||
### Network assignments
|
||||
Each system declares `assignments.<name>` (in its `nixos.systems.<host>` block) with IPv4/IPv6
|
||||
addresses, gateways, domains, MTU, etc. These are aggregated into `allAssignments` (passed to every
|
||||
module) and there is an assertion that fails on duplicate IPs. Host networking
|
||||
(`networking.hostName`, `domain`) defaults from the `internal` assignment.
|
||||
|
||||
### Hosts / "boxes"
|
||||
Per-host configs live under `nixos/boxes/<host>` (some are single `.nix` files, some directories
|
||||
with nested VMs/containers under e.g. `colony/vms`). Many "systems" are VMs or containers managed
|
||||
via the `vms` / `containers` modules and the `l2mesh` VXLAN module.
|
||||
|
||||
For a human-readable map of what is actually deployed (per-machine roles, services and networking),
|
||||
see `README.md` and the per-machine docs under `docs/boxes/` (grouped `docs/boxes/colony/`,
|
||||
`docs/boxes/home/`, `docs/boxes/misc/`). Keep these in sync when adding, removing or repurposing
|
||||
a machine or service.
|
||||
|
||||
## Secrets
|
||||
|
||||
age-encrypted secrets in `secrets/`, managed with **ragenix**. Each module declares
|
||||
`my.secrets.files.<name>` and `my.secrets.key` (the host pubkey to encrypt for). `secrets.nix`
|
||||
(the ragenix rules file) is generated by reading every system's declared secrets and computing the
|
||||
recipient key list (always including `.keys/dev.pub`). Edit secrets with the `ragenix` devshell
|
||||
command, which supplies `.keys/dev.key` as the identity. The `.keys/` directory (dev + deploy
|
||||
private keys) is required for editing secrets, deploying, and running dev VMs.
|
||||
|
||||
## Conventions
|
||||
|
||||
- Format with `nixpkgs-fmt` (`fmt`). 2-space indent, `inherit (...)` blocks at the top of `let`.
|
||||
- Prefer `lib.my` helpers (`mkOpt'`, `mkBoolOpt'`, `mkDefault'`) and `lib.my.c` constants over
|
||||
reimplementing.
|
||||
- New shared functionality → a module in `*/modules/` + entry in `_list.nix`, options under `my.*`.
|
||||
- New host → box file under `nixos/boxes/` + entry in the `configs` list in `flake.nix`.
|
||||
- Custom packages live in `pkgs/` and are registered in `pkgs/default.nix`; the overlay is exposed
|
||||
as `overlays.default`.
|
||||
- In prose and commit messages, quote code-like identifiers (commands, options, paths, package and
|
||||
attribute names) in backticks.
|
||||
@@ -1,80 +0,0 @@
|
||||
# nixfiles
|
||||
|
||||
Personal Nix flake managing every machine I run: hosted servers, home
|
||||
infrastructure, routers, a remote site, VPSes and personal workstations. It is
|
||||
built around a **custom module system** layered on top of NixOS and
|
||||
home-manager rather than the stock per-host `nixosConfigurations` pattern.
|
||||
|
||||
For day-to-day commands and a deeper explanation of the module system,
|
||||
conventions and secrets, see [`AGENTS.md`](AGENTS.md). This README is the map of
|
||||
**what is actually deployed**; the per-machine details live under [`docs/boxes/`](docs/boxes).
|
||||
|
||||
> **Note:** This documentation (the README and everything under `docs/`) is a
|
||||
> work in progress and was **agent-generated** from the repository. It may be
|
||||
> incomplete or out of date — treat the Nix configuration as the source of truth.
|
||||
|
||||
## The boxes at a glance
|
||||
|
||||
Machines are grouped by deployment/location. Each group has its own directory
|
||||
under `docs/boxes/` with a `README.md` overview and one file per machine.
|
||||
|
||||
| Group | What it is | Docs |
|
||||
| --- | --- | --- |
|
||||
| **colony** | Hosted dedicated server in Amsterdam (`ams1`). A VM host running the public-facing infrastructure: routing, web, git, media, object storage, chat, game servers. | [`docs/boxes/colony/`](docs/boxes/colony) |
|
||||
| **home** | Home network: a VM host (`palace`), the home routers, storage, Home Assistant, and personal desktops. | [`docs/boxes/home/`](docs/boxes/home) |
|
||||
| **misc** | Everything else: edge VPSes (`britway`, `britnet`), the remote `kelder` site, the `tower` workstation, and the installer image. | [`docs/boxes/misc/`](docs/boxes/misc) |
|
||||
|
||||
## The "big machine" pattern
|
||||
|
||||
The larger sites (`colony`, `home`) all follow the same shape:
|
||||
|
||||
```
|
||||
physical host (VM host)
|
||||
├── VM ── thing impractical to containerise (router, storage, podman host, …)
|
||||
├── VM ── container host ──┬── NixOS container ── application
|
||||
│ ├── NixOS container ──┬── application
|
||||
│ │ ├── application
|
||||
│ │ └── application
|
||||
│ └── …
|
||||
└── VM ── …
|
||||
```
|
||||
|
||||
- A **physical host** (`colony`, `palace`) does little itself beyond running
|
||||
**VMs** via the custom `my.vms` module (QEMU + systemd units, LVM-backed
|
||||
disks).
|
||||
- **VMs** exist for things that are impractical to put in a container — kernel
|
||||
features, separate networking, podman/OCI workloads, foreign OSes.
|
||||
- One VM is usually a **container host** (`shill` on colony, `sfh` on home; and
|
||||
`kelder` directly). It runs **NixOS containers** via the custom `my.containers`
|
||||
module (systemd-nspawn based, each with its own address on a bridge).
|
||||
- **Most applications live in those NixOS containers.** A container isn't limited
|
||||
to a single application — it commonly hosts a **group of related applications**
|
||||
that belong together (e.g. `jackflix` runs Jellyfin, the *arr stack,
|
||||
Transmission, PhotoPrism and copyparty; `object` runs MinIO, Harmonia, HedgeDoc
|
||||
and wastebin). Each container is a first-class entry in `docs/boxes/`.
|
||||
|
||||
Networking between everything is largely defined by per-system `assignments`
|
||||
(IPs/prefixes) plus an L2 VXLAN mesh (`my.vpns.l2`, AS211024) that ties the edge
|
||||
routers together. See [`AGENTS.md`](AGENTS.md) for the mechanics.
|
||||
|
||||
## Repo layout
|
||||
|
||||
```
|
||||
README.md <- you are here
|
||||
nixos/
|
||||
boxes/ per-machine configuration ("boxes")
|
||||
colony/ colony host + its VMs (vms/) + shill's containers
|
||||
home/ palace host + its VMs, plus stream, castle
|
||||
britway/ britnet.nix, kelder/, tower/, installer.nix …
|
||||
modules/ shared NixOS modules (my.* options); registered in _list.nix
|
||||
home-manager/ home-manager modules + configs
|
||||
lib/ lib.my helpers, constants (lib.my.c), net/dns helpers
|
||||
pkgs/ custom packages (overlays.default)
|
||||
secrets/ age-encrypted secrets (ragenix)
|
||||
devshell/ devshell commands (build/deploy/check/ssh helpers)
|
||||
docs/boxes/ per-machine documentation (colony/, home/, misc/)
|
||||
```
|
||||
|
||||
A machine is wired into the flake by adding its box file to the `configs` list
|
||||
in `flake.nix`. See [`AGENTS.md`](AGENTS.md#architecture) for how `evalModules`
|
||||
turns these into `nixosConfigurations`, `homeConfigurations` and `deploy` nodes.
|
||||
@@ -1 +0,0 @@
|
||||
object-ctr.ams1.int.nul.ie ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFdHbZErWLmTPO/aEWB1Fup/aGMf31Un5Wk66FJwTz/8
|
||||
@@ -1,31 +0,0 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
REMOTE_STORE=/var/lib/harmonia
|
||||
SSH_HOST="harmonia@object-ctr.ams1.int.nul.ie"
|
||||
SSH_KEY=/tmp/harmonia.key
|
||||
STORE_URI="ssh-ng://$SSH_HOST?ssh-key=$SSH_KEY&remote-store=$REMOTE_STORE"
|
||||
|
||||
remote_cmd() {
|
||||
ssh -i "$SSH_KEY" "$SSH_HOST" env HOME=/run/harmonia NIX_REMOTE="$REMOTE_STORE" "$@"
|
||||
}
|
||||
|
||||
umask_old=$(umask)
|
||||
umask 0066
|
||||
echo "$HARMONIA_SSH_KEY" | base64 -d > "$SSH_KEY"
|
||||
umask $umask_old
|
||||
|
||||
mkdir -p ~/.ssh
|
||||
cp ci/known_hosts ~/.ssh/
|
||||
path="$1"
|
||||
|
||||
echo "Pushing $path to cache..."
|
||||
nix copy --no-check-sigs --to "$STORE_URI" "$path"
|
||||
|
||||
if [ -n "$UPDATE_PROFILE" ]; then
|
||||
echo "Updating profile..."
|
||||
remote_cmd nix-env -p "$REMOTE_STORE"/nix/var/nix/profiles/nixfiles --set "$path"
|
||||
|
||||
echo "Collecting garbage..."
|
||||
remote_cmd nix-collect-garbage --delete-older-than 60d
|
||||
fi
|
||||
+19
-51
@@ -20,7 +20,7 @@ in
|
||||
[ -e "${homeFlake}" ] && echo "${homeFlake} already exists" && exit 1
|
||||
|
||||
mkdir -p "$(dirname "${homeFlake}")"
|
||||
ln -sf "$(pwd)/flake.nix" "${homeFlake}"
|
||||
ln -s "$(pwd)/flake.nix" "${homeFlake}"
|
||||
echo "Installed link to $(pwd)/flake.nix at ${homeFlake}"
|
||||
'';
|
||||
}
|
||||
@@ -48,30 +48,11 @@ in
|
||||
help = "Print the ed25519 pubkey for a host";
|
||||
command = "${pkgs.openssh}/bin/ssh-keyscan -t ed25519 \"$1\" 2> /dev/null | awk '{ print $2 \" \" $3 }'";
|
||||
}
|
||||
{
|
||||
name = "ssh-machine";
|
||||
category = "utilities";
|
||||
help = "SSH to a machine by NixOS system or home-manager config name";
|
||||
command = ''
|
||||
name="$1"
|
||||
shift
|
||||
# Run from the project root so deploy's relative identity path (.keys/deploy.key) resolves
|
||||
cd "$PRJ_ROOT"
|
||||
# deploy-rs node names are the system/home name, with `@` mangled to `-at-`
|
||||
node="''${name//@/-at-}"
|
||||
# Single eval: resolve `user@host` plus merged (global + node) deploy-rs ssh options
|
||||
info="$(nix eval --raw .#deploy --apply 'd: let n = d.nodes."'"$node"'"; in "''${n.sshUser}@''${n.hostname} ''${builtins.concatStringsSep " " (d.sshOpts ++ n.sshOpts)}"')"
|
||||
target="''${info%% *}"
|
||||
opts="''${info#* }"
|
||||
# shellcheck disable=SC2086
|
||||
exec ${pkgs.openssh}/bin/ssh $opts "$target" "$@"
|
||||
'';
|
||||
}
|
||||
{
|
||||
name = "json2nix";
|
||||
category = "utilities";
|
||||
help = "Convert JSON to formatted Nix";
|
||||
command = "nix eval --impure --expr 'builtins.fromJSON (builtins.readFile /dev/stdin)' | ${pkgs.nixfmt}/bin/nixfmt -";
|
||||
command = "nix eval --impure --expr 'builtins.fromJSON (builtins.readFile /dev/stdin)' | ${pkgs.nixfmt}/bin/nixfmt";
|
||||
}
|
||||
|
||||
{
|
||||
@@ -92,22 +73,11 @@ in
|
||||
help = "Build NixOS configuration";
|
||||
command = ''nix build "''${@:2}" ".#nixosConfigurations.\"$1\".config.system.build.toplevel"'';
|
||||
}
|
||||
{
|
||||
name = "check-system";
|
||||
category = "utilities";
|
||||
help = "Evaluate NixOS configuration (check validity without building)";
|
||||
command = ''nix eval "''${@:2}" ".#nixosConfigurations.\"$1\".config.system.build.toplevel.drvPath"'';
|
||||
}
|
||||
{
|
||||
name = "build-n-switch";
|
||||
category = "tasks";
|
||||
help = "Shortcut to nixos-rebuild for this flake";
|
||||
command = ''
|
||||
# HACK: Upstream changes in Git + Nix makes this necessary
|
||||
# https://github.com/NixOS/nix/issues/10202
|
||||
doas git config --global --add safe.directory "$PWD"
|
||||
doas nixos-rebuild --flake . "$@"
|
||||
'';
|
||||
command = ''doas nixos-rebuild --flake . "$@"'';
|
||||
}
|
||||
{
|
||||
name = "run-vm";
|
||||
@@ -136,8 +106,8 @@ in
|
||||
{
|
||||
name = "build-netboot";
|
||||
category = "tasks";
|
||||
help = "Build NixOS configuration as netboot tree";
|
||||
command = ''nix build "''${@:2}" ".#nixfiles.config.nixos.systems.\"$1\".configuration.config.my.buildAs.netbootTree"'';
|
||||
help = "Build NixOS configuration as netboot archive";
|
||||
command = ''nix build "''${@:2}" ".#nixfiles.config.nixos.systems.\"$1\".configuration.config.my.buildAs.netbootArchive"'';
|
||||
}
|
||||
{
|
||||
name = "build-home";
|
||||
@@ -145,17 +115,29 @@ in
|
||||
help = "Build home-manager configuration";
|
||||
command = ''nix build "''${@:2}" ".#homeConfigurations.\"$1\".activationPackage"'';
|
||||
}
|
||||
{
|
||||
name = "update-inputs";
|
||||
category = "tasks";
|
||||
help = "Update flake inputs";
|
||||
command = ''
|
||||
args=()
|
||||
for f in "$@"; do
|
||||
args+=(--update-input "$f")
|
||||
done
|
||||
nix flake lock "''${args[@]}"
|
||||
'';
|
||||
}
|
||||
{
|
||||
name = "update-nixpkgs";
|
||||
category = "tasks";
|
||||
help = "Update nixpkgs flake inputs";
|
||||
command = ''nix flake update nixpkgs-{unstable,stable,mine,mine-stable}'';
|
||||
command = ''update-inputs nixpkgs-{unstable,stable,mine,mine-stable}'';
|
||||
}
|
||||
{
|
||||
name = "update-home-manager";
|
||||
category = "tasks";
|
||||
help = "Update home-manager flake inputs";
|
||||
command = ''nix flake update home-manager-{unstable,stable}'';
|
||||
command = ''update-inputs home-manager-{unstable,stable}'';
|
||||
}
|
||||
{
|
||||
name = "update-installer";
|
||||
@@ -163,19 +145,5 @@ in
|
||||
help = "Update installer tag (to trigger new release)";
|
||||
command = ''git tag -f installer && git push -f origin installer'';
|
||||
}
|
||||
{
|
||||
name = "deploy-multi";
|
||||
category = "tasks";
|
||||
help = "Deploy multiple flakes at once";
|
||||
command = ''
|
||||
for f in $@; do
|
||||
if [ -n "''${O:-}" ]; then
|
||||
deploy "$O" $f
|
||||
else
|
||||
deploy $f
|
||||
fi
|
||||
done
|
||||
'';
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ in
|
||||
|
||||
NIX_USER_CONF_FILES = toString (pkgs.writeText "nix.conf"
|
||||
''
|
||||
experimental-features = nix-command flakes ca-derivations
|
||||
experimental-features = nix-command flakes ca-derivations repl-flake
|
||||
connect-timeout = 5
|
||||
fallback = true
|
||||
${lib.my.c.nix.cache.conf}
|
||||
@@ -24,10 +24,10 @@ in
|
||||
coreutils
|
||||
nixVersions.stable
|
||||
rage
|
||||
wireguard-tools
|
||||
(pkgs.writeShellScriptBin "deploy" ''
|
||||
exec ${deploy-rs.deploy-rs}/bin/deploy --skip-checks "$@"
|
||||
'')
|
||||
home-manager
|
||||
attic-client
|
||||
];
|
||||
}
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
# colony
|
||||
|
||||
The hosted dedicated server in Amsterdam (`ams1`). This is the public-facing
|
||||
half of the boxes: almost everything reachable from the internet lives here.
|
||||
|
||||
- **Internal domain:** `ams1.int.nul.ie`
|
||||
- **Public domain:** `nul.ie` (public services are published as `*.nul.ie`)
|
||||
- **Source:** [`nixos/boxes/colony/`](../../../nixos/boxes/colony)
|
||||
|
||||
## Shape
|
||||
|
||||
`colony` is the physical VM host. It runs the VMs below; one of them (`shill`)
|
||||
is itself a NixOS container host where most applications run.
|
||||
|
||||
```
|
||||
colony (physical VM host)
|
||||
├── estuary ── edge router / firewall / DNS / BGP
|
||||
├── shill ──── NixOS container host ──┬── middleman (reverse proxy, ACME, SSO)
|
||||
│ ├── colony-psql (shared PostgreSQL)
|
||||
│ ├── vaultwarden (password manager)
|
||||
│ ├── chatterbox (Matrix + bridges)
|
||||
│ ├── toot (Mastodon)
|
||||
│ ├── jackflix (media stack)
|
||||
│ ├── object (MinIO, Nix cache, …)
|
||||
│ ├── waffletail (Tailscale subnet router)
|
||||
│ ├── qclk (clock service)
|
||||
│ └── gam (game servers)
|
||||
├── whale2 ─── podman/OCI host (game servers)
|
||||
├── git ────── Gitea + Actions runner
|
||||
├── mail ───── Debian VM running Mailcow (not NixOS — configured out of repo)
|
||||
└── darts ──── third-party/customer VM (opaque to this repo)
|
||||
```
|
||||
|
||||
## Machines
|
||||
|
||||
| Machine | Role | Docs |
|
||||
| --- | --- | --- |
|
||||
| `colony` | Physical VM host (AMD, LVM-thin, borgthin backups → rsync.net) | [colony.md](colony.md) |
|
||||
| `estuary` | Edge router: WAN, firewall/NAT, DNS, BGP, IXP peering, WireGuard | [estuary.md](estuary.md) |
|
||||
| `shill` | NixOS container host (see containers below) | [shill.md](shill.md) |
|
||||
| `whale2` | podman/OCI host for game servers | [whale2.md](whale2.md) |
|
||||
| `git` | Gitea + Gitea Actions runner | [git.md](git.md) |
|
||||
|
||||
### shill containers
|
||||
|
||||
| Container | Role | Docs |
|
||||
| --- | --- | --- |
|
||||
| `middleman` | Front-end nginx reverse proxy, ACME certs, nginx-sso, librespeed | [middleman.md](middleman.md) |
|
||||
| `colony-psql` | Shared PostgreSQL (14) for colony services | [colony-psql.md](colony-psql.md) |
|
||||
| `vaultwarden` | Vaultwarden (Bitwarden-compatible password manager) | [vaultwarden.md](vaultwarden.md) |
|
||||
| `chatterbox` | Matrix homeserver + bridges (heisenbridge, mautrix-*) | [chatterbox.md](chatterbox.md) |
|
||||
| `toot` | Bluesky PDS (Mastodon disabled) | [toot.md](toot.md) |
|
||||
| `jackflix` | Media: Jellyfin, *arr stack, Transmission, PhotoPrism, copyparty | [jackflix.md](jackflix.md) |
|
||||
| `object` | MinIO (S3), Harmonia (Nix cache), HedgeDoc, wastebin | [object.md](object.md) |
|
||||
| `waffletail` | Tailscale subnet router (advertises colony prefixes into the tailnet) | [waffletail.md](waffletail.md) |
|
||||
| `qclk` | `qclk` clock service (reachable over WireGuard) | [qclk.md](qclk.md) |
|
||||
| `gam` | Game servers (Terraria, …) | [gam.md](gam.md) |
|
||||
|
||||
## Non-NixOS VMs
|
||||
|
||||
These run on `colony` but are **not** managed by this repo (no NixOS config). The
|
||||
QEMU instances are still declared in `colony`'s `my.vms.instances`, and colony's
|
||||
networking routes/firewalls traffic to them:
|
||||
|
||||
- **`mail`** — a Debian VM running [Mailcow](https://mailcow.email/) (`mail.nul.ie`).
|
||||
ACME certs are pushed to it from `middleman` (see [middleman.md](middleman.md)).
|
||||
- **`darts`** — a third-party/customer VM; opaque to this repo, given a routed
|
||||
prefix and otherwise left alone.
|
||||
@@ -1,19 +0,0 @@
|
||||
# chatterbox
|
||||
|
||||
The Matrix homeserver (`nul.ie`) and its chat-network bridges.
|
||||
|
||||
- **Source:** [`shill/containers/chatterbox.nix`](../../../nixos/boxes/colony/vms/shill/containers/chatterbox.nix)
|
||||
- **Host:** NixOS container on `shill`
|
||||
|
||||
## Role
|
||||
|
||||
- **Matrix homeserver** for `server_name = "nul.ie"`.
|
||||
- **Bridges** to other chat networks:
|
||||
- `heisenbridge` (IRC),
|
||||
- `mautrix-whatsapp` (WhatsApp),
|
||||
- `mautrix-meta` / `mautrix-messenger` (Facebook Messenger / Instagram).
|
||||
- Fronted by `middleman` (federation on `:8448`).
|
||||
|
||||
## Networking
|
||||
|
||||
- `internal` assignment on the `ctrs` network (alt name `chatterbox-ctr`).
|
||||
@@ -1,18 +0,0 @@
|
||||
# colony-psql
|
||||
|
||||
The shared PostgreSQL instance for colony. Several other containers
|
||||
(`middleman`, `chatterbox`, `toot`, `git`, …) connect here rather than each
|
||||
running their own database.
|
||||
|
||||
- **Source:** [`shill/containers/colony-psql.nix`](../../../nixos/boxes/colony/vms/shill/containers/colony-psql.nix)
|
||||
- **Host:** NixOS container on `shill`
|
||||
|
||||
## Role
|
||||
|
||||
- **PostgreSQL 14** serving the other colony services over the `ctrs` network.
|
||||
Consumers wait for it to be ready via the `systemdAwaitPostgres` helper.
|
||||
- netdata for monitoring.
|
||||
|
||||
## Networking
|
||||
|
||||
- `internal` assignment on the `ctrs` network (alt name `colony-psql-ctr`).
|
||||
@@ -1,39 +0,0 @@
|
||||
# colony (host)
|
||||
|
||||
The physical dedicated server in Amsterdam and the VM host for everything in
|
||||
this group.
|
||||
|
||||
- **Source:** [`nixos/boxes/colony/default.nix`](../../../nixos/boxes/colony/default.nix)
|
||||
(VM instances in [`nixos/boxes/colony/vms/default.nix`](../../../nixos/boxes/colony/vms/default.nix))
|
||||
- **nixpkgs:** `mine-stable`
|
||||
|
||||
## Role
|
||||
|
||||
Bare-metal AMD host. It does little application work itself — its job is to run
|
||||
the VMs and provide them with storage, networking and backups.
|
||||
|
||||
- **Virtualisation:** QEMU/KVM (`kvm-amd`, IOMMU on) via the `my.vms` module. VM
|
||||
disks are LVM logical volumes (`vm-<name>-<disk>`) in the `main` volume group;
|
||||
`estuary` additionally gets a WAN NIC by PCI passthrough.
|
||||
- **Storage:** LVM-thin (`services.lvm.boot.thin`), `/persist` for state,
|
||||
`/mnt/backup` for the local borg repo. `smartd` + `rasdaemon` for health.
|
||||
- **Backups:** `my.borgthin` snapshots the persist/data LVs of the host and its
|
||||
VMs into `/mnt/backup/main`, which is then `rsync`'d (along with LVM metadata)
|
||||
to rsync.net (`zh2855.rsync.net`).
|
||||
- **Monitoring:** netdata (with freeipmi), smartd.
|
||||
|
||||
## Networking
|
||||
|
||||
- Two bridges: `base` (the colony "base" network, shared with `estuary`) and
|
||||
`vms` (the VM network). Dummy interfaces keep the bridges up so dependent VMs
|
||||
can start.
|
||||
- Default gateway / edge is `estuary`; `colony` itself holds the `routing` and
|
||||
`internal` (a.k.a. `vm`) assignments and routes container/OCI/Tailscale
|
||||
prefixes to `shill` and `whale2`.
|
||||
- `my.firewall` trusts the `vms` interface and forwards customer prefixes
|
||||
(`vm-mail`, `vm-darts`) through.
|
||||
|
||||
## VMs hosted here
|
||||
|
||||
`estuary`, `shill`, `whale2`, `git` (all NixOS, documented in this directory),
|
||||
plus the non-NixOS `mail` and `darts` (see [README](README.md#non-nixos-vms)).
|
||||
@@ -1,41 +0,0 @@
|
||||
# estuary
|
||||
|
||||
The colony edge router and firewall — the machine that holds colony's public
|
||||
IPs and connects everything else to the internet.
|
||||
|
||||
- **Source:** [`nixos/boxes/colony/vms/estuary/`](../../../nixos/boxes/colony/vms/estuary)
|
||||
(`default.nix`, `bgp.nix`, `dns.nix`, `bandwidth.nix`)
|
||||
- **nixpkgs:** `mine`
|
||||
- **Host:** VM on `colony` (gets the WAN NIC by PCI passthrough)
|
||||
|
||||
## Role
|
||||
|
||||
- **Edge routing / firewall / NAT:** owns the colony public IPv4/IPv6
|
||||
(`94.142.241.x` / `2a02:898:0:20::`), does NAT and port-forwarding for the
|
||||
internal services (`my.firewall.nat.forwardPorts` driven by
|
||||
`firewallForwards`). Forwards HTTP/S to `middleman`, git to `git`, game ports
|
||||
to the OCI game servers on `whale2`, etc.
|
||||
- **BGP:** runs BIRD2 ([`bgp.nix`](../../../nixos/boxes/colony/vms/estuary/bgp.nix))
|
||||
announcing AS211024, over VLANs on the WAN link:
|
||||
- peers at the IXPs **Frys-IX**, **NL-ix** and **FogIXP**;
|
||||
- plus **iFog transit** (`ifog-transit`) — an upstream transit provider from
|
||||
iFog, **not** an IXP.
|
||||
- **DNS:** authoritative/recursive DNS ([`dns.nix`](../../../nixos/boxes/colony/vms/estuary/dns.nix)),
|
||||
redirected to port 5353 locally.
|
||||
- **VPNs:**
|
||||
- Part of the AS211024 **L2 VXLAN mesh** (`my.vpns.l2`) with `river`, `stream`
|
||||
and `britway`.
|
||||
- WireGuard endpoints for the remote `kelder` site, `hillcrest`, and
|
||||
`john-valorant`.
|
||||
- **Misc:** iperf3 server. (A bandwidth-accounting script,
|
||||
[`bandwidth.py`](../../../nixos/boxes/colony/vms/estuary/bandwidth.py), exists but
|
||||
is **legacy and not currently used**.)
|
||||
|
||||
## Networking
|
||||
|
||||
- `wan` — the passed-through igb NIC (9000 MTU), carrying the upstream uplink and
|
||||
tagged IXP VLANs (`ifog` 409 → `frys-ix`/`nl-ix`/`fogixp`/`ifog-transit`).
|
||||
- `base` — colony base network; sends RAs and provides DNS to the base prefix,
|
||||
routes the VM/container/OCI/Tailscale prefixes back to `colony`.
|
||||
- `as211024` — the L2 mesh interface.
|
||||
- Assignments: `internal` (public, alt name `fw`), `base`, `as211024`.
|
||||
@@ -1,17 +0,0 @@
|
||||
# gam
|
||||
|
||||
A game-server container (the lightweight counterpart to the OCI game servers on
|
||||
`whale2`).
|
||||
|
||||
- **Source:** [`shill/containers/gam.nix`](../../../nixos/boxes/colony/vms/shill/containers/gam.nix)
|
||||
- **Host:** NixOS container on `shill`
|
||||
|
||||
## Role
|
||||
|
||||
- Hosts game servers run directly as NixOS services — currently **Terraria**
|
||||
(config/world from secrets). Exposed to the internet via `estuary`'s port
|
||||
forwards (`:7777`).
|
||||
|
||||
## Networking
|
||||
|
||||
- `internal` assignment on the `ctrs` network (alt name `gam-ctr`).
|
||||
@@ -1,38 +0,0 @@
|
||||
# git
|
||||
|
||||
The Gitea VM — source hosting and CI for the boxes (`git.nul.ie`).
|
||||
|
||||
- **Source:** [`nixos/boxes/colony/vms/git/`](../../../nixos/boxes/colony/vms/git)
|
||||
(`default.nix`, `gitea.nix`, `gitea-actions.nix`)
|
||||
- **nixpkgs:** `mine`
|
||||
- **Host:** VM on `colony`
|
||||
|
||||
## Role
|
||||
|
||||
- **Gitea** ([`gitea.nix`](../../../nixos/boxes/colony/vms/git/gitea.nix)) — the Git
|
||||
forge (`git.nul.ie`). PostgreSQL-backed (the shared `colony-psql`), LFS
|
||||
enabled, with object storage backed by MinIO on `object` (a MinIO secret is
|
||||
spliced into `app.ini` at startup).
|
||||
- **Gitea Actions runner**
|
||||
([`gitea-actions.nix`](../../../nixos/boxes/colony/vms/git/gitea-actions.nix)) — a
|
||||
Docker-mode runner (`main-docker`) using podman. Labels provide Debian/node-24
|
||||
(Trixie) and Ubuntu 26.04 images; runner config comes from the upstream
|
||||
module's `settings` option. The Actions cache lives on a dedicated disk
|
||||
(`/var/cache/gitea-runner`). Runs as a fixed `gitea-runner` user (not
|
||||
`DynamicUser`) so it can read its token.
|
||||
- **nginx** — terminates TLS for `git.nul.ie` and proxies to Gitea on `:3000`.
|
||||
ACME certs for `nul.ie` / `*.nul.ie` via the Cloudflare DNS challenge.
|
||||
- **podman** — also hosts the OCI registry/build images; `/var/lib/containers`
|
||||
is an XFS data disk.
|
||||
|
||||
## Networking
|
||||
|
||||
- `vms` interface with `routing` / `internal` assignments.
|
||||
- HTTP/HTTPS forwarded in from `estuary`; podman default subnet `10.88.0.0/16` is
|
||||
allowed to forward.
|
||||
|
||||
## CI
|
||||
|
||||
This runner is what executes the repo's own `.gitea/workflows/ci.yaml`, building
|
||||
each `.#ci.x86_64-linux` attribute and pushing to the Harmonia binary cache. See
|
||||
[`AGENTS.md`](../../../AGENTS.md#commands).
|
||||
@@ -1,25 +0,0 @@
|
||||
# jackflix
|
||||
|
||||
The media stack — acquisition, library and streaming.
|
||||
|
||||
- **Source:** [`shill/containers/jackflix/`](../../../nixos/boxes/colony/vms/shill/containers/jackflix)
|
||||
(`default.nix`, `networking.nix`)
|
||||
- **Host:** NixOS container on `shill`
|
||||
|
||||
## Role
|
||||
|
||||
- **Streaming:** Jellyfin.
|
||||
- **Acquisition (*arr stack):** Transmission, Jackett, FlareSolverr, Radarr,
|
||||
Sonarr, and Jellyseerr (`seerr`) for requests.
|
||||
- **Photos:** PhotoPrism (`photos.nul.ie`).
|
||||
- **File sharing:** copyparty (`:3923`) serving public + private media volumes.
|
||||
- Media lives on the shared `/mnt/media` volume (bind-mounted read-write from
|
||||
`shill`). Downloaders bind to a VPN interface
|
||||
([`networking.nix`](../../../nixos/boxes/colony/vms/shill/containers/jackflix/networking.nix)),
|
||||
so torrent traffic only flows while `systemd-networkd-wait-online@vpn` is up.
|
||||
- A shared `media` group (gid 2000) gives the apps coordinated access.
|
||||
|
||||
## Networking
|
||||
|
||||
- `internal` assignment on the `ctrs` network (alt name `jackflix-ctr`), plus its
|
||||
own VPN interface for the download clients.
|
||||
@@ -1,63 +0,0 @@
|
||||
# middleman
|
||||
|
||||
The front-end reverse proxy for all public colony web services — the single
|
||||
ingress that `estuary` forwards HTTP/HTTPS to.
|
||||
|
||||
- **Source:** [`shill/containers/middleman/`](../../../nixos/boxes/colony/vms/shill/containers/middleman)
|
||||
(`default.nix`, `vhosts.nix`)
|
||||
- **Host:** NixOS container on `shill`
|
||||
|
||||
## Role
|
||||
|
||||
- **nginx** reverse proxy ([`vhosts.nix`](../../../nixos/boxes/colony/vms/shill/containers/middleman/vhosts.nix)
|
||||
holds the per-service vhosts) with VTS stats, fancyindex, brotli, caching, and
|
||||
a dynamic resolver pointed at `estuary` so upstreams can be re-resolved at
|
||||
runtime. It is the single public ingress for almost every web service — colony,
|
||||
home, and beyond.
|
||||
- **ACME** — issues the wildcard certificates that **its own** vhosts are served
|
||||
with (it is not a shared CA for the other boxes; `git`, `britway`, `kelder-spoder`, etc. each
|
||||
run their own ACME):
|
||||
- `nul.ie` / `*.nul.ie` (+ `*.s3.nul.ie`) via the Cloudflare DNS challenge,
|
||||
- the internal `ams1.int.nul.ie` / `*` via an `exec` challenge that calls
|
||||
`estuary`'s pdns over SSH.
|
||||
- As a one-off consumer, it then pushes the public cert to the `mail` (Mailcow)
|
||||
VM via `scp` + a remote `mailcow-ssl-reload`.
|
||||
- **nginx-sso** — single-sign-on (`sso.nul.ie`) with Google OAuth and a simple
|
||||
username/password provider; protects the SSO-gated vhosts below.
|
||||
- **librespeed** — speed-test frontend + backend (`librespeed.${domain}` /
|
||||
`speed.nul.ie`).
|
||||
|
||||
## Published vhosts
|
||||
|
||||
All under `*.nul.ie` with the wildcard cert unless noted. Upstreams are addressed
|
||||
by their internal container/VM hostnames. "SSO" = gated behind nginx-sso.
|
||||
|
||||
| Host(s) | Upstream | Notes |
|
||||
| --- | --- | --- |
|
||||
| `nul.ie` (default `_`) | static | landing page (CV, SSH pubkey) + Matrix/atproto `.well-known` |
|
||||
| `sso.nul.ie` | nginx-sso | SSO endpoint |
|
||||
| `pass.nul.ie` | `vaultwarden` | password manager |
|
||||
| `matrix.nul.ie` (+`:8448`) | `chatterbox` | Matrix client + federation |
|
||||
| `element.nul.ie` | element-web | Matrix web client |
|
||||
| `toot.nul.ie` | `toot` :80 | Mastodon (currently disabled — see [toot.md](toot.md)) |
|
||||
| `pds.nul.ie` | `toot` :3000 | Bluesky PDS |
|
||||
| `jackflix.nul.ie` | `jackflix` Jellyfin | streaming |
|
||||
| `torrents` / `jackett` / `radarr` / `sonarr` `.nul.ie` | `jackflix` | *arr stack (**SSO**) |
|
||||
| `gib.nul.ie` | `jackflix` Jellyseerr | requests |
|
||||
| `photos.nul.ie` | `jackflix` PhotoPrism | |
|
||||
| `stuff` / `public` / `p.nul.ie` | `jackflix` copyparty + `/mnt/media` | file sharing / index |
|
||||
| `share.nul.ie` | `object` :9090 | |
|
||||
| `minio` / `s3` / `*.s3.nul.ie` | `object` MinIO | S3 + console (Docker manifest MIME hack) |
|
||||
| `nix-cache.nul.ie` | `object` Harmonia | Nix binary cache (immutable cache headers) |
|
||||
| `md.nul.ie` / `pb.nul.ie` | `object` | HedgeDoc / wastebin |
|
||||
| `mc-map` / `mc-rail` / `mc-map-kink` `.nul.ie` | `whale2` OCI | Minecraft maps |
|
||||
| `netdata-colony.nul.ie` | many hosts :19999 | netdata fan-out (**SSO**) |
|
||||
| `pront.nul.ie` | `stream-hi` (home) | print/webcam (**SSO**) |
|
||||
| `hass.nul.ie` | `hass` (home) | Home Assistant |
|
||||
| `hass-john.nul.ie` | `john-valorant-tun` | remote HASS over WireGuard tunnel |
|
||||
|
||||
## Networking
|
||||
|
||||
- `internal` assignment on the `ctrs` network; bind-mounts `/mnt/media` for
|
||||
serving static/media content.
|
||||
- nginx waits for `colony-psql` before starting (DNS bootstrap hack).
|
||||
@@ -1,24 +0,0 @@
|
||||
# object
|
||||
|
||||
Object storage and Nix binary cache, plus a couple of small self-hosted web
|
||||
apps.
|
||||
|
||||
- **Source:** [`shill/containers/object.nix`](../../../nixos/boxes/colony/vms/shill/containers/object.nix)
|
||||
- **Host:** NixOS container on `shill`
|
||||
|
||||
## Role
|
||||
|
||||
- **MinIO** — S3-compatible object storage (`s3.nul.ie` / `*.s3.nul.ie`). Backs
|
||||
several other services (Gitea LFS/artifacts, social-media uploads, …). Stored on the
|
||||
`/mnt/minio` volume (XFS, bind-mounted from `shill`).
|
||||
- **Harmonia** — serves the Nix binary cache for all the boxes (`nix-cache.nul.ie`), backed
|
||||
by the `/mnt/nix-cache` volume.
|
||||
- **atticd** — an alternative Nix cache (stores into MinIO/S3). **Currently
|
||||
disabled** — present in the config but not running.
|
||||
- **HedgeDoc** — collaborative markdown notes.
|
||||
- **wastebin** — pastebin.
|
||||
|
||||
## Networking
|
||||
|
||||
- `internal` assignment on the `ctrs` network (alt name `object-ctr`).
|
||||
- `/mnt/minio` and `/mnt/nix-cache` bind-mounted read-write from `shill`.
|
||||
@@ -1,22 +0,0 @@
|
||||
# qclk
|
||||
|
||||
The `qclk` service container.
|
||||
|
||||
- **Source:** [`shill/containers/qclk/`](../../../nixos/boxes/colony/vms/shill/containers/qclk)
|
||||
- **Host:** NixOS container on `shill`
|
||||
|
||||
## Role
|
||||
|
||||
- Runs the custom `qclk` service, exposing an API that is reached over a
|
||||
dedicated WireGuard **`management`** network. Managed devices are configured as
|
||||
WireGuard peers (each gets an address in the `qclk` prefix), and AS211024
|
||||
trusted hosts are allowed to reach the API.
|
||||
- `shill` routes the `qclk` prefix to this container.
|
||||
|
||||
## Networking
|
||||
|
||||
- `internal` assignment on the `ctrs` network (alt name `qclk-ctr`), plus the
|
||||
`management` WireGuard interface carrying the `qclk` prefix.
|
||||
|
||||
> Check `qclk/default.nix` for the current peer list and exactly what the service
|
||||
> does — this entry intentionally stays high-level.
|
||||
@@ -1,48 +0,0 @@
|
||||
# shill
|
||||
|
||||
The colony **NixOS container host**. Most colony applications run as
|
||||
systemd-nspawn containers on `shill`.
|
||||
|
||||
- **Source:** [`nixos/boxes/colony/vms/shill/`](../../../nixos/boxes/colony/vms/shill)
|
||||
(`default.nix`, `containers-ext.nix`, `hercules.nix`, `containers/`)
|
||||
- **nixpkgs:** `mine`
|
||||
- **Host:** VM on `colony` (large: 12 cores, 40 GiB RAM)
|
||||
|
||||
## Role
|
||||
|
||||
- Runs the colony NixOS containers via `my.containers.instances`, each attached
|
||||
to the `ctrs` bridge with its own address.
|
||||
- Provides shared data volumes to those containers via bind mounts from
|
||||
LVM-backed disks: `/mnt/media` (→ `middleman`, `jackflix`), `/mnt/minio` and
|
||||
`/mnt/nix-cache` (→ `object`).
|
||||
- Acts as the router between the `vms` network and the `ctrs` container network
|
||||
(sends RAs on `ctrs`, routes Tailscale prefixes via `waffletail` and the
|
||||
`qclk` prefix via `qclk`). Includes an nftables `ct mark` hack to make
|
||||
internal DNAT return paths work.
|
||||
- Tuned sysctls for high connection counts / torrent traffic; netdata.
|
||||
|
||||
## Containers
|
||||
|
||||
Defined in [`shill/containers/`](../../../nixos/boxes/colony/vms/shill/containers)
|
||||
and wired up in `shill`'s `my.containers.instances`:
|
||||
|
||||
| Container | Role |
|
||||
| --- | --- |
|
||||
| [`middleman`](middleman.md) | Front-end nginx reverse proxy, ACME, nginx-sso, librespeed |
|
||||
| [`colony-psql`](colony-psql.md) | Shared PostgreSQL |
|
||||
| [`vaultwarden`](vaultwarden.md) | Password manager |
|
||||
| [`chatterbox`](chatterbox.md) | Matrix homeserver + bridges |
|
||||
| [`toot`](toot.md) | Bluesky PDS (Mastodon disabled) |
|
||||
| [`jackflix`](jackflix.md) | Media stack |
|
||||
| [`object`](object.md) | MinIO / Harmonia / HedgeDoc / wastebin |
|
||||
| [`waffletail`](waffletail.md) | Tailscale subnet router |
|
||||
| [`qclk`](qclk.md) | Clock service |
|
||||
| [`gam`](gam.md) | Game servers |
|
||||
|
||||
## Notes
|
||||
|
||||
- Container systems set `my.deploy.enable = false` (they are deployed as part of
|
||||
`shill`'s container profiles, not as standalone deploy nodes) and render via
|
||||
`my.asContainer`.
|
||||
- `hercules.nix` configures Hercules CI agent bits;
|
||||
`containers-ext.nix` holds extra per-container host wiring.
|
||||
@@ -1,19 +0,0 @@
|
||||
# toot
|
||||
|
||||
A federated-social container. Despite the name (Mastodon = "toots"), it currently
|
||||
hosts a **Bluesky PDS**; the Mastodon instance is disabled.
|
||||
|
||||
- **Source:** [`shill/containers/toot.nix`](../../../nixos/boxes/colony/vms/shill/containers/toot.nix)
|
||||
- **Host:** NixOS container on `shill`
|
||||
|
||||
## Role
|
||||
|
||||
- **Bluesky PDS** (Personal Data Server) — the active service, published as
|
||||
`pds.nul.ie` (proxied by `middleman` to `:3000`).
|
||||
- **Mastodon** — **currently disabled**. The config is still present and
|
||||
`toot.nul.ie` still maps to `:80`, but the instance is not running. (It was
|
||||
backed by the shared `colony-psql` and MinIO/S3 on `object`.)
|
||||
|
||||
## Networking
|
||||
|
||||
- `internal` assignment on the `ctrs` network (alt name `toot-ctr`).
|
||||
@@ -1,15 +0,0 @@
|
||||
# vaultwarden
|
||||
|
||||
[Vaultwarden](https://github.com/dani-garcia/vaultwarden), a Bitwarden-compatible
|
||||
password manager.
|
||||
|
||||
- **Source:** [`shill/containers/vaultwarden.nix`](../../../nixos/boxes/colony/vms/shill/containers/vaultwarden.nix)
|
||||
- **Host:** NixOS container on `shill`
|
||||
|
||||
## Role
|
||||
|
||||
- Runs Vaultwarden, fronted by `middleman` and published under `nul.ie`.
|
||||
|
||||
## Networking
|
||||
|
||||
- `internal` assignment on the `ctrs` network (alt name `vaultwarden-ctr`).
|
||||
@@ -1,19 +0,0 @@
|
||||
# waffletail
|
||||
|
||||
The colony Tailscale node / subnet router.
|
||||
|
||||
- **Source:** [`shill/containers/waffletail.nix`](../../../nixos/boxes/colony/vms/shill/containers/waffletail.nix)
|
||||
- **Host:** NixOS container on `shill`
|
||||
|
||||
## Role
|
||||
|
||||
- Joins the Tailscale tailnet (auth key from secrets) and **advertises the colony
|
||||
prefixes** into it, acting as the subnet router so tailnet clients can reach
|
||||
colony services and vice-versa.
|
||||
- nftables rules SNAT/forward between `host0` and `tailscale0` for the colony
|
||||
v4/v6 ranges. `shill` routes the Tailscale prefixes here.
|
||||
|
||||
## Networking
|
||||
|
||||
- `internal` assignment on the `ctrs` network (alt name `waffletail-ctr`); owns
|
||||
the `tailscale0` interface.
|
||||
@@ -1,27 +0,0 @@
|
||||
# whale2
|
||||
|
||||
A podman/OCI host on colony dedicated to game servers (kept off `shill` so the
|
||||
container churn and resource use stay isolated).
|
||||
|
||||
- **Source:** [`nixos/boxes/colony/vms/whale2/`](../../../nixos/boxes/colony/vms/whale2)
|
||||
(`default.nix`, `valheim.nix`, `minecraft/`, `enshrouded.nix`)
|
||||
- **nixpkgs:** `mine`
|
||||
- **Host:** VM on `colony`
|
||||
|
||||
## Role
|
||||
|
||||
- Runs OCI containers via **podman** (`virtualisation.oci-containers`, netavark
|
||||
backend) on a dedicated `colony` bridge network (`oci`) with both IPv4 and
|
||||
IPv6, so each game server gets its own routable address.
|
||||
- Game servers configured in-repo: **Valheim**, **Minecraft** (several worlds —
|
||||
see `extraAssignments`: `simpcraft`, `simpcraft-staging`, `kevcraft`,
|
||||
`kinkcraft`, `graeme`), and **Enshrouded** (currently commented out).
|
||||
- `/var/lib/containers` is an XFS data disk (project quotas).
|
||||
|
||||
## Networking
|
||||
|
||||
- `vms` interface with `routing` / `internal` (alt name `oci`) assignments.
|
||||
- An `oci` bridge carrying the `prefixes.oci` v4/v6 ranges; per-game addresses
|
||||
are handed out via `extraAssignments` (`valheim-oci`, `simpcraft-oci`, …) and
|
||||
exposed to the internet through `estuary`'s port forwards.
|
||||
- Firewall trusts the `oci` interface and forwards `vms → oci`.
|
||||
@@ -1,44 +0,0 @@
|
||||
# home
|
||||
|
||||
The home network. A VM host (`palace`), a redundant pair of routers, a storage
|
||||
server, Home Assistant, and personal desktops.
|
||||
|
||||
- **Domain:** `h.nul.ie`
|
||||
- **Source:** [`nixos/boxes/home/`](../../../nixos/boxes/home)
|
||||
|
||||
## Shape
|
||||
|
||||
```
|
||||
palace (physical VM host)
|
||||
├── river ──── home router (HA pair with stream)
|
||||
├── cellar ── NVMe-oF storage server (SPDK)
|
||||
└── sfh ───── NixOS container host ──┬── hass (Home Assistant)
|
||||
└── unifi (UniFi controller — defined, currently disabled)
|
||||
|
||||
stream ── standalone home router (HA pair with river)
|
||||
castle ── desktop workstation (boots its disks over NVMe-oF from cellar)
|
||||
```
|
||||
|
||||
The two routers `river` (a VM on `palace`) and `stream` (standalone hardware)
|
||||
share the [`routing-common`](../../../nixos/boxes/home/routing-common) config and
|
||||
form a **keepalived/VRRP high-availability pair**: DHCP (kea), router
|
||||
advertisements (radvd), DNS with blocklists, NAT, and the AS211024 L2 mesh link
|
||||
back to colony.
|
||||
|
||||
## Machines
|
||||
|
||||
| Machine | Role | Docs |
|
||||
| --- | --- | --- |
|
||||
| `palace` | Physical VM host | [palace.md](palace.md) |
|
||||
| `river` | Home router (VM; VRRP pair with `stream`) | [river.md](river.md) |
|
||||
| `cellar` | NVMe-oF storage server (SPDK) | [cellar.md](cellar.md) |
|
||||
| `sfh` | NixOS container host | [sfh.md](sfh.md) |
|
||||
| `stream` | Home router (standalone hardware; VRRP pair with `river`) | [stream.md](stream.md) |
|
||||
| `castle` | Desktop workstation | [castle.md](castle.md) |
|
||||
|
||||
### sfh containers
|
||||
|
||||
| Container | Role | Docs |
|
||||
| --- | --- | --- |
|
||||
| `hass` | Home Assistant | [hass.md](hass.md) |
|
||||
| `unifi` | UniFi network controller (defined, **currently not imported**) | [unifi.md](unifi.md) |
|
||||
@@ -1,19 +0,0 @@
|
||||
# castle
|
||||
|
||||
A desktop workstation.
|
||||
|
||||
- **Source:** [`nixos/boxes/home/castle/default.nix`](../../../nixos/boxes/home/castle/default.nix)
|
||||
|
||||
## Role
|
||||
|
||||
- AMD desktop running the GUI environment (`my.gui.enable`, Sway / Wayland via
|
||||
home-manager).
|
||||
- **Diskless-style boot:** its `/nix`, `/persist` and `/home` are NVMe-oF volumes
|
||||
served by [`cellar`](cellar.md) (`/dev/nvmeof/{nix,persist,home}`). The
|
||||
networking is careful not to drop the IP used for the NVMe-oF connection.
|
||||
- Uses the `my.nvme` module for the NVMe-oF client setup.
|
||||
|
||||
## Networking
|
||||
|
||||
- Sits on the home network; depends on the high-speed link to `cellar` for its
|
||||
root storage.
|
||||
@@ -1,39 +0,0 @@
|
||||
# cellar
|
||||
|
||||
The home storage server. Exports fast NVMe storage over the network so other
|
||||
machines (notably `castle`) can boot and run from it.
|
||||
|
||||
- **Source:** [`nixos/boxes/home/palace/vms/cellar/`](../../../nixos/boxes/home/palace/vms/cellar)
|
||||
(`default.nix`, `spdk.nix`)
|
||||
- **Host:** VM on `palace` (NVMe drives passed through)
|
||||
- **Deploy address:** `192.168.68.80`
|
||||
|
||||
## Role
|
||||
|
||||
- Runs **SPDK** ([`spdk.nix`](../../../nixos/boxes/home/palace/vms/cellar/spdk.nix)) as
|
||||
a userspace storage target. The kernel `nvme` driver is blacklisted so SPDK can
|
||||
drive the NVMe devices directly (attached by PCI BDF).
|
||||
- Builds a **RAID0** (`NVMeRaid`) across three NVMe drives, partitioned into three
|
||||
namespaces, and exports each as an **NVMe-oF target over RDMA** (port 4420) on
|
||||
the high-speed home network — one namespace per consumer:
|
||||
|
||||
| Namespace | NQN | Consumer |
|
||||
| --- | --- | --- |
|
||||
| `NVMeRaidp1` | `nqn.2016-06.io.spdk:river` | [`river`](river.md) |
|
||||
| `NVMeRaidp2` | `nqn.2016-06.io.spdk:castle` | [`castle`](castle.md) |
|
||||
| `NVMeRaidp3` | `nqn.2016-06.io.spdk:sfh` | [`sfh`](sfh.md) |
|
||||
|
||||
Each client is pinned by `hostnqn`, so `river`, `castle` and `sfh` all run their
|
||||
storage off `cellar` over the network.
|
||||
|
||||
## Networking
|
||||
|
||||
- Exports on the high-speed home network (`lan-hi` / the `hi` assignment) over
|
||||
RDMA; the SPDK target waits for that link to be online before starting.
|
||||
|
||||
## Notes
|
||||
|
||||
- The `ublk_*` calls in `my.spdk.debugCommands` are **only a debugging script** —
|
||||
they let you create a local ublk block device to mount and inspect the RAID on
|
||||
`cellar` itself. They are **not** how storage is exported to clients; that is the
|
||||
`nvmf` config above.
|
||||
@@ -1,18 +0,0 @@
|
||||
# hass
|
||||
|
||||
[Home Assistant](https://www.home-assistant.io/) — home automation.
|
||||
|
||||
- **Source:** [`sfh/containers/hass.nix`](../../../nixos/boxes/home/palace/vms/sfh/containers/hass.nix)
|
||||
- **Host:** NixOS container on `sfh`
|
||||
|
||||
## Role
|
||||
|
||||
- Runs Home Assistant plus supporting services in the container. The `hass-cli`
|
||||
is wired up against the local server for convenience.
|
||||
- Integrations/automations are configured here (see commit history for things
|
||||
like the "West Wood" integration).
|
||||
|
||||
## Networking
|
||||
|
||||
- `internal` assignment (alt name `hass-ctr`), plus a loopback assignment
|
||||
(`hass-ctr-lo`) used internally.
|
||||
@@ -1,21 +0,0 @@
|
||||
# palace (host)
|
||||
|
||||
The physical VM host for the home network — the home equivalent of `colony`.
|
||||
|
||||
- **Source:** [`nixos/boxes/home/palace/default.nix`](../../../nixos/boxes/home/palace/default.nix)
|
||||
(VM instances in [`palace/vms/default.nix`](../../../nixos/boxes/home/palace/vms/default.nix))
|
||||
|
||||
## Role
|
||||
|
||||
- Bare-metal host whose job is to run the home VMs via the `my.vms` module:
|
||||
`river` (router), `cellar` (storage), `sfh` (container host).
|
||||
- Provides the bridged networking those VMs sit on and passes through hardware
|
||||
where needed (e.g. NVMe drives to `cellar`, NICs to `river`).
|
||||
|
||||
## VMs hosted here
|
||||
|
||||
| VM | Role | Docs |
|
||||
| --- | --- | --- |
|
||||
| `river` | Home router (VRRP pair with `stream`) | [river.md](river.md) |
|
||||
| `cellar` | NVMe-oF storage server | [cellar.md](cellar.md) |
|
||||
| `sfh` | NixOS container host (Home Assistant, …) | [sfh.md](sfh.md) |
|
||||
@@ -1,25 +0,0 @@
|
||||
# river
|
||||
|
||||
One of the two home routers. `river` is a VM on `palace`; it forms a
|
||||
high-availability pair with the standalone `stream`.
|
||||
|
||||
- **Source:** [`nixos/boxes/home/palace/vms/river.nix`](../../../nixos/boxes/home/palace/vms/river.nix),
|
||||
built from [`routing-common`](../../../nixos/boxes/home/routing-common) (instance `0`)
|
||||
- **Host:** VM on `palace`
|
||||
- **Deploy address:** `192.168.68.1`
|
||||
|
||||
## Role
|
||||
|
||||
Everything in [`routing-common`](../../../nixos/boxes/home/routing-common):
|
||||
|
||||
- **VRRP/keepalived** failover with `stream` (`keepalived.nix`) — one router is
|
||||
master at a time, sharing virtual IPs.
|
||||
- **DHCP** via kea (`kea.nix`), **router advertisements** via radvd
|
||||
(`radvd.nix`).
|
||||
- **DNS** (`dns.nix`) — local resolver with a blocklist
|
||||
(`dns-blocklist.txt`) and a periodic update script.
|
||||
- **NAT / firewall** for the home LAN, with policy routing.
|
||||
- **AS211024 L2 mesh** link back to colony/`estuary` (and the other edge
|
||||
routers), so home and colony networks interconnect.
|
||||
|
||||
See [stream.md](stream.md) for the other half of the pair.
|
||||
@@ -1,24 +0,0 @@
|
||||
# sfh
|
||||
|
||||
The home **NixOS container host** ("smart from home" / home services).
|
||||
|
||||
- **Source:** [`nixos/boxes/home/palace/vms/sfh/`](../../../nixos/boxes/home/palace/vms/sfh)
|
||||
(`default.nix`, `containers/`)
|
||||
- **Host:** VM on `palace`
|
||||
|
||||
## Role
|
||||
|
||||
- Runs the home NixOS containers via `my.containers.instances`, in the same way
|
||||
`shill` does on colony.
|
||||
- Sits on the home network and connects to NVMe-oF storage (`cellar`) where
|
||||
needed.
|
||||
|
||||
## Containers
|
||||
|
||||
Defined in [`sfh/containers/`](../../../nixos/boxes/home/palace/vms/sfh/containers)
|
||||
and imported from its `containers/default.nix`:
|
||||
|
||||
| Container | Role | Docs |
|
||||
| --- | --- | --- |
|
||||
| `hass` | Home Assistant | [hass.md](hass.md) |
|
||||
| `unifi` | UniFi controller — **defined but currently commented out** of `containers/default.nix` | [unifi.md](unifi.md) |
|
||||
@@ -1,19 +0,0 @@
|
||||
# stream
|
||||
|
||||
One of the two home routers. `stream` is standalone hardware; it forms a
|
||||
high-availability pair with `river` (a VM on `palace`).
|
||||
|
||||
- **Source:** [`nixos/boxes/home/stream.nix`](../../../nixos/boxes/home/stream.nix),
|
||||
built from [`routing-common`](../../../nixos/boxes/home/routing-common) (instance `1`)
|
||||
- **Deploy address:** `192.168.68.2`
|
||||
|
||||
## Role
|
||||
|
||||
- Same [`routing-common`](../../../nixos/boxes/home/routing-common) role as
|
||||
[`river`](river.md): keepalived/VRRP, kea DHCP, radvd, DNS + blocklist, NAT and
|
||||
the AS211024 L2 mesh link to colony.
|
||||
- Additionally pulls in `mstpd` (`routing-common/mstpd.nix`) for spanning-tree on
|
||||
its bridged ports — `stream` is the one wired into the physical switching, so
|
||||
it manages the L2 topology.
|
||||
|
||||
See [river.md](river.md) for the other half of the pair.
|
||||
@@ -1,23 +0,0 @@
|
||||
# unifi
|
||||
|
||||
The UniFi network controller.
|
||||
|
||||
- **Source:** [`sfh/containers/unifi.nix`](../../../nixos/boxes/home/palace/vms/sfh/containers/unifi.nix)
|
||||
- **Host:** NixOS container on `sfh`
|
||||
|
||||
## Status
|
||||
|
||||
> **Currently disabled.** The system is still defined (`nixos.systems.unifi`),
|
||||
> but its import is commented out in
|
||||
> [`sfh/containers/default.nix`](../../../nixos/boxes/home/palace/vms/sfh/containers/default.nix),
|
||||
> so it is not deployed as a container right now. Re-enable by uncommenting
|
||||
> `./unifi.nix` there.
|
||||
|
||||
## Role
|
||||
|
||||
- Runs the UniFi controller (`services.unifi`) to manage the home UniFi network
|
||||
gear.
|
||||
|
||||
## Networking
|
||||
|
||||
- `internal` assignment (alt name `unifi-ctr`).
|
||||
@@ -1,21 +0,0 @@
|
||||
# misc
|
||||
|
||||
Everything that isn't part of the `colony` or `home` sites: the edge VPSes, the
|
||||
remote `kelder` site, a workstation, and the installer image.
|
||||
|
||||
| Machine | What it is | Docs |
|
||||
| --- | --- | --- |
|
||||
| `britway` | Vultr VPS (London, `lon1`): Headscale, Tailscale exit node, BGP edge, nginx | [britway.md](britway.md) |
|
||||
| `britnet` | VPS (Birmingham, `bhx1`): Tailscale/WireGuard gateway | [britnet.md](britnet.md) |
|
||||
| `kelder` | Remote site host (`hentai.engineer`): NixOS container host | [kelder.md](kelder.md) |
|
||||
| `tower` | Framework Laptop 13 (12th-gen Intel) workstation | [tower.md](tower.md) |
|
||||
| `installer` | Custom NixOS installer image | [installer.md](installer.md) |
|
||||
|
||||
## kelder containers
|
||||
|
||||
`kelder` is itself a container host (like `shill`/`sfh`):
|
||||
|
||||
| Container | Role | Docs |
|
||||
| --- | --- | --- |
|
||||
| `kelder-acquisition` | Media stack (Jellyfin + *arr + Transmission) | [kelder-acquisition.md](kelder-acquisition.md) |
|
||||
| `kelder-spoder` | nginx web host | [kelder-spoder.md](kelder-spoder.md) |
|
||||
@@ -1,21 +0,0 @@
|
||||
# britnet
|
||||
|
||||
A VPS in Birmingham (`bhx1`) acting as a Tailscale/WireGuard gateway node.
|
||||
|
||||
- **Source:** [`nixos/boxes/britnet.nix`](../../../nixos/boxes/britnet.nix)
|
||||
- **Internal domain:** `bhx1.int.nul.ie`
|
||||
|
||||
## Role
|
||||
|
||||
- **Tailscale node** + **WireGuard** (`wg0`) gateway: provides a second egress /
|
||||
entry point into the boxes' overlay networks.
|
||||
- nftables SNATs traffic arriving on `tailscale0` / `wg0` out of the provider
|
||||
interface (`veth0`), using the `allhost` assignment addresses.
|
||||
|
||||
## Networking
|
||||
|
||||
- Provider uplink with gateways `77.74.199.1` (v4) / `2a12:ab46:5344::1` (v6).
|
||||
- `tailscale0` and `wg0` overlay interfaces; `allhost` assignment for SNAT.
|
||||
|
||||
> `britnet` is a separate machine from [`britway`](britway.md) — different
|
||||
> provider/site, narrower role (gateway rather than control plane + BGP edge).
|
||||
@@ -1,27 +0,0 @@
|
||||
# britway
|
||||
|
||||
A Vultr VPS in London (`lon1`) acting as a network edge node: the Tailscale
|
||||
control plane, an exit node, and a BGP speaker in the AS211024 mesh.
|
||||
|
||||
- **Source:** [`nixos/boxes/britway/`](../../../nixos/boxes/britway)
|
||||
(`default.nix`, `bgp.nix`, `nginx.nix`, `tailscale.nix`)
|
||||
- **Internal domain:** `lon1.int.nul.ie`
|
||||
|
||||
## Role
|
||||
|
||||
- **Headscale** ([`tailscale.nix`](../../../nixos/boxes/britway/tailscale.nix)) — the
|
||||
self-hosted Tailscale control server (`hs.nul.ie`) the rest of the boxes log
|
||||
into.
|
||||
- **Tailscale node** — advertises itself as an **exit node** and advertises the
|
||||
tailnet routes, so tailnet clients can egress / reach internal prefixes via
|
||||
britway.
|
||||
- **BGP** ([`bgp.nix`](../../../nixos/boxes/britway/bgp.nix)) — part of the AS211024
|
||||
L2 VXLAN mesh (`my.vpns.l2`) alongside `estuary`, `river` and `stream`.
|
||||
- **nginx** ([`nginx.nix`](../../../nixos/boxes/britway/nginx.nix)) — reverse proxy /
|
||||
web front-end with ACME certs.
|
||||
|
||||
## Networking
|
||||
|
||||
- `vultr` assignment on the provider interface; `as211024` on the mesh.
|
||||
- A `veth0`/`tailscale0` setup with SNAT so tailnet traffic egresses via the VPS
|
||||
public IP.
|
||||
@@ -1,19 +0,0 @@
|
||||
# installer
|
||||
|
||||
The custom NixOS installer image used to bootstrap new boxes.
|
||||
|
||||
- **Source:** [`nixos/installer.nix`](../../../nixos/installer.nix)
|
||||
|
||||
## Role
|
||||
|
||||
- Defines `nixos.systems.installer`, a minimal system whose `my.buildAs.*`
|
||||
outputs produce installable artifacts — primarily a bootable **ISO**
|
||||
(`isoImage`), and the same base is reused for kexec/netboot trees.
|
||||
- Build it with the devshell commands (see [`AGENTS.md`](../../../AGENTS.md#commands)):
|
||||
- `build-iso installer`
|
||||
- `build-kexec installer` / `build-netboot installer`
|
||||
- A released ISO is what `colony`'s VM definitions reference as install media; the
|
||||
`update-installer` devshell command tags a release to trigger a rebuild.
|
||||
|
||||
This is a build target rather than a deployed machine — there is no running
|
||||
`installer` host.
|
||||
@@ -1,20 +0,0 @@
|
||||
# kelder-acquisition
|
||||
|
||||
The media stack for the `kelder` site — a slimmer cousin of colony's
|
||||
[`jackflix`](../colony/jackflix.md).
|
||||
|
||||
- **Source:** [`kelder/containers/acquisition/`](../../../nixos/boxes/kelder/containers/acquisition)
|
||||
(`default.nix`, `networking.nix`)
|
||||
- **Host:** NixOS container on `kelder`
|
||||
|
||||
## Role
|
||||
|
||||
- **Jellyfin** for streaming (with hardware transcoding — the `jellyfin` user is
|
||||
in the `render` group, `jellyfin-ffmpeg`).
|
||||
- **Acquisition:** Transmission, Jackett, Radarr, Sonarr.
|
||||
- Runs under the site's shared `kontent` user.
|
||||
|
||||
## Networking
|
||||
|
||||
- `internal` assignment (alt name `acquisition-ctr`) on the kelder container
|
||||
network; download client networking in `networking.nix`.
|
||||
@@ -1,17 +0,0 @@
|
||||
# kelder-spoder
|
||||
|
||||
An nginx web host on the `kelder` site.
|
||||
|
||||
- **Source:** [`kelder/containers/spoder/`](../../../nixos/boxes/kelder/containers/spoder)
|
||||
(`default.nix`, `nginx.nix`)
|
||||
- **Host:** NixOS container on `kelder`
|
||||
|
||||
## Role
|
||||
|
||||
- Serves web content via **nginx** ([`nginx.nix`](../../../nixos/boxes/kelder/containers/spoder/nginx.nix)),
|
||||
with ACME-managed certificates (nginx in the `acme` group, reloads on renewal).
|
||||
- Runs under the site's shared `kontent` user.
|
||||
|
||||
## Networking
|
||||
|
||||
- `internal` assignment (alt name `spoder-ctr`) on the kelder container network.
|
||||
@@ -1,24 +0,0 @@
|
||||
# kelder
|
||||
|
||||
A host at a remote site ("kelder" = cellar/basement), linked back to the rest of
|
||||
the other boxes over WireGuard. It is itself a **NixOS container host**.
|
||||
|
||||
- **Source:** [`nixos/boxes/kelder/`](../../../nixos/boxes/kelder)
|
||||
(`default.nix`, `boot.nix`, `containers/`, `plymouth/`)
|
||||
- **Domain:** `hentai.engineer`
|
||||
|
||||
## Role
|
||||
|
||||
- **Site uplink:** connects to colony's `estuary` over **WireGuard**
|
||||
(`kelder` peer; see [estuary.md](../colony/estuary.md)), so the remote site is
|
||||
reachable through the colony edge. A periodic `dns_update.py` keeps DNS current.
|
||||
- **Container host:** runs NixOS containers via `my.containers.instances`
|
||||
(`acquisition`, `spoder`).
|
||||
- Custom boot/splash (`boot.nix`, Plymouth theme in `plymouth/`).
|
||||
|
||||
## Containers
|
||||
|
||||
| Container | Role | Docs |
|
||||
| --- | --- | --- |
|
||||
| `kelder-acquisition` | Media stack (Jellyfin + *arr + Transmission) | [kelder-acquisition.md](kelder-acquisition.md) |
|
||||
| `kelder-spoder` | nginx web host | [kelder-spoder.md](kelder-spoder.md) |
|
||||
@@ -1,16 +0,0 @@
|
||||
# tower
|
||||
|
||||
A laptop workstation — a Framework Laptop 13 (12th-gen Intel).
|
||||
|
||||
- **Source:** [`nixos/boxes/tower/default.nix`](../../../nixos/boxes/tower/default.nix)
|
||||
|
||||
## Role
|
||||
|
||||
- Framework Laptop 13 (12th-gen Intel) running the GUI environment
|
||||
(`my.gui.enable`) with home-manager on the `mine` channel.
|
||||
- Joins the tailnet via the self-hosted Headscale on [`britway`](britway.md)
|
||||
(`tailscale up --login-server=https://hs.nul.ie --accept-routes`).
|
||||
- Local virtualisation enabled (`kvm-intel`, IOMMU on).
|
||||
|
||||
> Unlike [`castle`](../home/castle.md), `tower` lives outside the `home/` box
|
||||
> tree and boots from local disks rather than NVMe-oF.
|
||||
Generated
+266
-523
File diff suppressed because it is too large
Load Diff
@@ -3,52 +3,40 @@
|
||||
|
||||
inputs = {
|
||||
flake-utils.url = "github:numtide/flake-utils";
|
||||
# libnet.url = "github:reo101/nix-lib-net";
|
||||
libnetRepo = {
|
||||
url = "github:oddlama/nixos-extra-modules";
|
||||
flake = false;
|
||||
};
|
||||
devshell.url = "github:numtide/devshell";
|
||||
devshell.inputs.nixpkgs.follows = "nixpkgs-unstable";
|
||||
|
||||
nixpkgs-unstable.url = "nixpkgs/nixos-unstable";
|
||||
nixpkgs-stable.url = "nixpkgs/nixos-26.05";
|
||||
nixpkgs-stable.url = "nixpkgs/nixos-23.11";
|
||||
nixpkgs-mine.url = "github:devplayer0/nixpkgs/devplayer0";
|
||||
nixpkgs-mine-stable.url = "github:devplayer0/nixpkgs/devplayer0-stable";
|
||||
|
||||
home-manager-unstable.url = "home-manager";
|
||||
home-manager-unstable.inputs.nixpkgs.follows = "nixpkgs-unstable";
|
||||
home-manager-stable.url = "home-manager/release-26.05";
|
||||
home-manager-stable.url = "home-manager/release-23.11";
|
||||
home-manager-stable.inputs.nixpkgs.follows = "nixpkgs-stable";
|
||||
|
||||
# Stuff used by the flake for build / deployment
|
||||
# ragenix.url = "github:yaxitech/ragenix";
|
||||
ragenix.url = "github:devplayer0/ragenix/add-rekey-one-flag";
|
||||
ragenix.url = "github:yaxitech/ragenix";
|
||||
ragenix.inputs.nixpkgs.follows = "nixpkgs-unstable";
|
||||
deploy-rs.url = "github:serokell/deploy-rs";
|
||||
deploy-rs.inputs.nixpkgs.follows = "nixpkgs-unstable";
|
||||
|
||||
# Stuff used by systems
|
||||
impermanence.url = "github:nix-community/impermanence";
|
||||
impermanence.inputs.home-manager.follows = "home-manager-unstable";
|
||||
boardie.url = "github:devplayer0/boardie";
|
||||
boardie.inputs.nixpkgs.follows = "nixpkgs-unstable";
|
||||
nixGL.url = "github:nix-community/nixGL";
|
||||
nixGL.inputs.nixpkgs.follows = "nixpkgs-unstable";
|
||||
harmonia.url = "github:nix-community/harmonia";
|
||||
# harmonia.url = "github:devplayer0/harmonia/cache-config-daemon-store";
|
||||
harmonia.inputs.nixpkgs.follows = "nixpkgs-unstable";
|
||||
|
||||
# Packages not in nixpkgs
|
||||
sharry.url = "github:eikek/sharry";
|
||||
sharry.inputs.nixpkgs.follows = "nixpkgs-unstable";
|
||||
borgthin.url = "github:devplayer0/borg";
|
||||
# TODO: Update borgthin so this works
|
||||
# borgthin.inputs.nixpkgs.follows = "nixpkgs-mine";
|
||||
copyparty.url = "github:9001/copyparty";
|
||||
copyparty.inputs.nixpkgs.follows = "nixpkgs-unstable";
|
||||
hass-west-wood.url = "github:devplayer0/hass-west-wood";
|
||||
hass-west-wood.inputs.nixpkgs.follows = "nixpkgs-unstable";
|
||||
borgthin.inputs.nixpkgs.follows = "nixpkgs-mine";
|
||||
attic.url = "github:zhaofengli/attic";
|
||||
attic.inputs.nixpkgs.follows = "nixpkgs-unstable";
|
||||
attic.inputs.nixpkgs-stable.follows = "nixpkgs-stable";
|
||||
};
|
||||
|
||||
outputs =
|
||||
@@ -63,7 +51,7 @@
|
||||
...
|
||||
}:
|
||||
let
|
||||
inherit (builtins) mapAttrs replaceStrings elem;
|
||||
inherit (builtins) mapAttrs replaceStrings;
|
||||
inherit (lib) mapAttrs' filterAttrs nameValuePair recurseIntoAttrs evalModules;
|
||||
inherit (lib.flake) flattenTree eachDefaultSystem;
|
||||
inherit (lib.my) mkDefaultSystemsPkgs flakePackageOverlay;
|
||||
@@ -71,7 +59,7 @@
|
||||
# Extend a lib with extras that _must not_ internally reference private nixpkgs. flake-utils doesn't, but many
|
||||
# other flakes (e.g. home-manager) probably do internally.
|
||||
libOverlay = final: prev: {
|
||||
my = import ./lib { inherit inputs; lib = final; };
|
||||
my = import ./lib { lib = final; };
|
||||
flake = flake-utils.lib;
|
||||
};
|
||||
pkgsLibOverlay = final: prev: { lib = prev.lib.extend libOverlay; };
|
||||
@@ -102,12 +90,12 @@
|
||||
(_: path: mkDefaultSystemsPkgs path (system: {
|
||||
overlays = [
|
||||
pkgsLibOverlay
|
||||
|
||||
myPkgsOverlay
|
||||
inputs.devshell.overlays.default
|
||||
inputs.ragenix.overlays.default
|
||||
inputs.deploy-rs.overlays.default
|
||||
inputs.deploy-rs.overlay
|
||||
(flakePackageOverlay inputs.home-manager-unstable system)
|
||||
inputs.attic.overlays.default
|
||||
];
|
||||
}))
|
||||
pkgsFlakes;
|
||||
@@ -117,19 +105,8 @@
|
||||
(_: path: mkDefaultSystemsPkgs path (_: {
|
||||
overlays = [
|
||||
pkgsLibOverlay
|
||||
|
||||
myPkgsOverlay
|
||||
];
|
||||
|
||||
config = {
|
||||
# RMS forgive me...
|
||||
# Normally this is set modularly, but sometimes we need to use other pkgs
|
||||
allowUnfreePredicate = p: elem (lib.getName p) [
|
||||
"widevine-cdm"
|
||||
"chromium-unwrapped"
|
||||
"chromium"
|
||||
];
|
||||
};
|
||||
}))
|
||||
pkgsFlakes;
|
||||
|
||||
@@ -138,11 +115,10 @@
|
||||
nixos/installer.nix
|
||||
nixos/boxes/colony
|
||||
nixos/boxes/tower
|
||||
nixos/boxes/castle
|
||||
nixos/boxes/home/stream.nix
|
||||
nixos/boxes/home/palace
|
||||
nixos/boxes/home/castle
|
||||
nixos/boxes/britway
|
||||
nixos/boxes/britnet.nix
|
||||
nixos/boxes/kelder
|
||||
|
||||
# Homes
|
||||
@@ -173,7 +149,7 @@
|
||||
# Platform independent stuff
|
||||
{
|
||||
nixpkgs = pkgs';
|
||||
inherit inputs lib nixfiles;
|
||||
inherit lib nixfiles;
|
||||
|
||||
overlays.default = myPkgsOverlay;
|
||||
|
||||
@@ -221,9 +197,8 @@
|
||||
systems' = mapAttrs' (n: v: nameValuePair "system-${n}" v) systems;
|
||||
packages' = mapAttrs' (n: v: nameValuePair "package-${n}" v) packages;
|
||||
in
|
||||
homes' // systems' // packages' // {
|
||||
pkgs.linkFarm "ci" (homes' // systems' // packages' // {
|
||||
inherit shell;
|
||||
};
|
||||
ciDrv = pkgs.linkFarm "ci" ci;
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ in
|
||||
|
||||
lsd = {
|
||||
enable = mkDefault true;
|
||||
enableFishIntegration = mkDefault true;
|
||||
enableAliases = mkDefault true;
|
||||
};
|
||||
|
||||
starship = {
|
||||
@@ -132,38 +132,38 @@ in
|
||||
|
||||
ssh = {
|
||||
enable = mkDefault true;
|
||||
enableDefaultConfig = false;
|
||||
settings = {
|
||||
matchBlocks = {
|
||||
nix-dev-vm = {
|
||||
User = "dev";
|
||||
HostName = "localhost";
|
||||
Port = 2222;
|
||||
StrictHostKeyChecking = "no";
|
||||
UserKnownHostsFile = "/dev/null";
|
||||
user = "dev";
|
||||
hostname = "localhost";
|
||||
port = 2222;
|
||||
extraOptions = {
|
||||
StrictHostKeyChecking = "no";
|
||||
UserKnownHostsFile = "/dev/null";
|
||||
};
|
||||
};
|
||||
|
||||
"rsync.net" = {
|
||||
Host = "rsyncnet";
|
||||
User = "16413";
|
||||
HostName = "ch-s010.rsync";
|
||||
host = "rsyncnet";
|
||||
user = "16413";
|
||||
hostname = "ch-s010.rsync";
|
||||
};
|
||||
|
||||
shoe = {
|
||||
Host = "shoe.netsoc.tcd.ie shoe";
|
||||
User = "netsoc";
|
||||
host = "shoe.netsoc.tcd.ie shoe";
|
||||
user = "netsoc";
|
||||
};
|
||||
netsocBoxes = {
|
||||
Host = "cube spoon napalm gandalf saruman";
|
||||
User = "root";
|
||||
};
|
||||
|
||||
"*" = {
|
||||
IdentityFile = [
|
||||
"~/.ssh/id_rsa"
|
||||
"~/.ssh/borg"
|
||||
];
|
||||
host = "cube spoon napalm gandalf saruman";
|
||||
user = "root";
|
||||
};
|
||||
};
|
||||
extraConfig =
|
||||
''
|
||||
IdentityFile ~/.ssh/id_rsa
|
||||
IdentityFile ~/.ssh/netsoc
|
||||
IdentityFile ~/.ssh/borg
|
||||
'';
|
||||
};
|
||||
|
||||
direnv = {
|
||||
@@ -199,20 +199,17 @@ in
|
||||
file
|
||||
tree
|
||||
pwgen
|
||||
|
||||
minicom
|
||||
iperf3
|
||||
mosh
|
||||
wget
|
||||
hyx
|
||||
whois
|
||||
ldns
|
||||
minicom
|
||||
mtr
|
||||
|
||||
hyx
|
||||
ncdu
|
||||
jq
|
||||
yq-go
|
||||
nix-tree
|
||||
];
|
||||
|
||||
sessionVariables = {
|
||||
@@ -226,7 +223,7 @@ in
|
||||
# Note: If globalPkgs mode is on, then these will be overridden by the NixOS equivalents of these options
|
||||
nixpkgs = {
|
||||
overlays = [
|
||||
inputs.deploy-rs.overlays.default
|
||||
inputs.deploy-rs.overlay
|
||||
inputs.boardie.overlays.default
|
||||
inputs.nixGL.overlays.default
|
||||
];
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
# XTerm's default colors
|
||||
|
||||
# Default colors
|
||||
[colors.primary]
|
||||
background = '#000000'
|
||||
foreground = '#ffffff'
|
||||
|
||||
# Normal colors
|
||||
[colors.normal]
|
||||
black = '#000000'
|
||||
red = '#cd0000'
|
||||
green = '#00cd00'
|
||||
yellow = '#cdcd00'
|
||||
blue = '#0000ee'
|
||||
magenta = '#cd00cd'
|
||||
cyan = '#00cdcd'
|
||||
white = '#e5e5e5'
|
||||
|
||||
# Bright colors
|
||||
[colors.bright]
|
||||
black = '#7f7f7f'
|
||||
red = '#ff0000'
|
||||
green = '#00ff00'
|
||||
yellow = '#ffff00'
|
||||
blue = '#5c5cff'
|
||||
magenta = '#ff00ff'
|
||||
cyan = '#00ffff'
|
||||
white = '#ffffff'
|
||||
@@ -1,520 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# Source: https://github.com/daniel3303/ClaudeCodeStatusLine
|
||||
# Single line: Model | tokens | %used | %remain | think | 5h bar @reset | 7d bar @reset | extra
|
||||
|
||||
set -f # disable globbing
|
||||
VERSION="1.4.4"
|
||||
|
||||
input=$(cat)
|
||||
|
||||
if [ -z "$input" ]; then
|
||||
printf "Claude"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# ANSI colors matching oh-my-posh theme
|
||||
blue='\033[38;2;0;153;255m'
|
||||
orange='\033[38;2;255;176;85m'
|
||||
green='\033[38;2;0;160;0m'
|
||||
cyan='\033[38;2;46;149;153m'
|
||||
red='\033[38;2;255;85;85m'
|
||||
yellow='\033[38;2;230;200;0m'
|
||||
purple='\033[38;2;167;139;250m'
|
||||
white='\033[38;2;220;220;220m'
|
||||
dim='\033[2m'
|
||||
reset='\033[0m'
|
||||
|
||||
# Format token counts (e.g., 50k / 200k)
|
||||
format_tokens() {
|
||||
local num=$1
|
||||
if [ "$num" -ge 1000000 ]; then
|
||||
awk "BEGIN {v=sprintf(\"%.1f\",$num/1000000)+0; if(v==int(v)) printf \"%dm\",v; else printf \"%.1fm\",v}"
|
||||
elif [ "$num" -ge 1000 ]; then
|
||||
awk "BEGIN {printf \"%.0fk\", $num / 1000}"
|
||||
else
|
||||
printf "%d" "$num"
|
||||
fi
|
||||
}
|
||||
|
||||
# Format number with commas (e.g., 134,938)
|
||||
format_commas() {
|
||||
printf "%'d" "$1"
|
||||
}
|
||||
|
||||
# Return color escape based on usage percentage
|
||||
# Usage: usage_color <pct>
|
||||
usage_color() {
|
||||
local pct=$1
|
||||
if [ "$pct" -ge 90 ]; then echo "$red"
|
||||
elif [ "$pct" -ge 70 ]; then echo "$orange"
|
||||
elif [ "$pct" -ge 50 ]; then echo "$yellow"
|
||||
else echo "$green"
|
||||
fi
|
||||
}
|
||||
|
||||
# Resolve config directory: CLAUDE_CONFIG_DIR (set by alias) or default ~/.claude
|
||||
claude_config_dir="${CLAUDE_CONFIG_DIR:-$HOME/.claude}"
|
||||
|
||||
# Return 0 (true) if $1 > $2 using semantic versioning
|
||||
version_gt() {
|
||||
local a="${1#v}" b="${2#v}"
|
||||
local IFS='.'
|
||||
read -r a1 a2 a3 <<< "$a"
|
||||
read -r b1 b2 b3 <<< "$b"
|
||||
a1=${a1:-0}; a2=${a2:-0}; a3=${a3:-0}
|
||||
b1=${b1:-0}; b2=${b2:-0}; b3=${b3:-0}
|
||||
[ "$a1" -gt "$b1" ] 2>/dev/null && return 0
|
||||
[ "$a1" -lt "$b1" ] 2>/dev/null && return 1
|
||||
[ "$a2" -gt "$b2" ] 2>/dev/null && return 0
|
||||
[ "$a2" -lt "$b2" ] 2>/dev/null && return 1
|
||||
[ "$a3" -gt "$b3" ] 2>/dev/null && return 0
|
||||
return 1
|
||||
}
|
||||
# ===== Extract data from JSON =====
|
||||
model_name=$(echo "$input" | jq -r '.model.display_name // "Claude"')
|
||||
model_name=$(echo "$model_name" | sed 's/ *(\([0-9.]*[kKmM]*\) context)/ \1/') # "(1M context)" → "1M"
|
||||
|
||||
# Context window
|
||||
size=$(echo "$input" | jq -r '.context_window.context_window_size // 200000')
|
||||
[ "$size" -eq 0 ] 2>/dev/null && size=200000
|
||||
|
||||
# Token usage
|
||||
input_tokens=$(echo "$input" | jq -r '.context_window.current_usage.input_tokens // 0')
|
||||
cache_create=$(echo "$input" | jq -r '.context_window.current_usage.cache_creation_input_tokens // 0')
|
||||
cache_read=$(echo "$input" | jq -r '.context_window.current_usage.cache_read_input_tokens // 0')
|
||||
current=$(( input_tokens + cache_create + cache_read ))
|
||||
|
||||
used_tokens=$(format_tokens $current)
|
||||
total_tokens=$(format_tokens $size)
|
||||
|
||||
if [ "$size" -gt 0 ]; then
|
||||
pct_used=$(( current * 100 / size ))
|
||||
else
|
||||
pct_used=0
|
||||
fi
|
||||
pct_remain=$(( 100 - pct_used ))
|
||||
|
||||
used_comma=$(format_commas $current)
|
||||
remain_comma=$(format_commas $(( size - current )))
|
||||
|
||||
settings_path="$claude_config_dir/settings.json"
|
||||
effort_level=""
|
||||
stdin_effort=$(echo "$input" | jq -r '.effort.level // empty' 2>/dev/null)
|
||||
if [ -n "$stdin_effort" ]; then
|
||||
effort_level="$stdin_effort"
|
||||
elif [ -n "$CLAUDE_CODE_EFFORT_LEVEL" ]; then
|
||||
effort_level="$CLAUDE_CODE_EFFORT_LEVEL"
|
||||
elif [ -f "$settings_path" ]; then
|
||||
effort_val=$(jq -r '.effortLevel // empty' "$settings_path" 2>/dev/null)
|
||||
[ -n "$effort_val" ] && effort_level="$effort_val"
|
||||
fi
|
||||
[ -z "$effort_level" ] && effort_level="medium"
|
||||
|
||||
# ===== Claude CLI version (cached, 1h TTL) =====
|
||||
cli_version_cache="/tmp/claude/statusline-cli-version"
|
||||
cli_version=""
|
||||
cli_version_max_age=3600
|
||||
|
||||
if [ -f "$cli_version_cache" ]; then
|
||||
cv_mtime=$(stat -c %Y "$cli_version_cache" 2>/dev/null || stat -f %m "$cli_version_cache" 2>/dev/null)
|
||||
cv_now=$(date +%s)
|
||||
cv_age=$(( cv_now - cv_mtime ))
|
||||
if [ "$cv_age" -lt "$cli_version_max_age" ]; then
|
||||
cli_version=$(cat "$cli_version_cache" 2>/dev/null)
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -z "$cli_version" ]; then
|
||||
cli_version=$(claude --version 2>/dev/null | awk '{print $1}')
|
||||
if [ -n "$cli_version" ]; then
|
||||
mkdir -p /tmp/claude 2>/dev/null
|
||||
echo "$cli_version" > "$cli_version_cache"
|
||||
fi
|
||||
fi
|
||||
|
||||
# ===== Build single-line output =====
|
||||
out=""
|
||||
out+="${blue}${model_name}${reset}"
|
||||
|
||||
# Current working directory
|
||||
cwd=$(echo "$input" | jq -r '.cwd // empty')
|
||||
if [ -n "$cwd" ]; then
|
||||
display_dir="${cwd##*/}"
|
||||
git_branch=$(git -C "${cwd}" rev-parse --abbrev-ref HEAD 2>/dev/null)
|
||||
out+=" ${dim}|${reset} "
|
||||
out+="${cyan}${display_dir}${reset}"
|
||||
if [ -n "$git_branch" ]; then
|
||||
out+="${dim}@${reset}${green}${git_branch}${reset}"
|
||||
git_stat=$(git -C "${cwd}" diff --numstat 2>/dev/null | awk '{a+=$1; d+=$2} END {if (a+d>0) printf "+%d -%d", a, d}')
|
||||
[ -n "$git_stat" ] && out+=" ${dim}(${reset}${green}${git_stat%% *}${reset} ${red}${git_stat##* }${reset}${dim})${reset}"
|
||||
fi
|
||||
fi
|
||||
|
||||
out+=" ${dim}|${reset} "
|
||||
out+="${orange}${used_tokens}/${total_tokens}${reset} ${dim}(${reset}${green}${pct_used}%${reset}${dim})${reset}"
|
||||
out+=" ${dim}|${reset} "
|
||||
out+="effort: "
|
||||
case "$effort_level" in
|
||||
low) out+="${dim}${effort_level}${reset}" ;;
|
||||
medium) out+="${orange}med${reset}" ;;
|
||||
high) out+="${green}${effort_level}${reset}" ;;
|
||||
xhigh) out+="${purple}${effort_level}${reset}" ;;
|
||||
max) out+="${red}${effort_level}${reset}" ;;
|
||||
*) out+="${green}${effort_level}${reset}" ;;
|
||||
esac
|
||||
|
||||
# ===== Cross-platform OAuth token resolution (from statusline.sh) =====
|
||||
# Tries credential sources in order: env var → macOS Keychain → Linux creds file → GNOME Keyring
|
||||
get_oauth_token() {
|
||||
local token=""
|
||||
|
||||
# 1. Explicit env var override
|
||||
if [ -n "$CLAUDE_CODE_OAUTH_TOKEN" ]; then
|
||||
echo "$CLAUDE_CODE_OAUTH_TOKEN"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# 2. macOS Keychain (Claude Code appends a SHA256 hash of CLAUDE_CONFIG_DIR to the service name)
|
||||
if command -v security >/dev/null 2>&1; then
|
||||
local keychain_svc="Claude Code-credentials"
|
||||
if [ -n "$CLAUDE_CONFIG_DIR" ]; then
|
||||
local dir_hash
|
||||
dir_hash=$(echo -n "$CLAUDE_CONFIG_DIR" | shasum -a 256 | cut -c1-8)
|
||||
keychain_svc="Claude Code-credentials-${dir_hash}"
|
||||
fi
|
||||
local blob
|
||||
blob=$(security find-generic-password -s "$keychain_svc" -w 2>/dev/null)
|
||||
if [ -n "$blob" ]; then
|
||||
token=$(echo "$blob" | jq -r '.claudeAiOauth.accessToken // empty' 2>/dev/null)
|
||||
if [ -n "$token" ] && [ "$token" != "null" ]; then
|
||||
echo "$token"
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# 3. Linux credentials file
|
||||
local creds_file="${claude_config_dir}/.credentials.json"
|
||||
if [ -f "$creds_file" ]; then
|
||||
token=$(jq -r '.claudeAiOauth.accessToken // empty' "$creds_file" 2>/dev/null)
|
||||
if [ -n "$token" ] && [ "$token" != "null" ]; then
|
||||
echo "$token"
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
|
||||
# 4. GNOME Keyring via secret-tool
|
||||
if command -v secret-tool >/dev/null 2>&1; then
|
||||
local blob
|
||||
blob=$(timeout 2 secret-tool lookup service "Claude Code-credentials" 2>/dev/null)
|
||||
if [ -n "$blob" ]; then
|
||||
token=$(echo "$blob" | jq -r '.claudeAiOauth.accessToken // empty' 2>/dev/null)
|
||||
if [ -n "$token" ] && [ "$token" != "null" ]; then
|
||||
echo "$token"
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
echo ""
|
||||
}
|
||||
|
||||
# ===== LINE 2 & 3: Usage limits with progress bars =====
|
||||
# First, try to use rate_limits data provided directly by Claude Code in the JSON input.
|
||||
# This is the most reliable source — no OAuth token or API call required.
|
||||
builtin_five_hour_pct=$(echo "$input" | jq -r '.rate_limits.five_hour.used_percentage // empty')
|
||||
builtin_five_hour_reset=$(echo "$input" | jq -r '.rate_limits.five_hour.resets_at // empty')
|
||||
builtin_seven_day_pct=$(echo "$input" | jq -r '.rate_limits.seven_day.used_percentage // empty')
|
||||
builtin_seven_day_reset=$(echo "$input" | jq -r '.rate_limits.seven_day.resets_at // empty')
|
||||
|
||||
use_builtin=false
|
||||
if [ -n "$builtin_five_hour_pct" ] || [ -n "$builtin_seven_day_pct" ]; then
|
||||
use_builtin=true
|
||||
fi
|
||||
|
||||
# Cache setup — shared across all Claude Code instances to avoid rate limits
|
||||
claude_config_dir_hash=$(echo -n "$claude_config_dir" | shasum -a 256 2>/dev/null || echo -n "$claude_config_dir" | sha256sum 2>/dev/null)
|
||||
claude_config_dir_hash=$(echo "$claude_config_dir_hash" | cut -c1-8)
|
||||
cache_file="/tmp/claude/statusline-usage-cache-${claude_config_dir_hash}.json"
|
||||
cache_max_age=60 # seconds between API calls
|
||||
mkdir -p /tmp/claude
|
||||
|
||||
needs_refresh=true
|
||||
usage_data=""
|
||||
|
||||
# Always load cache — used as primary source for API path, and as fallback when builtin reports zero
|
||||
if [ -f "$cache_file" ] && [ -s "$cache_file" ]; then
|
||||
cache_mtime=$(stat -c %Y "$cache_file" 2>/dev/null || stat -f %m "$cache_file" 2>/dev/null)
|
||||
now=$(date +%s)
|
||||
cache_age=$(( now - cache_mtime ))
|
||||
if [ "$cache_age" -lt "$cache_max_age" ]; then
|
||||
needs_refresh=false
|
||||
fi
|
||||
usage_data=$(cat "$cache_file" 2>/dev/null)
|
||||
fi
|
||||
|
||||
# When builtin values are all zero AND reset timestamps are missing, it likely indicates
|
||||
# an API failure on Claude's side — fall through to cached data instead of displaying
|
||||
# misleading 0%. Genuine zero responses (after a billing reset) still include valid
|
||||
# resets_at timestamps, so we trust those.
|
||||
effective_builtin=false
|
||||
if $use_builtin; then
|
||||
# Trust builtin if any percentage is non-zero
|
||||
if { [ -n "$builtin_five_hour_pct" ] && [ "$(printf '%.0f' "$builtin_five_hour_pct" 2>/dev/null)" != "0" ]; } || \
|
||||
{ [ -n "$builtin_seven_day_pct" ] && [ "$(printf '%.0f' "$builtin_seven_day_pct" 2>/dev/null)" != "0" ]; }; then
|
||||
effective_builtin=true
|
||||
fi
|
||||
# Also trust if reset timestamps are present — genuine zero responses include valid reset times
|
||||
if ! $effective_builtin; then
|
||||
if { [ -n "$builtin_five_hour_reset" ] && [ "$builtin_five_hour_reset" != "null" ] && [ "$builtin_five_hour_reset" != "0" ]; } || \
|
||||
{ [ -n "$builtin_seven_day_reset" ] && [ "$builtin_seven_day_reset" != "null" ] && [ "$builtin_seven_day_reset" != "0" ]; }; then
|
||||
effective_builtin=true
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# Refresh API cache when stale — runs regardless of builtin rate_limits because
|
||||
# extra_usage is only exposed through the OAuth usage endpoint (not stdin JSON).
|
||||
# Throttled to cache_max_age and stampede-locked via touch for shared panes.
|
||||
if $needs_refresh; then
|
||||
touch "$cache_file" # stampede lock: prevent parallel panes from fetching simultaneously
|
||||
token=$(get_oauth_token)
|
||||
if [ -n "$token" ] && [ "$token" != "null" ]; then
|
||||
response=$(curl -s --max-time 10 \
|
||||
-H "Accept: application/json" \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer $token" \
|
||||
-H "anthropic-beta: oauth-2025-04-20" \
|
||||
-H "User-Agent: claude-code/2.1.34" \
|
||||
"https://api.anthropic.com/api/oauth/usage" 2>/dev/null)
|
||||
# Only cache valid usage responses (not error/rate-limit JSON)
|
||||
if [ -n "$response" ] && echo "$response" | jq -e '.five_hour' >/dev/null 2>&1; then
|
||||
usage_data="$response"
|
||||
echo "$response" > "$cache_file"
|
||||
fi
|
||||
fi
|
||||
# Remove the stampede sentinel if the fetch failed to produce valid JSON —
|
||||
# otherwise an empty cache file would suppress retries for a full cache_max_age window.
|
||||
[ -f "$cache_file" ] && [ ! -s "$cache_file" ] && rm -f "$cache_file"
|
||||
fi
|
||||
|
||||
# Cross-platform ISO to epoch conversion
|
||||
# Converts ISO 8601 timestamp (e.g. "2025-06-15T12:30:00Z" or "2025-06-15T12:30:00.123+00:00") to epoch seconds.
|
||||
# Properly handles UTC timestamps and converts to local time.
|
||||
iso_to_epoch() {
|
||||
local iso_str="$1"
|
||||
|
||||
# Try GNU date first (Linux) — handles ISO 8601 format automatically
|
||||
local epoch
|
||||
epoch=$(date -d "${iso_str}" +%s 2>/dev/null)
|
||||
if [ -n "$epoch" ]; then
|
||||
echo "$epoch"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# BSD date (macOS) - handle various ISO 8601 formats
|
||||
local stripped="${iso_str%%.*}" # Remove fractional seconds (.123456)
|
||||
stripped="${stripped%%Z}" # Remove trailing Z
|
||||
stripped="${stripped%%+*}" # Remove timezone offset (+00:00)
|
||||
stripped="${stripped%%-[0-9][0-9]:[0-9][0-9]}" # Remove negative timezone offset
|
||||
|
||||
# Check if timestamp is UTC (has Z or +00:00 or -00:00)
|
||||
if [[ "$iso_str" == *"Z"* ]] || [[ "$iso_str" == *"+00:00"* ]] || [[ "$iso_str" == *"-00:00"* ]]; then
|
||||
# For UTC timestamps, parse with timezone set to UTC
|
||||
epoch=$(env TZ=UTC date -j -f "%Y-%m-%dT%H:%M:%S" "$stripped" +%s 2>/dev/null)
|
||||
else
|
||||
epoch=$(date -j -f "%Y-%m-%dT%H:%M:%S" "$stripped" +%s 2>/dev/null)
|
||||
fi
|
||||
|
||||
if [ -n "$epoch" ]; then
|
||||
echo "$epoch"
|
||||
return 0
|
||||
fi
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
# Format ISO reset time to compact local time
|
||||
# Usage: format_reset_time <iso_string> <style: time|datetime|date>
|
||||
format_reset_time() {
|
||||
local iso_str="$1"
|
||||
local style="$2"
|
||||
{ [ -z "$iso_str" ] || [ "$iso_str" = "null" ]; } && return
|
||||
|
||||
# Parse ISO datetime and convert to local time (cross-platform)
|
||||
local epoch
|
||||
epoch=$(iso_to_epoch "$iso_str")
|
||||
[ -z "$epoch" ] && return
|
||||
|
||||
# Format based on style
|
||||
# Try GNU date first (Linux), then BSD date (macOS)
|
||||
# Previous implementation piped BSD date through sed/tr, which always returned
|
||||
# exit code 0 from the last pipe stage, preventing the GNU date fallback from
|
||||
# ever executing on Linux.
|
||||
local formatted=""
|
||||
case "$style" in
|
||||
time)
|
||||
formatted=$(date -d "@$epoch" +"%H:%M" 2>/dev/null) || \
|
||||
formatted=$(date -j -r "$epoch" +"%H:%M" 2>/dev/null)
|
||||
;;
|
||||
datetime)
|
||||
formatted=$(date -d "@$epoch" +"%a %b %-d, %H:%M" 2>/dev/null) || \
|
||||
formatted=$(date -j -r "$epoch" +"%a %b %-d, %H:%M" 2>/dev/null)
|
||||
;;
|
||||
*)
|
||||
formatted=$(date -d "@$epoch" +"%b %-d" 2>/dev/null) || \
|
||||
formatted=$(date -j -r "$epoch" +"%b %-d" 2>/dev/null)
|
||||
;;
|
||||
esac
|
||||
[ -n "$formatted" ] && echo "$formatted"
|
||||
}
|
||||
|
||||
sep=" ${dim}|${reset} "
|
||||
|
||||
# Render extra_usage segment from API usage data (not available via stdin rate_limits).
|
||||
# Appends to the global $out. No-op when data is missing or is_enabled is false.
|
||||
render_extra_usage() {
|
||||
local data="$1"
|
||||
[ -z "$data" ] && return
|
||||
local enabled
|
||||
enabled=$(echo "$data" | jq -r '.extra_usage.is_enabled // false' 2>/dev/null)
|
||||
[ "$enabled" != "true" ] && return
|
||||
|
||||
local pct used limit
|
||||
pct=$(echo "$data" | jq -r '.extra_usage.utilization // 0' | awk '{printf "%.0f", $1}')
|
||||
used=$(echo "$data" | jq -r '.extra_usage.used_credits // 0' | LC_NUMERIC=C awk '{printf "%.2f", $1/100}')
|
||||
limit=$(echo "$data" | jq -r '.extra_usage.monthly_limit // 0' | LC_NUMERIC=C awk '{printf "%.2f", $1/100}')
|
||||
|
||||
if [ -n "$used" ] && [ -n "$limit" ] && [[ "$used" != *'$'* ]] && [[ "$limit" != *'$'* ]]; then
|
||||
local color
|
||||
color=$(usage_color "$pct")
|
||||
out+="${sep}${white}extra${reset} ${color}\$${used}/\$${limit}${reset}"
|
||||
else
|
||||
out+="${sep}${white}extra${reset} ${green}enabled${reset}"
|
||||
fi
|
||||
}
|
||||
|
||||
if $effective_builtin; then
|
||||
# ---- Use rate_limits data provided directly by Claude Code in JSON input ----
|
||||
# resets_at values are Unix epoch integers in this source
|
||||
if [ -n "$builtin_five_hour_pct" ]; then
|
||||
five_hour_pct=$(printf "%.0f" "$builtin_five_hour_pct")
|
||||
five_hour_color=$(usage_color "$five_hour_pct")
|
||||
out+="${sep}${white}5h${reset} ${five_hour_color}${five_hour_pct}%${reset}"
|
||||
if [ -n "$builtin_five_hour_reset" ] && [ "$builtin_five_hour_reset" != "null" ]; then
|
||||
five_hour_reset=$(date -j -r "$builtin_five_hour_reset" +"%H:%M" 2>/dev/null || date -d "@$builtin_five_hour_reset" +"%H:%M" 2>/dev/null)
|
||||
[ -n "$five_hour_reset" ] && out+=" ${dim}@${five_hour_reset}${reset}"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -n "$builtin_seven_day_pct" ]; then
|
||||
seven_day_pct=$(printf "%.0f" "$builtin_seven_day_pct")
|
||||
seven_day_color=$(usage_color "$seven_day_pct")
|
||||
out+="${sep}${white}7d${reset} ${seven_day_color}${seven_day_pct}%${reset}"
|
||||
if [ -n "$builtin_seven_day_reset" ] && [ "$builtin_seven_day_reset" != "null" ]; then
|
||||
seven_day_reset=$(date -j -r "$builtin_seven_day_reset" +"%a %b %-d, %H:%M" 2>/dev/null || date -d "@$builtin_seven_day_reset" +"%a %b %-d, %H:%M" 2>/dev/null)
|
||||
[ -n "$seven_day_reset" ] && out+=" ${dim}@${seven_day_reset}${reset}"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Render extra_usage from API cache (stdin rate_limits doesn't expose it)
|
||||
render_extra_usage "$usage_data"
|
||||
|
||||
# Cache builtin values so they're available as fallback when API is unavailable.
|
||||
# Convert epoch resets_at to ISO 8601 for compatibility with the API-format cache parser.
|
||||
# Preserve extra_usage from prior API response so we don't clobber it.
|
||||
_fh_reset_json="null"
|
||||
if [ -n "$builtin_five_hour_reset" ] && [ "$builtin_five_hour_reset" != "null" ] && [ "$builtin_five_hour_reset" != "0" ]; then
|
||||
_fh_iso=$(date -u -r "$builtin_five_hour_reset" +"%Y-%m-%dT%H:%M:%SZ" 2>/dev/null || \
|
||||
date -u -d "@$builtin_five_hour_reset" +"%Y-%m-%dT%H:%M:%SZ" 2>/dev/null)
|
||||
[ -n "$_fh_iso" ] && _fh_reset_json="\"$_fh_iso\""
|
||||
fi
|
||||
_sd_reset_json="null"
|
||||
if [ -n "$builtin_seven_day_reset" ] && [ "$builtin_seven_day_reset" != "null" ] && [ "$builtin_seven_day_reset" != "0" ]; then
|
||||
_sd_iso=$(date -u -r "$builtin_seven_day_reset" +"%Y-%m-%dT%H:%M:%SZ" 2>/dev/null || \
|
||||
date -u -d "@$builtin_seven_day_reset" +"%Y-%m-%dT%H:%M:%SZ" 2>/dev/null)
|
||||
[ -n "$_sd_iso" ] && _sd_reset_json="\"$_sd_iso\""
|
||||
fi
|
||||
_extra_json=$(echo "$usage_data" | jq -c '.extra_usage // null' 2>/dev/null)
|
||||
[ -z "$_extra_json" ] && _extra_json="null"
|
||||
printf '{"five_hour":{"utilization":%s,"resets_at":%s},"seven_day":{"utilization":%s,"resets_at":%s},"extra_usage":%s}' \
|
||||
"${builtin_five_hour_pct:-0}" "$_fh_reset_json" \
|
||||
"${builtin_seven_day_pct:-0}" "$_sd_reset_json" \
|
||||
"$_extra_json" > "$cache_file" 2>/dev/null
|
||||
elif [ -n "$usage_data" ] && echo "$usage_data" | jq -e '.five_hour' >/dev/null 2>&1; then
|
||||
# ---- Fall back: API-fetched usage data ----
|
||||
# ---- 5-hour (current) ----
|
||||
five_hour_pct=$(echo "$usage_data" | jq -r '.five_hour.utilization // 0' | awk '{printf "%.0f", $1}')
|
||||
five_hour_reset_iso=$(echo "$usage_data" | jq -r '.five_hour.resets_at // empty')
|
||||
five_hour_reset=$(format_reset_time "$five_hour_reset_iso" "time")
|
||||
five_hour_color=$(usage_color "$five_hour_pct")
|
||||
|
||||
out+="${sep}${white}5h${reset} ${five_hour_color}${five_hour_pct}%${reset}"
|
||||
[ -n "$five_hour_reset" ] && out+=" ${dim}@${five_hour_reset}${reset}"
|
||||
|
||||
# ---- 7-day (weekly) ----
|
||||
seven_day_pct=$(echo "$usage_data" | jq -r '.seven_day.utilization // 0' | awk '{printf "%.0f", $1}')
|
||||
seven_day_reset_iso=$(echo "$usage_data" | jq -r '.seven_day.resets_at // empty')
|
||||
seven_day_reset=$(format_reset_time "$seven_day_reset_iso" "datetime")
|
||||
seven_day_color=$(usage_color "$seven_day_pct")
|
||||
|
||||
out+="${sep}${white}7d${reset} ${seven_day_color}${seven_day_pct}%${reset}"
|
||||
[ -n "$seven_day_reset" ] && out+=" ${dim}@${seven_day_reset}${reset}"
|
||||
|
||||
render_extra_usage "$usage_data"
|
||||
else
|
||||
# No valid usage data — show placeholders
|
||||
out+="${sep}${white}5h${reset} ${dim}-${reset}"
|
||||
out+="${sep}${white}7d${reset} ${dim}-${reset}"
|
||||
fi
|
||||
|
||||
# ===== Update check (cached, 24h TTL) =====
|
||||
# Set STATUSLINE_CHECK_UPDATES=false to disable the update check (no network calls).
|
||||
update_line=""
|
||||
if [ "${STATUSLINE_CHECK_UPDATES:-true}" != "false" ]; then
|
||||
version_cache_file="/tmp/claude/statusline-version-cache.json"
|
||||
version_cache_max_age=86400 # 24 hours
|
||||
|
||||
version_needs_refresh=true
|
||||
version_data=""
|
||||
|
||||
if [ -f "$version_cache_file" ]; then
|
||||
vc_mtime=$(stat -c %Y "$version_cache_file" 2>/dev/null || stat -f %m "$version_cache_file" 2>/dev/null)
|
||||
vc_now=$(date +%s)
|
||||
vc_age=$(( vc_now - vc_mtime ))
|
||||
if [ "$vc_age" -lt "$version_cache_max_age" ]; then
|
||||
version_needs_refresh=false
|
||||
fi
|
||||
version_data=$(cat "$version_cache_file" 2>/dev/null)
|
||||
fi
|
||||
|
||||
if $version_needs_refresh; then
|
||||
touch "$version_cache_file" 2>/dev/null
|
||||
vc_response=$(curl -s --max-time 5 \
|
||||
-H "Accept: application/vnd.github+json" \
|
||||
"https://api.github.com/repos/daniel3303/ClaudeCodeStatusLine/releases/latest" 2>/dev/null)
|
||||
if [ -n "$vc_response" ] && echo "$vc_response" | jq -e '.tag_name' >/dev/null 2>&1; then
|
||||
version_data="$vc_response"
|
||||
echo "$vc_response" > "$version_cache_file"
|
||||
elif [ ! -s "$version_cache_file" ]; then
|
||||
rm -f "$version_cache_file" 2>/dev/null
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -n "$version_data" ]; then
|
||||
latest_tag=$(echo "$version_data" | jq -r '.tag_name // empty')
|
||||
if [ -n "$latest_tag" ] && version_gt "$latest_tag" "$VERSION"; then
|
||||
update_line="\n${dim}Update available: ${latest_tag} → Tell Claude: \"Find my installed status bar and update it\"${reset}"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# Append CLI version as last segment
|
||||
if [ -n "$cli_version" ]; then
|
||||
out+=" ${dim}|${reset} ${orange}v${cli_version}${reset}"
|
||||
fi
|
||||
|
||||
# Output
|
||||
printf "%b" "$out$update_line"
|
||||
|
||||
exit 0
|
||||
@@ -1,8 +1,7 @@
|
||||
{ lib, pkgs', pkgs, config, ... }:
|
||||
{ lib, pkgs, config, ... }:
|
||||
let
|
||||
inherit (lib) genAttrs mkIf mkMerge mkForce mapAttrs mkOptionDefault mkDefault;
|
||||
inherit (lib.my) mkOpt' mkBoolOpt';
|
||||
inherit (lib.my.c) pubDomain;
|
||||
inherit (lib) genAttrs mkIf mkMerge mkForce;
|
||||
inherit (lib.my) mkBoolOpt';
|
||||
|
||||
cfg = config.my.gui;
|
||||
|
||||
@@ -11,80 +10,33 @@ let
|
||||
name = "Monocraft";
|
||||
size = 10;
|
||||
};
|
||||
|
||||
doomWad = pkgs.fetchurl {
|
||||
url = "https://distro.ibiblio.org/slitaz/sources/packages/d/doom1.wad";
|
||||
hash = "sha256-HX1DvlAeZ9kn5BXguPPinDvzMHXoWXIYFvZSpSbKx3E=";
|
||||
};
|
||||
subwaySurfers = pkgs.fetchurl {
|
||||
url = "https://p.${pubDomain}/video/subway-surfers-smol.mkv";
|
||||
hash = "sha256-fMe7TDRNTymRHIJOi7qG3trzu4GP8a3gCDz+FMkX1dY=";
|
||||
};
|
||||
minecraftParkour = pkgs.fetchurl {
|
||||
url = "https://p.${pubDomain}/video/minecraft-parkour-smol.mkv";
|
||||
hash = "sha256-723pRm4AsIjY/WFUyAHzTJp+JvH4Pn5hvzF9wHTnOPA=";
|
||||
};
|
||||
|
||||
genLipsum = pkgs.writeScript "lipsum" ''
|
||||
#!${pkgs.python3.withPackages (ps: [ ps.python-lorem ])}/bin/python
|
||||
import lorem
|
||||
print(lorem.get_paragraph(count=5, sep='\n\n'))
|
||||
'';
|
||||
doomsaver' = brainrotTextCommand: pkgs.runCommand "doomsaver" {
|
||||
inherit (pkgs) windowtolayer tmux terminaltexteffects;
|
||||
chocoDoom = pkgs.chocolate-doom2xx;
|
||||
ffmpeg = pkgs.ffmpeg-full;
|
||||
python = pkgs.python3.withPackages (ps: [ ps.filelock ]);
|
||||
|
||||
inherit doomWad;
|
||||
enojy = ./enojy.jpg;
|
||||
inherit brainrotTextCommand subwaySurfers minecraftParkour;
|
||||
} ''
|
||||
mkdir -p "$out"/bin
|
||||
substituteAll ${./screensaver.py} "$out"/bin/doomsaver
|
||||
chmod +x "$out"/bin/doomsaver
|
||||
'';
|
||||
doomsaver = doomsaver' cfg.screensaver.brainrotTextCommand;
|
||||
in
|
||||
{
|
||||
options.my.gui = with lib.types; {
|
||||
options.my.gui = {
|
||||
enable = mkBoolOpt' true "Enable settings and packages meant for graphical systems";
|
||||
manageGraphical = mkBoolOpt' false "Configure the graphical session";
|
||||
standalone = mkBoolOpt' false "Enable settings for fully Nix managed systems";
|
||||
screensaver.brainrotTextCommand = mkOpt' (either path str) genLipsum "Command to generate brainrot text.";
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable (mkMerge [
|
||||
{
|
||||
home = {
|
||||
packages = with pkgs; [
|
||||
xdg-utils
|
||||
|
||||
font.package
|
||||
nerd-fonts.sauce-code-pro
|
||||
nerd-fonts.droid-sans-mono
|
||||
noto-fonts-color-emoji
|
||||
(nerdfonts.override {
|
||||
fonts = [ "DroidSansMono" "SourceCodePro" ];
|
||||
})
|
||||
noto-fonts-emoji
|
||||
|
||||
grim
|
||||
slurp
|
||||
swappy
|
||||
|
||||
python3Packages.python-lsp-server
|
||||
python310Packages.python-lsp-server
|
||||
nil # nix language server
|
||||
zls # zig language server
|
||||
rust-analyzer
|
||||
|
||||
cowsay
|
||||
fortune
|
||||
jp2a
|
||||
terminaltexteffects
|
||||
screenfetch
|
||||
fastfetch
|
||||
cmatrix
|
||||
doomsaver
|
||||
|
||||
ffmpeg-full
|
||||
xournalpp
|
||||
neofetch
|
||||
];
|
||||
};
|
||||
|
||||
@@ -99,15 +51,7 @@ in
|
||||
alacritty = {
|
||||
enable = true;
|
||||
settings = {
|
||||
general.import = [ ./alacritty-xterm.toml ];
|
||||
|
||||
font = {
|
||||
size = font.size;
|
||||
normal = {
|
||||
family = font.name;
|
||||
style = "Regular";
|
||||
};
|
||||
};
|
||||
font.normal.family = font.name;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -115,23 +59,9 @@ in
|
||||
enable = true;
|
||||
inherit font;
|
||||
settings = {
|
||||
background_opacity = "0.65";
|
||||
background_opacity = "0.8";
|
||||
tab_bar_edge = "top";
|
||||
shell_integration = "no-sudo";
|
||||
font_features = "${font.name} -liga";
|
||||
};
|
||||
};
|
||||
|
||||
foot = {
|
||||
enable = true;
|
||||
settings = {
|
||||
main = {
|
||||
font = "${font.name}:size=${toString font.size}";
|
||||
};
|
||||
colors = {
|
||||
alpha = 0.8;
|
||||
background = "000000";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
@@ -155,19 +85,6 @@ in
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
claude-code = {
|
||||
enable = true;
|
||||
settings = {
|
||||
model = "opus";
|
||||
theme = "auto";
|
||||
statusLine = {
|
||||
type = "command";
|
||||
command = ./claude-statusline.sh;
|
||||
padding = 0;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -191,19 +108,6 @@ in
|
||||
};
|
||||
Install.RequiredBy = [ "sway-session.target" ];
|
||||
};
|
||||
|
||||
activate-linux = {
|
||||
Unit = {
|
||||
Description = "Linux activation watermark";
|
||||
After = "graphical-session.target";
|
||||
PartOf = "graphical-session.target";
|
||||
};
|
||||
Service = {
|
||||
Type = "simple";
|
||||
ExecStart = "${pkgs.activate-linux}/bin/activate-linux";
|
||||
};
|
||||
Install.RequiredBy = [ "graphical-session.target" ];
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
@@ -213,7 +117,6 @@ in
|
||||
wl-clipboard
|
||||
wev
|
||||
wdisplays
|
||||
swaysome
|
||||
|
||||
pavucontrol
|
||||
libsecret
|
||||
@@ -223,11 +126,10 @@ in
|
||||
];
|
||||
|
||||
pointerCursor = {
|
||||
package = pkgs.posy-cursors;
|
||||
name = "Posy_Cursor";
|
||||
size = 32;
|
||||
package = pkgs.vanilla-dmz;
|
||||
name = "Vanilla-DMZ";
|
||||
size = 16;
|
||||
gtk.enable = true;
|
||||
x11.enable = true;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -236,36 +138,9 @@ in
|
||||
xsession.preferStatusNotifierItems = true;
|
||||
wayland = {
|
||||
windowManager = {
|
||||
sway =
|
||||
let
|
||||
cfg = config.wayland.windowManager.sway.config;
|
||||
mod = cfg.modifier;
|
||||
|
||||
renameWs = pkgs.writeShellScript "sway-rename-ws" ''
|
||||
focused_ws="$(swaymsg -t get_workspaces | jq ".[] | select(.focused)")"
|
||||
focused_num="$(jq -r ".num" <<< "$focused_ws")"
|
||||
focused_name="$(jq -r ".name" <<< "$focused_ws")"
|
||||
placeholder="$(sed -E 's/[0-9]+: //' <<< "$focused_name")"
|
||||
|
||||
name="$(rofi -dmenu -p "rename ws $focused_num" -theme+entry+placeholder "\"$placeholder\"")"
|
||||
if [ -n "$name" ]; then
|
||||
swaymsg rename workspace "$focused_name" to "$focused_num: $name"
|
||||
fi
|
||||
'';
|
||||
clearWsName = pkgs.writeShellScript "sway-clear-ws-name" ''
|
||||
focused_ws="$(swaymsg -t get_workspaces | jq ".[] | select(.focused)")"
|
||||
focused_num="$(jq -r ".num" <<< "$focused_ws")"
|
||||
focused_name="$(jq -r ".name" <<< "$focused_ws")"
|
||||
|
||||
swaymsg rename workspace "$focused_name" to "$focused_num"
|
||||
'';
|
||||
in
|
||||
{
|
||||
sway = {
|
||||
enable = true;
|
||||
xwayland = true;
|
||||
extraConfigEarly = ''
|
||||
set $mod ${mod}
|
||||
'';
|
||||
config = {
|
||||
input = {
|
||||
"type:touchpad" = {
|
||||
@@ -280,95 +155,31 @@ in
|
||||
|
||||
modifier = "Mod4";
|
||||
terminal = "kitty";
|
||||
keybindings = mapAttrs (k: mkOptionDefault) {
|
||||
"${mod}+Left" = "focus left";
|
||||
"${mod}+Down" = "focus down";
|
||||
"${mod}+Up" = "focus up";
|
||||
"${mod}+Right" = "focus right";
|
||||
keybindings =
|
||||
let
|
||||
cfg = config.wayland.windowManager.sway.config;
|
||||
mod = cfg.modifier;
|
||||
in
|
||||
lib.mkOptionDefault {
|
||||
"${mod}+d" = null;
|
||||
"${mod}+l" = "exec swaylock -i ${./lock.png} -s stretch";
|
||||
"${mod}+x" = "exec ${cfg.menu}";
|
||||
"${mod}+Shift+x" = "exec rofi -show drun";
|
||||
"${mod}+q" = "kill";
|
||||
"${mod}+Shift+q" = "exec swaynag -t warning -m 'bruh you really wanna kill sway?' -b 'ye' 'systemctl --user stop graphical-session.target && swaymsg exit'";
|
||||
"${mod}+Shift+d" = ''exec grim - | swappy -f -'';
|
||||
"${mod}+Shift+s" = ''exec grim -g "$(slurp)" - | swappy -f -'';
|
||||
"${mod}+Shift+e" = "exec rofi -show emoji";
|
||||
# Config for this doesn't seem to work :/
|
||||
"${mod}+c" = ''exec rofi -show calc -calc-command "echo -n '{result}' | ${pkgs.wl-clipboard}/bin/wl-copy"'';
|
||||
|
||||
"${mod}+Shift+Left" = "move left";
|
||||
"${mod}+Shift+Down" = "move down";
|
||||
"${mod}+Shift+Up" = "move up";
|
||||
"${mod}+Shift+Right" = "move right";
|
||||
|
||||
"${mod}+b" = "splith";
|
||||
"${mod}+v" = "splitv";
|
||||
"${mod}+f" = "fullscreen toggle";
|
||||
"${mod}+a" = "focus parent";
|
||||
|
||||
"${mod}+s" = "layout stacking";
|
||||
"${mod}+w" = "layout tabbed";
|
||||
"${mod}+e" = "layout toggle split";
|
||||
|
||||
"${mod}+Shift+space" = "floating toggle";
|
||||
"${mod}+space" = "focus mode_toggle";
|
||||
|
||||
"${mod}+1" = "workspace number 1";
|
||||
"${mod}+2" = "workspace number 2";
|
||||
"${mod}+3" = "workspace number 3";
|
||||
"${mod}+4" = "workspace number 4";
|
||||
"${mod}+5" = "workspace number 5";
|
||||
"${mod}+6" = "workspace number 6";
|
||||
"${mod}+7" = "workspace number 7";
|
||||
"${mod}+8" = "workspace number 8";
|
||||
"${mod}+9" = "workspace number 9";
|
||||
"${mod}+0" = "workspace number 10";
|
||||
|
||||
"${mod}+Shift+1" =
|
||||
"move container to workspace number 1";
|
||||
"${mod}+Shift+2" =
|
||||
"move container to workspace number 2";
|
||||
"${mod}+Shift+3" =
|
||||
"move container to workspace number 3";
|
||||
"${mod}+Shift+4" =
|
||||
"move container to workspace number 4";
|
||||
"${mod}+Shift+5" =
|
||||
"move container to workspace number 5";
|
||||
"${mod}+Shift+6" =
|
||||
"move container to workspace number 6";
|
||||
"${mod}+Shift+7" =
|
||||
"move container to workspace number 7";
|
||||
"${mod}+Shift+8" =
|
||||
"move container to workspace number 8";
|
||||
"${mod}+Shift+9" =
|
||||
"move container to workspace number 9";
|
||||
"${mod}+Shift+0" =
|
||||
"move container to workspace number 10";
|
||||
|
||||
"${mod}+Shift+minus" = "move scratchpad";
|
||||
"${mod}+minus" = "scratchpad show";
|
||||
|
||||
"${mod}+Return" = "exec ${cfg.terminal}";
|
||||
"${mod}+r" = "mode resize";
|
||||
"${mod}+d" = null;
|
||||
"${mod}+l" = "exec ${doomsaver}/bin/doomsaver";
|
||||
"${mod}+q" = "kill";
|
||||
"${mod}+Shift+c" = "reload";
|
||||
"${mod}+Shift+q" = "exec swaynag -t warning -m 'bruh you really wanna kill sway?' -b 'ye' 'systemctl --user stop graphical-session.target && swaymsg exit'";
|
||||
|
||||
# rofi
|
||||
"${mod}+x" = "exec ${cfg.menu}";
|
||||
"${mod}+Shift+x" = "exec rofi -show drun";
|
||||
"${mod}+Shift+e" = "exec rofi -show emoji";
|
||||
# Config for this doesn't seem to work :/
|
||||
"${mod}+c" = ''exec rofi -show calc -calc-command "echo -n '{result}' | ${pkgs.wl-clipboard}/bin/wl-copy"'';
|
||||
"${mod}+Shift+r" = "exec ${renameWs}";
|
||||
"${mod}+Shift+n" = "exec ${clearWsName}";
|
||||
|
||||
# Screenshots
|
||||
"${mod}+Shift+d" = ''exec grim - | swappy -f -'';
|
||||
"${mod}+Shift+s" = ''exec grim -g "$(slurp)" - | swappy -f -'';
|
||||
|
||||
"XF86MonBrightnessDown" = "exec ${pkgs.brightnessctl}/bin/brightnessctl set 5%-";
|
||||
"XF86MonBrightnessUp" = "exec ${pkgs.brightnessctl}/bin/brightnessctl set +5%";
|
||||
|
||||
"XF86AudioRaiseVolume" = "exec ${pkgs.pamixer}/bin/pamixer -i 5";
|
||||
"XF86AudioLowerVolume" = "exec ${pkgs.pamixer}/bin/pamixer -d 5";
|
||||
"XF86AudioPlay" = "exec ${pkgs.playerctl}/bin/playerctl play";
|
||||
"XF86AudioPause" = "exec ${pkgs.playerctl}/bin/playerctl pause";
|
||||
"XF86AudioNext" = "exec ${pkgs.playerctl}/bin/playerctl next";
|
||||
"XF86AudioPrev" = "exec ${pkgs.playerctl}/bin/playerctl previous";
|
||||
};
|
||||
"XF86AudioRaiseVolume" = "exec ${pkgs.pamixer}/bin/pamixer -i 5";
|
||||
"XF86AudioLowerVolume" = "exec ${pkgs.pamixer}/bin/pamixer -d 5";
|
||||
"XF86AudioPlay" = "exec ${pkgs.playerctl}/bin/playerctl play";
|
||||
"XF86AudioPause" = "exec ${pkgs.playerctl}/bin/playerctl pause";
|
||||
"XF86AudioNext" = "exec ${pkgs.playerctl}/bin/playerctl next";
|
||||
"XF86AudioPrev" = "exec ${pkgs.playerctl}/bin/playerctl previous";
|
||||
};
|
||||
keycodebindings = {
|
||||
# keycode for XF86AudioPlayPause (no sym for some reason)
|
||||
"172" = "exec ${pkgs.playerctl}/bin/playerctl play-pause";
|
||||
@@ -377,9 +188,6 @@ in
|
||||
menu = "rofi -show run";
|
||||
bars = mkForce [ ];
|
||||
};
|
||||
extraConfig = ''
|
||||
include ${./swaysome.conf}
|
||||
'';
|
||||
|
||||
swaynag = {
|
||||
enable = true;
|
||||
@@ -394,10 +202,6 @@ in
|
||||
name = "Numix";
|
||||
package = pkgs.numix-gtk-theme;
|
||||
};
|
||||
gtk4.theme = {
|
||||
name = "Numix";
|
||||
package = pkgs.numix-gtk-theme;
|
||||
};
|
||||
iconTheme = {
|
||||
name = "Numix";
|
||||
package = pkgs.numix-icon-theme;
|
||||
@@ -406,10 +210,17 @@ in
|
||||
};
|
||||
qt = {
|
||||
enable = true;
|
||||
platformTheme.name = "gtk";
|
||||
platformTheme = "gtk";
|
||||
};
|
||||
|
||||
services = {
|
||||
swaync = {
|
||||
enable = true;
|
||||
settings = {
|
||||
widgets = [ "title" "dnd" "mpris" "notifications" ];
|
||||
};
|
||||
};
|
||||
|
||||
playerctld.enable = true;
|
||||
spotifyd = {
|
||||
enable = false;
|
||||
@@ -426,45 +237,18 @@ in
|
||||
device_type = "computer";
|
||||
};
|
||||
};
|
||||
|
||||
easyeffects = {
|
||||
enable = mkDefault false;
|
||||
preset = mkDefault "moar-bass";
|
||||
extraPresets = {
|
||||
moar-bass = {
|
||||
output = {
|
||||
"bass_enhancer#0" = {
|
||||
amount = 3;
|
||||
blend = 0;
|
||||
bypass = false;
|
||||
floor = 20;
|
||||
floor-active = false;
|
||||
harmonics = 8.5;
|
||||
input-gain = 0;
|
||||
output-gain = 0;
|
||||
scope = 100;
|
||||
};
|
||||
blocklist = [ ];
|
||||
plugins_order = [ "bass_enhancer#0" ];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
programs = {
|
||||
git = {
|
||||
enable = true;
|
||||
settings = {
|
||||
user = {
|
||||
email = "jackos1998@gmail.com";
|
||||
name = "Jack O'Sullivan";
|
||||
};
|
||||
diff-so-fancy.enable = true;
|
||||
userEmail = "jackos1998@gmail.com";
|
||||
userName = "Jack O'Sullivan";
|
||||
extraConfig = {
|
||||
pull.rebase = true;
|
||||
};
|
||||
lfs.enable = true;
|
||||
};
|
||||
diff-so-fancy.enable = true;
|
||||
|
||||
waybar = import ./waybar.nix { inherit lib pkgs config font; };
|
||||
rofi = {
|
||||
@@ -487,7 +271,7 @@ in
|
||||
|
||||
chromium = {
|
||||
enable = true;
|
||||
package = (pkgs'.unstable.chromium.override { enableWideVine = true; }).overrideAttrs (old: {
|
||||
package = (pkgs.chromium.override { enableWideVine = true; }).overrideAttrs (old: {
|
||||
buildCommand = ''
|
||||
${old.buildCommand}
|
||||
|
||||
@@ -513,15 +297,6 @@ in
|
||||
] (_: "chromium-browser.desktop");
|
||||
};
|
||||
};
|
||||
|
||||
my = {
|
||||
swaync = {
|
||||
enable = true;
|
||||
settings = {
|
||||
widgets = [ "title" "dnd" "mpris" "notifications" ];
|
||||
};
|
||||
};
|
||||
};
|
||||
})
|
||||
|
||||
(mkIf (cfg.standalone && !pkgs.stdenv.isDarwin) {
|
||||
@@ -529,7 +304,6 @@ in
|
||||
userDirs = {
|
||||
enable = true;
|
||||
createDirectories = true;
|
||||
setSessionVariables = true;
|
||||
desktop = "$HOME/desktop";
|
||||
documents = "$HOME/documents";
|
||||
download = "$HOME/downloads";
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 7.4 KiB |
@@ -1,257 +0,0 @@
|
||||
#!@python@/bin/python
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import random
|
||||
import signal
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
import filelock
|
||||
|
||||
class Screensaver:
|
||||
def __init__(self, cmd, env=None, weight=1):
|
||||
self.cmd = cmd
|
||||
self.weight = weight
|
||||
|
||||
if env is not None:
|
||||
self.env = os.environ.copy()
|
||||
for k, v in env.items():
|
||||
self.env[k] = v
|
||||
else:
|
||||
self.env = None
|
||||
self.proc = None
|
||||
|
||||
def start(self):
|
||||
assert self.proc is None
|
||||
self.proc = subprocess.Popen(self.cmd, env=self.env)
|
||||
|
||||
def wait(self):
|
||||
assert self.proc is not None
|
||||
self.proc.wait()
|
||||
|
||||
def stop(self, kill=False):
|
||||
assert self.proc is not None
|
||||
if kill:
|
||||
self.proc.kill()
|
||||
else:
|
||||
self.proc.terminate()
|
||||
|
||||
class DoomSaver(Screensaver):
|
||||
wad = '@doomWad@'
|
||||
|
||||
def __init__(self, demo_index, weight=1.5):
|
||||
super().__init__(
|
||||
['@chocoDoom@/bin/chocolate-doom',
|
||||
'-iwad', self.wad,
|
||||
'-demoloopi', str(demo_index)],
|
||||
env={
|
||||
'SDL_AUDIODRIVER': 'null',
|
||||
'SDL_VIDEODRIVER': 'caca',
|
||||
'CACA_DRIVER': 'ncurses',
|
||||
},
|
||||
weight=weight,
|
||||
)
|
||||
|
||||
def stop(self):
|
||||
super().stop(kill=True)
|
||||
|
||||
class TTESaver(Screensaver):
|
||||
effects = (
|
||||
'beams,binarypath,blackhole,bouncyballs,bubbles,burn,colorshift,crumble,'
|
||||
'decrypt,errorcorrect,expand,fireworks,middleout,orbittingvolley,overflow,'
|
||||
'pour,print,rain,randomsequence,rings,scattered,slice,slide,spotlights,'
|
||||
'spray,swarm,synthgrid,unstable,vhstape,waves,wipe'
|
||||
).split(',')
|
||||
|
||||
def __init__(self, cmd, env=None, weight=1):
|
||||
super().__init__(cmd, env=env, weight=weight)
|
||||
self.running = False
|
||||
|
||||
def start(self):
|
||||
self.running = True
|
||||
|
||||
def wait(self):
|
||||
while self.running:
|
||||
effect_cmd = ['@terminaltexteffects@/bin/tte', random.choice(self.effects)]
|
||||
print(f"$ {self.cmd} | {' '.join(effect_cmd)}")
|
||||
content = subprocess.check_output(self.cmd, shell=True, env=self.env, stderr=subprocess.DEVNULL)
|
||||
|
||||
self.proc = subprocess.Popen(effect_cmd, stdin=subprocess.PIPE)
|
||||
self.proc.stdin.write(content)
|
||||
self.proc.stdin.close()
|
||||
self.proc.wait()
|
||||
|
||||
def stop(self):
|
||||
self.running = False
|
||||
self.proc.terminate()
|
||||
|
||||
class FFmpegCACASaver(Screensaver):
|
||||
@staticmethod
|
||||
def command(video, size):
|
||||
return ['@ffmpeg@/bin/ffmpeg', '-hide_banner', '-loglevel', 'error',
|
||||
'-stream_loop', '-1', '-i', video,
|
||||
'-pix_fmt', 'rgb24', '-window_size', f'{size}x{size}',
|
||||
'-f', 'caca', '-']
|
||||
|
||||
def __init__(self, video, weight=2):
|
||||
cols, lines = os.get_terminal_size()
|
||||
# IDK if it's reasonable to do this as "1:1"
|
||||
size = lines - 4
|
||||
super().__init__(
|
||||
self.command(video, size),
|
||||
env={'CACA_DRIVER': 'ncurses'},
|
||||
weight=weight,
|
||||
)
|
||||
|
||||
def stop(self):
|
||||
super().stop(kill=True)
|
||||
|
||||
class BrainrotStorySaver(Screensaver):
|
||||
def __init__(self, video, text_command, weight=2):
|
||||
cols, lines = os.get_terminal_size()
|
||||
video_size = lines - 1
|
||||
video_command = ' '.join(FFmpegCACASaver.command(video, video_size))
|
||||
text_command = (
|
||||
f'while true; do {text_command} | '
|
||||
f'@terminaltexteffects@/bin/tte --wrap-text --canvas-width=80 --canvas-height={video_size//2} --anchor-canvas=c '
|
||||
'print --final-gradient-stops=ffffff; clear; done' )
|
||||
self.tmux_session = f'screensaver-{os.urandom(4).hex()}'
|
||||
super().__init__(
|
||||
['@tmux@/bin/tmux', 'new-session', '-s', self.tmux_session, '-n', 'brainrot',
|
||||
text_command, ';', 'split-window', '-hbl', str(lines), video_command],
|
||||
# ['sh', '-c', text_command],
|
||||
env={
|
||||
'CACA_DRIVER': 'ncurses',
|
||||
'SHELL': '/bin/sh',
|
||||
},
|
||||
weight=weight,
|
||||
)
|
||||
|
||||
def stop(self):
|
||||
subprocess.check_call(['@tmux@/bin/tmux', 'kill-session', '-t', self.tmux_session])
|
||||
|
||||
class MultiSaver:
|
||||
savers = [
|
||||
DoomSaver(0),
|
||||
DoomSaver(1),
|
||||
DoomSaver(2),
|
||||
|
||||
Screensaver(['cmatrix']),
|
||||
|
||||
TTESaver('screenfetch -N'),
|
||||
TTESaver('fortune | cowsay'),
|
||||
TTESaver('top -bn1 | head -n50'),
|
||||
TTESaver('ss -nltu'),
|
||||
TTESaver('ss -ntu'),
|
||||
TTESaver('jp2a --width=100 @enojy@'),
|
||||
|
||||
BrainrotStorySaver('@subwaySurfers@', '@brainrotTextCommand@'),
|
||||
BrainrotStorySaver('@minecraftParkour@', '@brainrotTextCommand@'),
|
||||
]
|
||||
state_filename = 'screensaver.json'
|
||||
|
||||
def __init__(self, select=None):
|
||||
self.state_path = os.path.join(f'/run/user/{os.geteuid()}', self.state_filename)
|
||||
self.lock = filelock.FileLock(f'{self.state_path}.lock')
|
||||
|
||||
if select is not None:
|
||||
assert select >= 0 and select < len(self.savers), 'Invalid screensaver index'
|
||||
self.selected = self.savers[select]
|
||||
else:
|
||||
self.selected = None
|
||||
self.cleaned_up = False
|
||||
|
||||
def select(self):
|
||||
with self.lock:
|
||||
if not os.path.exists(self.state_path):
|
||||
state = {'instances': []}
|
||||
else:
|
||||
with open(self.state_path) as f:
|
||||
state = json.load(f)
|
||||
|
||||
if self.selected is None:
|
||||
available = set(range(len(self.savers)))
|
||||
new_instances = []
|
||||
for instance in state['instances']:
|
||||
if not os.path.exists(f"/proc/{instance['pid']}"):
|
||||
continue
|
||||
|
||||
new_instances.append(instance)
|
||||
i = instance['saver']
|
||||
assert i in available
|
||||
available.remove(i)
|
||||
assert available, 'No screensavers left'
|
||||
available = list(available)
|
||||
|
||||
weights = []
|
||||
for i in available:
|
||||
weights.append(self.savers[i].weight)
|
||||
selected_i = random.choices(available, weights=weights)[0]
|
||||
|
||||
new_instances.append({'pid': os.getpid(), 'saver': selected_i})
|
||||
state['instances'] = new_instances
|
||||
|
||||
# print(f'Selected saver {selected_i}')
|
||||
self.selected = self.savers[selected_i]
|
||||
|
||||
with open(self.state_path, 'w') as f:
|
||||
json.dump(state, f)
|
||||
|
||||
def cleanup(self):
|
||||
if self.cleaned_up:
|
||||
return
|
||||
self.cleaned_up = True
|
||||
|
||||
with self.lock:
|
||||
with open(self.state_path) as f:
|
||||
state = json.load(f)
|
||||
|
||||
for i, instance in enumerate(state['instances']):
|
||||
if instance['pid'] == os.getpid():
|
||||
del state['instances'][i]
|
||||
|
||||
with open(self.state_path, 'w') as f:
|
||||
json.dump(state, f)
|
||||
|
||||
def run(self):
|
||||
assert self.selected is not None
|
||||
self.selected.start()
|
||||
|
||||
signal.signal(signal.SIGINT, self._sighandler)
|
||||
signal.signal(signal.SIGTERM, self._sighandler)
|
||||
signal.signal(signal.SIGHUP, self._sighandler)
|
||||
self.selected.wait()
|
||||
self.cleanup()
|
||||
|
||||
def stop(self):
|
||||
assert self.selected is not None
|
||||
print('Shutting down')
|
||||
self.selected.stop()
|
||||
self.cleanup()
|
||||
def _sighandler(self, signum, frame):
|
||||
self.stop()
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description='Wayland terminal-based lock screen')
|
||||
parser.add_argument('-l', '--locker-cmd', default='swaylock-plugin', help='swaylock-plugin command to use')
|
||||
parser.add_argument('-t', '--terminal', default='alacritty', help='Terminal emulator to use')
|
||||
parser.add_argument('-i', '--instance', action='store_true', help='Run as instance')
|
||||
parser.add_argument('-s', '--screensaver', type=int, help='Force use of specific screensaver')
|
||||
|
||||
args = parser.parse_args()
|
||||
if not args.instance:
|
||||
cmd = [
|
||||
args.locker_cmd, '--command-each',
|
||||
f'@windowtolayer@/bin/windowtolayer -- {args.terminal} -e {sys.argv[0]} --instance']
|
||||
if args.screensaver is not None:
|
||||
cmd[-1] += f' --screensaver {args.screensaver}'
|
||||
subprocess.check_call(cmd)
|
||||
return
|
||||
|
||||
ms = MultiSaver(select=args.screensaver)
|
||||
ms.select()
|
||||
ms.run()
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 251 KiB After Width: | Height: | Size: 249 KiB |
@@ -1,66 +0,0 @@
|
||||
# Use (un)bindcode or (un)bindsym, depending on what you used in your main sway config file.
|
||||
# The `--no-warn` setting is only added to shortcuts that exist in the default config. You may want to add or remove
|
||||
# that flag on some bindings depending on your config.
|
||||
|
||||
|
||||
# Change focus between workspaces
|
||||
bindsym $mod+Alt+1 exec "swaysome focus 1"
|
||||
bindsym $mod+Alt+2 exec "swaysome focus 2"
|
||||
bindsym $mod+Alt+3 exec "swaysome focus 3"
|
||||
bindsym $mod+Alt+4 exec "swaysome focus 4"
|
||||
bindsym $mod+Alt+5 exec "swaysome focus 5"
|
||||
bindsym $mod+Alt+6 exec "swaysome focus 6"
|
||||
bindsym $mod+Alt+7 exec "swaysome focus 7"
|
||||
bindsym $mod+Alt+8 exec "swaysome focus 8"
|
||||
bindsym $mod+Alt+9 exec "swaysome focus 9"
|
||||
bindsym $mod+Alt+0 exec "swaysome focus 0"
|
||||
|
||||
# Focus workspace groups
|
||||
bindsym --no-warn $mod+1 exec "swaysome focus-group 1"
|
||||
bindsym --no-warn $mod+2 exec "swaysome focus-group 2"
|
||||
bindsym --no-warn $mod+3 exec "swaysome focus-group 3"
|
||||
bindsym --no-warn $mod+4 exec "swaysome focus-group 4"
|
||||
bindsym --no-warn $mod+5 exec "swaysome focus-group 5"
|
||||
bindsym --no-warn $mod+6 exec "swaysome focus-group 6"
|
||||
bindsym --no-warn $mod+7 exec "swaysome focus-group 7"
|
||||
bindsym --no-warn $mod+8 exec "swaysome focus-group 8"
|
||||
bindsym --no-warn $mod+9 exec "swaysome focus-group 9"
|
||||
bindsym --no-warn $mod+0 exec "swaysome focus-group 0"
|
||||
|
||||
# Move containers between workspaces
|
||||
bindsym $mod+Alt+Shift+1 exec "swaysome move 1"
|
||||
bindsym $mod+Alt+Shift+2 exec "swaysome move 2"
|
||||
bindsym $mod+Alt+Shift+3 exec "swaysome move 3"
|
||||
bindsym $mod+Alt+Shift+4 exec "swaysome move 4"
|
||||
bindsym $mod+Alt+Shift+5 exec "swaysome move 5"
|
||||
bindsym $mod+Alt+Shift+6 exec "swaysome move 6"
|
||||
bindsym $mod+Alt+Shift+7 exec "swaysome move 7"
|
||||
bindsym $mod+Alt+Shift+8 exec "swaysome move 8"
|
||||
bindsym $mod+Alt+Shift+9 exec "swaysome move 9"
|
||||
bindsym $mod+Alt+Shift+0 exec "swaysome move 0"
|
||||
|
||||
# Move containers to other workspace groups
|
||||
bindsym --no-warn $mod+Shift+1 exec "swaysome move-to-group 1"
|
||||
bindsym --no-warn $mod+Shift+2 exec "swaysome move-to-group 2"
|
||||
bindsym --no-warn $mod+Shift+3 exec "swaysome move-to-group 3"
|
||||
bindsym --no-warn $mod+Shift+4 exec "swaysome move-to-group 4"
|
||||
bindsym --no-warn $mod+Shift+5 exec "swaysome move-to-group 5"
|
||||
bindsym --no-warn $mod+Shift+6 exec "swaysome move-to-group 6"
|
||||
bindsym --no-warn $mod+Shift+7 exec "swaysome move-to-group 7"
|
||||
bindsym --no-warn $mod+Shift+8 exec "swaysome move-to-group 8"
|
||||
bindsym --no-warn $mod+Shift+9 exec "swaysome move-to-group 9"
|
||||
bindsym --no-warn $mod+Shift+0 exec "swaysome move-to-group 0"
|
||||
|
||||
# Move focused container to next output
|
||||
bindsym $mod+Alt+Right exec "swaysome next-output"
|
||||
# Move focused container to previous output
|
||||
bindsym $mod+Alt+Left exec "swaysome prev-output"
|
||||
|
||||
# Move focused workspace group to next output
|
||||
bindsym $mod+Shift+Alt+Right exec "swaysome workspace-group-next-output"
|
||||
# Move focused workspace group to previous output
|
||||
bindsym $mod+Shift+Alt+Left exec "swaysome workspace-group-prev-output"
|
||||
|
||||
# Init workspaces for every screen
|
||||
exec "swaysome init 1"
|
||||
|
||||
@@ -146,9 +146,9 @@ in
|
||||
dnd-none = "";
|
||||
};
|
||||
return-type = "json";
|
||||
exec = "${config.my.swaync.package}/bin/swaync-client -swb";
|
||||
on-click = "${config.my.swaync.package}/bin/swaync-client -t -sw";
|
||||
on-click-right = "${config.my.swaync.package}/bin/swaync-client -d -sw";
|
||||
exec = "${config.services.swaync.package}/bin/swaync-client -swb";
|
||||
on-click = "${config.services.swaync.package}/bin/swaync-client -t -sw";
|
||||
on-click-right = "${config.services.swaync.package}/bin/swaync-client -d -sw";
|
||||
escape = true;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -19,10 +19,10 @@ let
|
||||
};
|
||||
};
|
||||
|
||||
cfg = config.my.swaync;
|
||||
cfg = config.services.swaync;
|
||||
in
|
||||
{
|
||||
options.my.swaync = with lib.types; {
|
||||
options.services.swaync = with lib.types; {
|
||||
enable = mkEnableOption "Sway Notification Center";
|
||||
package = mkOption {
|
||||
type = package;
|
||||
|
||||
+9
-128
@@ -11,9 +11,6 @@ rec {
|
||||
jellyseerr = 402;
|
||||
atticd = 403;
|
||||
kea = 404;
|
||||
keepalived_script = 405;
|
||||
photoprism = 406;
|
||||
copyparty = 408;
|
||||
};
|
||||
gids = {
|
||||
matrix-syncv3 = 400;
|
||||
@@ -21,16 +18,12 @@ rec {
|
||||
jellyseerr = 402;
|
||||
atticd = 403;
|
||||
kea = 404;
|
||||
keepalived_script = 405;
|
||||
photoprism = 406;
|
||||
adbusers = 407;
|
||||
copyparty = 408;
|
||||
};
|
||||
};
|
||||
|
||||
kernel = {
|
||||
lts = pkgs: pkgs.linuxKernel.packages.linux_6_18;
|
||||
latest = pkgs: pkgs.linuxKernel.packages.linux_7_0;
|
||||
lts = pkgs: pkgs.linuxKernel.packages.linux_6_1;
|
||||
latest = pkgs: pkgs.linuxKernel.packages.linux_6_6;
|
||||
};
|
||||
|
||||
nginx = rec {
|
||||
@@ -101,10 +94,10 @@ rec {
|
||||
nix = {
|
||||
cache = rec {
|
||||
substituters = [
|
||||
"https://nix-cache.${pubDomain}"
|
||||
"https://nix-cache.${pubDomain}/main"
|
||||
];
|
||||
keys = [
|
||||
"nix-cache.nul.ie-1:BzH5yMfF4HbzY1C977XzOxoPhEc9Zbu39ftPkUbH+m4="
|
||||
"main:mMChkG8LwXrFirVfudqjSHasK1jV31OVElYD3eImYl8="
|
||||
];
|
||||
conf = ''
|
||||
extra-substituters = ${concatStringsSep " " substituters}
|
||||
@@ -114,7 +107,7 @@ rec {
|
||||
};
|
||||
|
||||
pubDomain = "nul.ie";
|
||||
colony = rec {
|
||||
colony = {
|
||||
domain = "ams1.int.${pubDomain}";
|
||||
pubV4 = "94.142.240.44";
|
||||
prefixes = with lib.my.net.cidr; rec {
|
||||
@@ -138,19 +131,6 @@ rec {
|
||||
v4 = subnet 8 3 all.v4;
|
||||
v6 = subnet 4 3 all.v6;
|
||||
};
|
||||
qclk = {
|
||||
v4 = subnet 8 4 all.v4;
|
||||
};
|
||||
|
||||
p2pTunnels = {
|
||||
v4 = subnet 8 5 all.v4;
|
||||
};
|
||||
hillcrest = {
|
||||
v4 = subnet 6 0 p2pTunnels.v4;
|
||||
};
|
||||
john-valorant = {
|
||||
v4 = subnet 6 1 p2pTunnels.v4;
|
||||
};
|
||||
|
||||
cust = {
|
||||
v4 = subnet 8 100 all.v4; # single ip for routing only
|
||||
@@ -164,10 +144,6 @@ rec {
|
||||
v4 = "94.142.242.255/32";
|
||||
v6 = subnet 8 1 cust.v6;
|
||||
};
|
||||
jam = {
|
||||
v4 = subnet 8 4 cust.v4;
|
||||
v6 = subnet 8 2 cust.v6;
|
||||
};
|
||||
|
||||
vip1 = "94.142.241.224/30";
|
||||
vip2 = "94.142.242.254/31";
|
||||
@@ -180,16 +156,6 @@ rec {
|
||||
home.v6 = "2a0e:97c0:4d0::/48";
|
||||
};
|
||||
|
||||
custRouting = with lib.my.net.cidr; {
|
||||
mail-vm = host 1 prefixes.cust.v4;
|
||||
darts-vm = host 2 prefixes.cust.v4;
|
||||
jam-ctr = host 3 prefixes.cust.v4;
|
||||
};
|
||||
|
||||
qclk = {
|
||||
wgPort = 51821;
|
||||
};
|
||||
|
||||
firewallForwards = aa: [
|
||||
{
|
||||
port = "http";
|
||||
@@ -203,7 +169,6 @@ rec {
|
||||
port = 8448;
|
||||
dst = aa.middleman.internal.ipv4.address;
|
||||
}
|
||||
|
||||
{
|
||||
port = 25565;
|
||||
dst = aa.simpcraft-oci.internal.ipv4.address;
|
||||
@@ -213,27 +178,8 @@ rec {
|
||||
dst = aa.simpcraft-staging-oci.internal.ipv4.address;
|
||||
}
|
||||
{
|
||||
port = 25567;
|
||||
dst = aa.kevcraft-oci.internal.ipv4.address;
|
||||
}
|
||||
{
|
||||
port = 25568;
|
||||
dst = aa.kinkcraft-oci.internal.ipv4.address;
|
||||
}
|
||||
{
|
||||
port = 25569;
|
||||
dst = aa.graeme-oci.internal.ipv4.address;
|
||||
}
|
||||
|
||||
# RCON... unsafe?
|
||||
# {
|
||||
# port = 25575;
|
||||
# dst = aa.simpcraft-oci.internal.ipv4.address;
|
||||
# }
|
||||
|
||||
{
|
||||
port = 7777;
|
||||
dst = aa.gam.internal.ipv4.address;
|
||||
port = 25575;
|
||||
dst = aa.simpcraft-oci.internal.ipv4.address;
|
||||
}
|
||||
|
||||
{
|
||||
@@ -258,44 +204,6 @@ rec {
|
||||
dst = aa.simpcraft-oci.internal.ipv4.address;
|
||||
proto = "udp";
|
||||
}
|
||||
{
|
||||
port = 25567;
|
||||
dst = aa.kevcraft-oci.internal.ipv4.address;
|
||||
proto = "udp";
|
||||
}
|
||||
{
|
||||
port = 25568;
|
||||
dst = aa.kinkcraft-oci.internal.ipv4.address;
|
||||
proto = "udp";
|
||||
}
|
||||
{
|
||||
port = 25569;
|
||||
dst = aa.graeme-oci.internal.ipv4.address;
|
||||
proto = "udp";
|
||||
}
|
||||
|
||||
{
|
||||
port = 15636;
|
||||
dst = aa.enshrouded-oci.internal.ipv4.address;
|
||||
proto = "udp";
|
||||
}
|
||||
{
|
||||
port = 15637;
|
||||
dst = aa.enshrouded-oci.internal.ipv4.address;
|
||||
proto = "udp";
|
||||
}
|
||||
|
||||
{
|
||||
port = qclk.wgPort;
|
||||
dst = aa.qclk.internal.ipv4.address;
|
||||
proto = "udp";
|
||||
}
|
||||
|
||||
{
|
||||
port = 7777;
|
||||
dst = aa.gam.internal.ipv4.address;
|
||||
proto = "udp";
|
||||
}
|
||||
];
|
||||
|
||||
fstrimConfig = {
|
||||
@@ -319,8 +227,8 @@ rec {
|
||||
"stream"
|
||||
];
|
||||
routersPubV4 = [
|
||||
"109.255.108.88"
|
||||
"109.255.108.121"
|
||||
"109.255.1.246"
|
||||
"109.255.252.63"
|
||||
];
|
||||
|
||||
prefixes = with lib.my.net.cidr; rec {
|
||||
@@ -369,8 +277,6 @@ rec {
|
||||
v6 = host ((1*65536*65536*65536) + 65535) prefixes.as211024.v6;
|
||||
};
|
||||
};
|
||||
|
||||
roceBootModules = [ "ib_core" "ib_uverbs" "mlx5_core" "mlx5_ib" ];
|
||||
};
|
||||
|
||||
britway = {
|
||||
@@ -386,20 +292,6 @@ rec {
|
||||
assignedV6 = "2001:19f0:7402:128b:5400:04ff:feac:6e06";
|
||||
};
|
||||
|
||||
britnet = {
|
||||
domain = "bhx1.int.${pubDomain}";
|
||||
pubV4 = "77.74.199.67";
|
||||
vpn = {
|
||||
port = 51820;
|
||||
};
|
||||
prefixes = with lib.my.net.cidr; rec {
|
||||
vpn = {
|
||||
v4 = "10.200.0.0/24";
|
||||
v6 = "fdfb:5ebf:6e84::/64";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
tailscale = {
|
||||
prefix = {
|
||||
v4 = "100.64.0.0/10";
|
||||
@@ -435,7 +327,6 @@ rec {
|
||||
};
|
||||
|
||||
domain = "hentai.engineer";
|
||||
ipv4MTU = 1460;
|
||||
vpn = {
|
||||
port = 51820;
|
||||
};
|
||||
@@ -444,21 +335,11 @@ rec {
|
||||
ctrs.v4 = subnet 4 0 all.v4;
|
||||
};
|
||||
};
|
||||
|
||||
hillcrest = {
|
||||
vpn.port = 51822;
|
||||
};
|
||||
|
||||
john-valorant = {
|
||||
vpn.port = 51823;
|
||||
};
|
||||
|
||||
sshKeyFiles = {
|
||||
me = ../.keys/me.pub;
|
||||
deploy = ../.keys/deploy.pub;
|
||||
rsyncNet = ../.keys/zh2855.rsync.net.pub;
|
||||
mailcowAcme = ../.keys/mailcow-acme.pub;
|
||||
harmonia = ../.keys/harmonia.pub;
|
||||
};
|
||||
sshHostKeys = {
|
||||
mail-vm = ../.keys/mail-vm-host.pub;
|
||||
|
||||
+6
-34
@@ -1,11 +1,11 @@
|
||||
{ inputs, lib }:
|
||||
{ lib }:
|
||||
let
|
||||
inherit (builtins) length match elemAt filter replaceStrings substring;
|
||||
inherit (lib)
|
||||
genAttrs mapAttrsToList filterAttrsRecursive nameValuePair types
|
||||
mkOption mkOverride mkForce mkIf mergeEqualOption optional
|
||||
showWarnings concatStringsSep flatten unique optionalAttrs
|
||||
mkBefore toLower splitString last;
|
||||
mkBefore;
|
||||
inherit (lib.flake) defaultSystems;
|
||||
in
|
||||
rec {
|
||||
@@ -23,7 +23,7 @@ rec {
|
||||
|
||||
attrsToNVList = mapAttrsToList nameValuePair;
|
||||
|
||||
inherit ((import "${inputs.libnetRepo}/lib/netu.nix" { inherit lib; }).lib) net;
|
||||
inherit (import ./net.nix { inherit lib; }) net;
|
||||
dns = import ./dns.nix { inherit lib; };
|
||||
c = import ./constants.nix { inherit lib; };
|
||||
|
||||
@@ -53,7 +53,7 @@ rec {
|
||||
in mkApp "${app}/bin/${app.meta.mainProgram}";
|
||||
flakePackageOverlay' = flake: pkg: system: (final: prev:
|
||||
let
|
||||
pkg' = if pkg != null then flake.packages.${system}.${pkg} else flake.packages.${system}.default;
|
||||
pkg' = if pkg != null then flake.packages.${system}.${pkg} else flake.defaultPackage.${system};
|
||||
name = if pkg != null then pkg else pkg'.name;
|
||||
in
|
||||
{
|
||||
@@ -248,38 +248,10 @@ rec {
|
||||
in
|
||||
{
|
||||
trivial = prev.trivial // {
|
||||
release = "26.06:u-${prev.trivial.release}";
|
||||
codeName = "Irritating";
|
||||
release = "23.12:u-${prev.trivial.release}";
|
||||
codeName = "Amogus";
|
||||
revisionWithDefault = default: self.rev or default;
|
||||
versionSuffix = ".${date}.${revCode self}:u-${revCode pkgsFlake}";
|
||||
};
|
||||
};
|
||||
upstreamRelease = last (splitString "-" lib.trivial.release);
|
||||
|
||||
netbootKeaClientClasses = { tftpIP, hostname, systems }:
|
||||
let
|
||||
testIPXE = "substring(option[user-class].hex, 0, 4) == 'iPXE'";
|
||||
in
|
||||
(mapAttrsToList (name: mac: {
|
||||
name = "nixos-${name}";
|
||||
test = "(${testIPXE}) and (hexstring(pkt4.mac, ':') == '${toLower mac}')";
|
||||
next-server = tftpIP;
|
||||
server-hostname = hostname;
|
||||
boot-file-name = "http://${hostname}/systems/${name}/menu.ipxe";
|
||||
}) systems) ++ [
|
||||
{
|
||||
name = "ipxe";
|
||||
test = testIPXE;
|
||||
next-server = tftpIP;
|
||||
server-hostname = hostname;
|
||||
boot-file-name = "http://${hostname}/boot.ipxe";
|
||||
}
|
||||
{
|
||||
name = "efi-x86_64";
|
||||
test = "option[client-system].hex == 0x0007";
|
||||
next-server = tftpIP;
|
||||
server-hostname = hostname;
|
||||
boot-file-name = "ipxe-x86_64.efi";
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
+1322
File diff suppressed because it is too large
Load Diff
@@ -1,191 +0,0 @@
|
||||
{ lib, ... }:
|
||||
let
|
||||
inherit (lib.my) net;
|
||||
inherit (lib.my.c) pubDomain;
|
||||
inherit (lib.my.c.britnet) domain pubV4 prefixes;
|
||||
in
|
||||
{
|
||||
nixos.systems.britnet = {
|
||||
system = "x86_64-linux";
|
||||
nixpkgs = "mine";
|
||||
|
||||
assignments = {
|
||||
allhost = {
|
||||
inherit domain;
|
||||
ipv4 = {
|
||||
address = pubV4;
|
||||
mask = 24;
|
||||
gateway = "77.74.199.1";
|
||||
};
|
||||
ipv6 = {
|
||||
address = "2a12:ab46:5344:99::a";
|
||||
gateway = "2a12:ab46:5344::1";
|
||||
};
|
||||
};
|
||||
vpn = {
|
||||
ipv4 = {
|
||||
address = net.cidr.host 1 prefixes.vpn.v4;
|
||||
gateway = null;
|
||||
};
|
||||
ipv6.address = net.cidr.host 1 prefixes.vpn.v6;
|
||||
};
|
||||
};
|
||||
|
||||
configuration = { lib, pkgs, modulesPath, config, assignments, allAssignments, ... }:
|
||||
let
|
||||
inherit (lib) mkMerge mkForce;
|
||||
inherit (lib.my) networkdAssignment;
|
||||
in
|
||||
{
|
||||
imports = [
|
||||
"${modulesPath}/profiles/qemu-guest.nix"
|
||||
];
|
||||
|
||||
config = mkMerge [
|
||||
{
|
||||
boot = {
|
||||
initrd.availableKernelModules = [
|
||||
"ata_piix" "uhci_hcd" "virtio_pci" "virtio_scsi" "ahci" "sr_mod" "virtio_blk"
|
||||
];
|
||||
loader = {
|
||||
systemd-boot.enable = false;
|
||||
grub = {
|
||||
enable = true;
|
||||
device = "/dev/vda";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
fileSystems = {
|
||||
"/boot" = {
|
||||
device = "/dev/disk/by-uuid/457444a1-81dd-4934-960c-650ad16c92b5";
|
||||
fsType = "ext4";
|
||||
};
|
||||
"/nix" = {
|
||||
device = "/dev/disk/by-uuid/992c0c79-5be6-45b6-bc30-dc82e3ec082a";
|
||||
fsType = "ext4";
|
||||
};
|
||||
"/persist" = {
|
||||
device = "/dev/disk/by-uuid/f020a955-54d5-4098-98ba-d3615781d96a";
|
||||
fsType = "ext4";
|
||||
neededForBoot = true;
|
||||
};
|
||||
};
|
||||
|
||||
environment = {
|
||||
systemPackages = with pkgs; [
|
||||
wireguard-tools
|
||||
];
|
||||
};
|
||||
|
||||
services = {
|
||||
iperf3 = {
|
||||
enable = true;
|
||||
openFirewall = true;
|
||||
};
|
||||
|
||||
tailscale = {
|
||||
enable = true;
|
||||
authKeyFile = config.age.secrets."tailscale-auth.key".path;
|
||||
openFirewall = true;
|
||||
interfaceName = "tailscale0";
|
||||
extraUpFlags = [
|
||||
"--operator=${config.my.user.config.name}"
|
||||
"--login-server=https://hs.nul.ie"
|
||||
"--netfilter-mode=off"
|
||||
"--advertise-exit-node"
|
||||
"--accept-routes=false"
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
networking = { inherit domain; };
|
||||
|
||||
systemd.network = {
|
||||
netdevs = {
|
||||
"30-wg0" = {
|
||||
netdevConfig = {
|
||||
Name = "wg0";
|
||||
Kind = "wireguard";
|
||||
};
|
||||
wireguardConfig = {
|
||||
PrivateKeyFile = config.age.secrets."britnet/wg.key".path;
|
||||
ListenPort = lib.my.c.britnet.vpn.port;
|
||||
};
|
||||
wireguardPeers = [
|
||||
{
|
||||
PublicKey = "EfPwREfZ/q3ogHXBIqFZh4k/1NRJRyq4gBkBXtegNkE=";
|
||||
AllowedIPs = [
|
||||
(net.cidr.host 10 prefixes.vpn.v4)
|
||||
(net.cidr.host 10 prefixes.vpn.v6)
|
||||
];
|
||||
}
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
links = {
|
||||
"10-veth0" = {
|
||||
matchConfig.PermanentMACAddress = "00:db:d9:62:68:1a";
|
||||
linkConfig.Name = "veth0";
|
||||
};
|
||||
};
|
||||
|
||||
networks = {
|
||||
"20-veth0" = mkMerge [
|
||||
(networkdAssignment "veth0" assignments.allhost)
|
||||
{
|
||||
dns = [ "1.1.1.1" "1.0.0.1" ];
|
||||
routes = [
|
||||
{
|
||||
# Gateway is on a different network for some reason...
|
||||
Destination = "2a12:ab46:5344::1";
|
||||
Scope = "link";
|
||||
}
|
||||
];
|
||||
}
|
||||
];
|
||||
"30-wg0" = mkMerge [
|
||||
(networkdAssignment "wg0" assignments.vpn)
|
||||
{
|
||||
networkConfig.IPv6AcceptRA = mkForce false;
|
||||
}
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
my = {
|
||||
server.enable = true;
|
||||
secrets = {
|
||||
key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJIEx+1EC/lN6WKIaOB+O5LJgVHRK962YpZEPQg/m78O";
|
||||
files = {
|
||||
"tailscale-auth.key" = {};
|
||||
"britnet/wg.key" = {
|
||||
owner = "systemd-network";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
firewall = {
|
||||
udp.allowed = [ lib.my.c.britnet.vpn.port ];
|
||||
trustedInterfaces = [ "tailscale0" ];
|
||||
extraRules = ''
|
||||
table inet filter {
|
||||
chain forward {
|
||||
iifname wg0 oifname veth0 accept
|
||||
}
|
||||
}
|
||||
table inet nat {
|
||||
chain postrouting {
|
||||
iifname { tailscale0, wg0 } oifname veth0 snat ip to ${assignments.allhost.ipv4.address}
|
||||
iifname { tailscale0, wg0 } oifname veth0 snat ip6 to ${assignments.allhost.ipv6.address}
|
||||
}
|
||||
}
|
||||
'';
|
||||
};
|
||||
};
|
||||
}
|
||||
];
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -11,24 +11,23 @@ in
|
||||
config = {
|
||||
my = {
|
||||
secrets.files."britway/bgp-password-vultr.conf" = {
|
||||
owner = "bird";
|
||||
group = "bird";
|
||||
owner = "bird2";
|
||||
group = "bird2";
|
||||
};
|
||||
};
|
||||
|
||||
environment.etc."bird/vultr-password.conf".source = config.age.secrets."britway/bgp-password-vultr.conf".path;
|
||||
|
||||
systemd = {
|
||||
services.bird.after = [ "systemd-networkd-wait-online@veth0.service" ];
|
||||
services.bird2.after = [ "systemd-networkd-wait-online@veth0.service" ];
|
||||
network = {
|
||||
config.networkConfig.ManageForeignRoutes = false;
|
||||
};
|
||||
};
|
||||
|
||||
services = {
|
||||
bird = {
|
||||
bird2 = {
|
||||
enable = true;
|
||||
package = pkgs.bird2;
|
||||
preCheckConfig = ''
|
||||
echo '"dummy"' > vultr-password.conf
|
||||
'';
|
||||
|
||||
@@ -106,7 +106,7 @@ in
|
||||
{
|
||||
matchConfig.Name = "as211024";
|
||||
networkConfig.IPv6AcceptRA = mkForce false;
|
||||
routes = [
|
||||
routes = map (r: { routeConfig = r; }) [
|
||||
{
|
||||
Destination = lib.my.c.colony.prefixes.all.v4;
|
||||
Gateway = allAssignments.estuary.as211024.ipv4.address;
|
||||
@@ -123,7 +123,7 @@ in
|
||||
Table = "ts-extra";
|
||||
}
|
||||
];
|
||||
routingPolicyRules = [
|
||||
routingPolicyRules = map (r: { routingPolicyRuleConfig = r; }) [
|
||||
{
|
||||
IncomingInterface = "tailscale0";
|
||||
To = lib.my.c.colony.prefixes.all.v6;
|
||||
|
||||
@@ -9,6 +9,11 @@ in
|
||||
config = {
|
||||
my = {
|
||||
secrets.files = {
|
||||
"dhparams.pem" = {
|
||||
owner = "acme";
|
||||
group = "acme";
|
||||
mode = "440";
|
||||
};
|
||||
"britway/cloudflare-credentials.conf" = {
|
||||
owner = "acme";
|
||||
group = "acme";
|
||||
@@ -40,7 +45,7 @@ in
|
||||
"*.${pubDomain}"
|
||||
];
|
||||
dnsProvider = "cloudflare";
|
||||
environmentFile = config.age.secrets."britway/cloudflare-credentials.conf".path;
|
||||
credentialsFile = config.age.secrets."britway/cloudflare-credentials.conf".path;
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -53,6 +58,7 @@ in
|
||||
logError = "stderr info";
|
||||
recommendedTlsSettings = true;
|
||||
serverTokens = true;
|
||||
sslDhparam = config.age.secrets."dhparams.pem".path;
|
||||
|
||||
# Based on recommended*Settings, but probably better to be explicit about these
|
||||
appendHttpConfig = ''
|
||||
@@ -74,7 +80,7 @@ in
|
||||
};
|
||||
};
|
||||
|
||||
"hs.${pubDomain}" = {
|
||||
"ts.${pubDomain}" = {
|
||||
locations."/" = {
|
||||
proxyPass = "http://localhost:${toString config.services.headscale.port}";
|
||||
proxyWebsockets = true;
|
||||
|
||||
@@ -4,6 +4,20 @@ let
|
||||
inherit (lib.my.c) pubDomain;
|
||||
inherit (lib.my.c.britway) prefixes domain;
|
||||
|
||||
# Can't use overrideAttrs because we need to override `vendorHash` within `buildGoModule`
|
||||
headscale = pkgs.headscale.override {
|
||||
buildGoModule = args: pkgs.buildGoModule (args // rec {
|
||||
version = "0.23.0-alpha2";
|
||||
src = pkgs.fetchFromGitHub {
|
||||
owner = "juanfont";
|
||||
repo = "headscale";
|
||||
rev = "v${version}";
|
||||
hash = "sha256-sz+uQyyq/5YYDe5I44x5x2nvd48swAhNlInB8KZYvDo=";
|
||||
};
|
||||
vendorHash = "sha256-u9AmJguQ5dnJpfhOeLN43apvMHuraOrJhvlEIp9RoIc=";
|
||||
});
|
||||
};
|
||||
|
||||
advRoutes = concatStringsSep "," [
|
||||
lib.my.c.home.prefixes.all.v4
|
||||
lib.my.c.home.prefixes.all.v6
|
||||
@@ -25,21 +39,19 @@ in
|
||||
services = {
|
||||
headscale = {
|
||||
enable = true;
|
||||
package = headscale;
|
||||
settings = {
|
||||
disable_check_updates = true;
|
||||
unix_socket_permission = "0770";
|
||||
server_url = "https://hs.${pubDomain}";
|
||||
database = {
|
||||
type = "sqlite3";
|
||||
sqlite.path = "/var/lib/headscale/db.sqlite3";
|
||||
};
|
||||
server_url = "https://ts.${pubDomain}";
|
||||
db_type = "sqlite3";
|
||||
db_path = "/var/lib/headscale/db.sqlite3";
|
||||
noise.private_key_path = "/var/lib/headscale/noise_private.key";
|
||||
prefixes = with lib.my.c.tailscale.prefix; { inherit v4 v6; };
|
||||
dns = {
|
||||
override_local_dns = false;
|
||||
ip_prefixes = with lib.my.c.tailscale.prefix; [ v4 v6 ];
|
||||
dns_config = {
|
||||
# Use IPs that will route inside the VPN to prevent interception
|
||||
# (e.g. DNS rebinding filtering)
|
||||
nameservers.split = {
|
||||
restricted_nameservers = {
|
||||
"${domain}" = pubNameservers;
|
||||
"${lib.my.c.colony.domain}" = with allAssignments.estuary.base; [
|
||||
ipv4.address ipv6.address
|
||||
@@ -53,6 +65,7 @@ in
|
||||
};
|
||||
magic_dns = true;
|
||||
base_domain = "ts.${pubDomain}";
|
||||
override_local_dns = false;
|
||||
};
|
||||
oidc = {
|
||||
only_start_if_oidc_is_available = true;
|
||||
@@ -72,7 +85,7 @@ in
|
||||
interfaceName = "tailscale0";
|
||||
extraUpFlags = [
|
||||
"--operator=${config.my.user.config.name}"
|
||||
"--login-server=https://hs.nul.ie"
|
||||
"--login-server=https://ts.nul.ie"
|
||||
"--netfilter-mode=off"
|
||||
"--advertise-exit-node"
|
||||
"--advertise-routes=${advRoutes}"
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
{ lib, ... }:
|
||||
let
|
||||
inherit (lib.my) net;
|
||||
inherit (lib.my.c) networkd;
|
||||
inherit (lib.my.c.home) domain vlans prefixes vips roceBootModules;
|
||||
inherit (lib.my.c.home) domain vlans prefixes;
|
||||
in
|
||||
{
|
||||
nixos.systems.castle = {
|
||||
@@ -16,7 +15,7 @@ in
|
||||
ipv4 = {
|
||||
address = net.cidr.host 40 prefixes.hi.v4;
|
||||
mask = 22;
|
||||
gateway = vips.hi.v4;
|
||||
gateway = null;
|
||||
};
|
||||
ipv6 = {
|
||||
iid = "::3:1";
|
||||
@@ -36,7 +35,7 @@ in
|
||||
cpu = {
|
||||
amd.updateMicrocode = true;
|
||||
};
|
||||
graphics.extraPackages = with pkgs; [
|
||||
opengl.extraPackages = with pkgs; [
|
||||
intel-media-driver
|
||||
];
|
||||
bluetooth.enable = true;
|
||||
@@ -48,7 +47,7 @@ in
|
||||
timeout = 10;
|
||||
};
|
||||
kernelPackages = lib.my.c.kernel.latest pkgs;
|
||||
kernelModules = [ "kvm-amd" "dm-snapshot" ];
|
||||
kernelModules = [ "kvm-amd" ];
|
||||
kernelParams = [ "amd_iommu=on" "amd_pstate=passive" ];
|
||||
kernelPatches = [
|
||||
# {
|
||||
@@ -58,40 +57,27 @@ in
|
||||
# }
|
||||
];
|
||||
initrd = {
|
||||
availableKernelModules = [
|
||||
"thunderbolt" "xhci_pci" "nvme" "ahci" "usbhid" "usb_storage" "sd_mod"
|
||||
"8021q"
|
||||
] ++ roceBootModules;
|
||||
systemd.network = {
|
||||
netdevs = mkVLAN "lan-hi" vlans.hi;
|
||||
networks = {
|
||||
"10-et100g" = {
|
||||
matchConfig.Name = "et100g";
|
||||
vlan = [ "lan-hi" ];
|
||||
linkConfig.RequiredForOnline = "no";
|
||||
networkConfig = networkd.noL3;
|
||||
};
|
||||
"20-lan-hi" = networkdAssignment "lan-hi" assignments.hi;
|
||||
};
|
||||
};
|
||||
availableKernelModules = [ "thunderbolt" "xhci_pci" "nvme" "ahci" "usbhid" "usb_storage" "sd_mod" ];
|
||||
};
|
||||
|
||||
binfmt.emulatedSystems = [ "aarch64-linux" "armv7l-linux" ];
|
||||
};
|
||||
|
||||
fileSystems = {
|
||||
"/boot" = {
|
||||
device = "/dev/disk/by-partuuid/8ce4248a-3ee4-f44f-801f-064a628b4d6e";
|
||||
fsType = "vfat";
|
||||
};
|
||||
"/nix" = {
|
||||
device = "/dev/nvmeof/nix";
|
||||
device = "/dev/disk/by-partuuid/2da23a1d-2daf-d943-b91e-fc175f3dad07";
|
||||
fsType = "ext4";
|
||||
};
|
||||
|
||||
"/persist" = {
|
||||
device = "/dev/nvmeof/persist";
|
||||
device = "/dev/disk/by-partuuid/f4c80d4f-a022-e941-b5d1-fe2e65e444b9";
|
||||
fsType = "ext4";
|
||||
neededForBoot = true;
|
||||
};
|
||||
|
||||
"/home" = {
|
||||
device = "/dev/nvmeof/home";
|
||||
device = "/dev/disk/by-partuuid/992a93cf-6c9c-324b-b0ce-f8eb2d1ce10d";
|
||||
fsType = "ext4";
|
||||
};
|
||||
};
|
||||
@@ -109,29 +95,26 @@ in
|
||||
};
|
||||
fstrim.enable = true;
|
||||
|
||||
resolved.settings.Resolve.LLMNR = mkForce true;
|
||||
|
||||
pipewire.extraConfig.pipewire = {
|
||||
"10-buffer"."context.properties" = {
|
||||
"default.clock.quantum" = 128;
|
||||
"default.clock.max-quantum" = 128;
|
||||
};
|
||||
resolved = {
|
||||
enable = true;
|
||||
extraConfig = mkForce "";
|
||||
dnssec = "false";
|
||||
};
|
||||
|
||||
blueman.enable = true;
|
||||
avahi.enable = true;
|
||||
};
|
||||
|
||||
programs = {
|
||||
virt-manager.enable = true;
|
||||
wireshark = {
|
||||
enable = true;
|
||||
package = pkgs.wireshark;
|
||||
package = pkgs.wireshark-qt;
|
||||
};
|
||||
};
|
||||
virtualisation.libvirtd.enable = true;
|
||||
|
||||
networking = {
|
||||
inherit domain;
|
||||
domain = "h.${lib.my.c.pubDomain}";
|
||||
firewall.enable = false;
|
||||
};
|
||||
|
||||
@@ -147,22 +130,25 @@ in
|
||||
mstflint
|
||||
qperf
|
||||
ethtool
|
||||
android-tools
|
||||
];
|
||||
environment.etc = {
|
||||
"pipewire/pipewire.conf.d/sample-size.conf".text = ''
|
||||
context.properties = {
|
||||
default.clock.quantum = 128
|
||||
default.clock.max-quantum = 128
|
||||
}
|
||||
'';
|
||||
};
|
||||
|
||||
nix = {
|
||||
gc.automatic = false;
|
||||
settings = {
|
||||
experimental-features = [ "recursive-nix" ];
|
||||
system-features = [ "nixos-test" "benchmark" "big-parallel" "kvm" "recursive-nix" ];
|
||||
};
|
||||
};
|
||||
|
||||
systemd = {
|
||||
network = {
|
||||
wait-online.enable = false;
|
||||
netdevs = mkMerge [
|
||||
(mkVLAN "lan-hi" vlans.hi)
|
||||
(mkVLAN "lan-lo" vlans.lo)
|
||||
];
|
||||
links = {
|
||||
"10-et2.5g" = {
|
||||
@@ -177,37 +163,30 @@ in
|
||||
matchConfig.PermanentMACAddress = "24:8a:07:a8:fe:3a";
|
||||
linkConfig = {
|
||||
Name = "et100g";
|
||||
MTUBytes = toString lib.my.c.home.hiMTU;
|
||||
MTUBytes = "9000";
|
||||
};
|
||||
};
|
||||
};
|
||||
networks = {
|
||||
"30-et100g" = {
|
||||
"50-lan" = {
|
||||
matchConfig.Name = "et2.5g";
|
||||
DHCP = "no";
|
||||
address = [ "10.16.7.1/16" ];
|
||||
};
|
||||
|
||||
"50-et100g" = {
|
||||
matchConfig.Name = "et100g";
|
||||
vlan = [ "lan-hi" "lan-lo" ];
|
||||
vlan = [ "lan-hi" ];
|
||||
networkConfig.IPv6AcceptRA = false;
|
||||
};
|
||||
"40-lan-hi" = mkMerge [
|
||||
"60-lan-hi" = mkMerge [
|
||||
(networkdAssignment "lan-hi" assignments.hi)
|
||||
# So we don't drop the IP we use to connect to NVMe-oF!
|
||||
{ networkConfig.KeepConfiguration = "static"; }
|
||||
{
|
||||
DHCP = "yes";
|
||||
matchConfig.Name = "lan-hi";
|
||||
linkConfig.MTUBytes = "9000";
|
||||
}
|
||||
];
|
||||
"45-lan-lo" = {
|
||||
matchConfig.Name = "lan-lo";
|
||||
networkConfig = {
|
||||
DHCP = "ipv4";
|
||||
IPv6AcceptRA = true;
|
||||
UseDomains = false;
|
||||
};
|
||||
dhcpV4Config = {
|
||||
UseDNS = false;
|
||||
UseGateway = false;
|
||||
};
|
||||
ipv6AcceptRAConfig = {
|
||||
UseDNS = false;
|
||||
UseGateway = false;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -226,19 +205,15 @@ in
|
||||
packages = with pkgs; [
|
||||
jacktrip
|
||||
qpwgraph
|
||||
boardie
|
||||
# TODO: seems to be borked (infinite recursion???)
|
||||
# (writeShellScriptBin "boardie" ''
|
||||
# exec pw-jack ${boardie}/bin/boardie "$@"
|
||||
# '')
|
||||
];
|
||||
};
|
||||
|
||||
programs = {
|
||||
claude-code = {
|
||||
enable = true;
|
||||
};
|
||||
};
|
||||
|
||||
services = {
|
||||
blueman-applet.enable = true;
|
||||
easyeffects.enable = true;
|
||||
};
|
||||
|
||||
wayland.windowManager.sway = {
|
||||
@@ -247,7 +222,6 @@ in
|
||||
HDMI-A-1 = {
|
||||
transform = "270";
|
||||
position = "0 0";
|
||||
bg = "${./his-team-player.jpg} fill";
|
||||
};
|
||||
DP-1 = {
|
||||
mode = "2560x1440@170Hz";
|
||||
@@ -269,19 +243,11 @@ in
|
||||
};
|
||||
|
||||
#deploy.generate.system.mode = "boot";
|
||||
deploy.node.hostname = "castle.box.${config.networking.domain}";
|
||||
secrets = {
|
||||
key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMlVuTzKObeaUuPocCF41IO/8X+443lzUJLuCIclt2vr";
|
||||
};
|
||||
netboot.client = {
|
||||
enable = true;
|
||||
};
|
||||
nvme = {
|
||||
uuid = "2230b066-a674-4f45-a1dc-f7727b3a9e7b";
|
||||
boot = {
|
||||
nqn = "nqn.2016-06.io.spdk:castle";
|
||||
address = "192.168.68.80";
|
||||
};
|
||||
};
|
||||
nvme.uuid = "2230b066-a674-4f45-a1dc-f7727b3a9e7b";
|
||||
|
||||
firewall = {
|
||||
enable = false;
|
||||
@@ -1,7 +1,7 @@
|
||||
{ lib, ... }:
|
||||
let
|
||||
inherit (lib.my) net;
|
||||
inherit (lib.my.c.colony) domain prefixes custRouting firewallForwards;
|
||||
inherit (lib.my.c.colony) domain prefixes firewallForwards;
|
||||
in
|
||||
{
|
||||
imports = [ ./vms ];
|
||||
@@ -60,8 +60,8 @@ in
|
||||
kernelPackages = (lib.my.c.kernel.lts pkgs).extend (self: super: {
|
||||
kernel = super.kernel.override {
|
||||
structuredExtraConfig = with lib.kernel; {
|
||||
ACPI_APEI_PCIEAER = yes;
|
||||
PCIEAER = yes;
|
||||
#SOME_OPT = yes;
|
||||
#A_MOD = module;
|
||||
};
|
||||
};
|
||||
});
|
||||
@@ -150,12 +150,12 @@ in
|
||||
"serial-getty@ttyS1".enable = true;
|
||||
lvm-activate-main = {
|
||||
description = "Activate remaining LVs";
|
||||
unitConfig.DefaultDependencies = false;
|
||||
before = [ "local-fs-pre.target" ];
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
ExecStart = "${pkgs.lvm2.bin}/bin/vgchange -aay main";
|
||||
};
|
||||
wantedBy = [ "local-fs-pre.target" ];
|
||||
wantedBy = [ "sysinit.target" ];
|
||||
};
|
||||
|
||||
rsync-lvm-meta = {
|
||||
@@ -252,10 +252,10 @@ in
|
||||
};
|
||||
ipv6Prefixes = [
|
||||
{
|
||||
Prefix = prefixes.vms.v6;
|
||||
ipv6PrefixConfig.Prefix = prefixes.vms.v6;
|
||||
}
|
||||
];
|
||||
routes = [
|
||||
routes = map (r: { routeConfig = r; }) [
|
||||
{
|
||||
Destination = prefixes.ctrs.v4;
|
||||
Gateway = allAssignments.shill.routing.ipv4.address;
|
||||
@@ -264,12 +264,10 @@ in
|
||||
Destination = prefixes.ctrs.v6;
|
||||
Gateway = allAssignments.shill.internal.ipv6.address;
|
||||
}
|
||||
|
||||
{
|
||||
Destination = allAssignments.shill.internal.ipv4.address;
|
||||
Gateway = allAssignments.shill.routing.ipv4.address;
|
||||
}
|
||||
|
||||
{
|
||||
Destination = lib.my.c.tailscale.prefix.v4;
|
||||
Gateway = allAssignments.shill.routing.ipv4.address;
|
||||
@@ -278,15 +276,6 @@ in
|
||||
Destination = lib.my.c.tailscale.prefix.v6;
|
||||
Gateway = allAssignments.shill.internal.ipv6.address;
|
||||
}
|
||||
{
|
||||
Destination = prefixes.qclk.v4;
|
||||
Gateway = allAssignments.shill.routing.ipv4.address;
|
||||
}
|
||||
|
||||
{
|
||||
Destination = prefixes.jam.v6;
|
||||
Gateway = allAssignments.shill.internal.ipv6.address;
|
||||
}
|
||||
|
||||
{
|
||||
Destination = prefixes.oci.v4;
|
||||
@@ -318,7 +307,7 @@ in
|
||||
"90-vm-mail" = {
|
||||
matchConfig.Name = "vm-mail";
|
||||
address = [
|
||||
"${custRouting.mail-vm}/32"
|
||||
(net.cidr.subnet 8 1 prefixes.cust.v4)
|
||||
prefixes.mail.v6
|
||||
];
|
||||
networkConfig = {
|
||||
@@ -327,10 +316,10 @@ in
|
||||
};
|
||||
ipv6Prefixes = [
|
||||
{
|
||||
Prefix = prefixes.mail.v6;
|
||||
ipv6PrefixConfig.Prefix = prefixes.mail.v6;
|
||||
}
|
||||
];
|
||||
routes = [
|
||||
routes = map (r: { routeConfig = r; }) [
|
||||
{
|
||||
Destination = prefixes.mail.v4;
|
||||
Scope = "link";
|
||||
@@ -341,7 +330,7 @@ in
|
||||
"90-vm-darts" = {
|
||||
matchConfig.Name = "vm-darts";
|
||||
address = [
|
||||
"${custRouting.darts-vm}/32"
|
||||
(net.cidr.subnet 8 2 prefixes.cust.v4)
|
||||
prefixes.darts.v6
|
||||
];
|
||||
networkConfig = {
|
||||
@@ -350,10 +339,10 @@ in
|
||||
};
|
||||
ipv6Prefixes = [
|
||||
{
|
||||
Prefix = prefixes.darts.v6;
|
||||
ipv6PrefixConfig.Prefix = prefixes.darts.v6;
|
||||
}
|
||||
];
|
||||
routes = [
|
||||
routes = map (r: { routeConfig = r; }) [
|
||||
{
|
||||
Destination = prefixes.darts.v4;
|
||||
Scope = "link";
|
||||
|
||||
@@ -29,9 +29,6 @@
|
||||
};
|
||||
in
|
||||
{
|
||||
# Kernel Same-Page Merging to attempt memory usage reduction
|
||||
hardware.ksm.enable = false;
|
||||
|
||||
systemd = {
|
||||
network = {
|
||||
links = {
|
||||
@@ -133,8 +130,7 @@
|
||||
|
||||
(vm.lvmDisk "media")
|
||||
(vm.lvmDisk "minio")
|
||||
(vm.lvmDisk "nix-cache")
|
||||
(vm.lvmDisk "jam")
|
||||
(vm.lvmDisk "nix-atticd")
|
||||
]);
|
||||
};
|
||||
|
||||
@@ -213,7 +209,6 @@
|
||||
drives = [
|
||||
(mkMerge [ (vm.disk "darts" "root") { frontendOpts.bootindex = 0; } ])
|
||||
(vm.lvmDisk' "media" "darts-media")
|
||||
(vm.lvmDisk' "ext" "darts-ext")
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
@@ -8,9 +8,8 @@ in
|
||||
{
|
||||
config = {
|
||||
services = {
|
||||
bird = {
|
||||
bird2 = {
|
||||
enable = true;
|
||||
package = pkgs.bird2;
|
||||
# TODO: Clean up and modularise
|
||||
config = ''
|
||||
define OWNAS = 211024;
|
||||
@@ -233,88 +232,59 @@ in
|
||||
neighbor 2a07:cd40:1::9 as 202413;
|
||||
}
|
||||
|
||||
protocol bgp ixp4_frysix_rs3 from ixp_bgp4 {
|
||||
description "Frys-IX route server 3 (IPv4)";
|
||||
neighbor 185.1.160.255 as 56393;
|
||||
protocol bgp ixp4_frysix_rs1 from ixp_bgp4 {
|
||||
description "Frys-IX route server 1 (IPv4)";
|
||||
neighbor 185.1.203.253 as 56393;
|
||||
}
|
||||
protocol bgp ixp6_frysix_rs3 from ixp_bgp6 {
|
||||
description "Frys-IX route server 3 (IPv6)";
|
||||
neighbor 2001:7f8:10f::dc49:1 as 56393;
|
||||
protocol bgp ixp6_frysix_rs1 from ixp_bgp6 {
|
||||
description "Frys-IX route server 1 (IPv6)";
|
||||
neighbor 2001:7f8:10f::dc49:253 as 56393;
|
||||
}
|
||||
|
||||
protocol bgp ixp4_frysix_rs4 from ixp_bgp4 {
|
||||
description "Frys-IX route server 4 (IPv4)";
|
||||
neighbor 185.1.161.0 as 56393;
|
||||
protocol bgp ixp4_frysix_rs2 from ixp_bgp4 {
|
||||
description "Frys-IX route server 2 (IPv4)";
|
||||
neighbor 185.1.203.254 as 56393;
|
||||
}
|
||||
protocol bgp ixp6_frysix_rs4 from ixp_bgp6 {
|
||||
description "Frys-IX route server 4 (IPv6)";
|
||||
neighbor 2001:7f8:10f::dc49:2 as 56393;
|
||||
protocol bgp ixp6_frysix_rs2 from ixp_bgp6 {
|
||||
description "Frys-IX route server 2 (IPv6)";
|
||||
neighbor 2001:7f8:10f::dc49:254 as 56393;
|
||||
}
|
||||
|
||||
protocol bgp peer4_frysix_luje from peer_bgp4 {
|
||||
description "LUJE.net (on Frys-IX, IPv4)";
|
||||
neighbor 185.1.160.152 as 212855;
|
||||
neighbor 185.1.203.152 as 212855;
|
||||
}
|
||||
protocol bgp peer6_frysix_luje from peer_bgp6 {
|
||||
description "LUJE.net (on Frys-IX, IPv6)";
|
||||
neighbor 2001:7f8:10f::3:3f95:152 as 212855;
|
||||
}
|
||||
|
||||
protocol bgp peer4_frysix_he from peer_bgp4 {
|
||||
description "Hurricane Electric (on Frys-IX, IPv4)";
|
||||
neighbor 185.1.160.154 as 6939;
|
||||
neighbor 185.1.203.154 as 6939;
|
||||
}
|
||||
|
||||
protocol bgp peer4_frysix_cloudflare1 from peer_bgp4 {
|
||||
description "Cloudflare 1 (on Frys-IX, IPv4)";
|
||||
neighbor 185.1.160.217 as 13335;
|
||||
protocol bgp peer4_frysix_cloudflare from peer_bgp4 {
|
||||
description "Cloudflare (on Frys-IX, IPv4)";
|
||||
neighbor 185.1.203.217 as 13335;
|
||||
}
|
||||
protocol bgp peer4_frysix_cloudflare2 from peer_bgp4 {
|
||||
description "Cloudflare 2 (on Frys-IX, IPv4)";
|
||||
neighbor 185.1.160.109 as 13335;
|
||||
}
|
||||
protocol bgp peer6_frysix_cloudflare1 from peer_bgp6 {
|
||||
description "Cloudflare 1 (on Frys-IX, IPv6)";
|
||||
protocol bgp peer6_frysix_cloudflare from peer_bgp6 {
|
||||
description "Cloudflare (on Frys-IX, IPv6)";
|
||||
neighbor 2001:7f8:10f::3417:217 as 13335;
|
||||
}
|
||||
protocol bgp peer6_frysix_cloudflare2 from peer_bgp6 {
|
||||
description "Cloudflare 2 (on Frys-IX, IPv6)";
|
||||
neighbor 2001:7f8:10f::3417:109 as 13335;
|
||||
}
|
||||
|
||||
protocol bgp peer4_frysix_jurrian from peer_bgp4 {
|
||||
description "AS212635 aka jurrian (on Frys-IX, IPv4)";
|
||||
neighbor 185.1.160.134 as 212635;
|
||||
neighbor 185.1.203.134 as 212635;
|
||||
}
|
||||
protocol bgp peer6_frysix_jurrian from peer_bgp6 {
|
||||
description "AS212635 aka jurrian (on Frys-IX, IPv6)";
|
||||
neighbor 2001:7f8:10f::3:3e9b:134 as 212635;
|
||||
}
|
||||
|
||||
protocol bgp peer4_nlix_meta1 from peer_bgp4 {
|
||||
description "Meta 1 (on NL-ix, IPv4)";
|
||||
neighbor 193.239.116.147 as 32934;
|
||||
}
|
||||
protocol bgp peer4_nlix_meta2 from peer_bgp4 {
|
||||
description "Meta 2 (on NL-ix, IPv4)";
|
||||
neighbor 193.239.116.148 as 32934;
|
||||
}
|
||||
protocol bgp peer6_nlix_meta1 from peer_bgp6 {
|
||||
description "Meta 1 (on NL-ix, IPv6)";
|
||||
neighbor 2001:7f8:13::a503:2934:1 as 32934;
|
||||
}
|
||||
protocol bgp peer6_nlix_meta2 from peer_bgp6 {
|
||||
description "Meta 2 (on NL-ix, IPv6)";
|
||||
neighbor 2001:7f8:13::a503:2934:2 as 32934;
|
||||
}
|
||||
|
||||
protocol bgp peer4_frysix_meta1 from peer_bgp4 {
|
||||
description "Meta 1 (on Frys-IX, IPv4)";
|
||||
neighbor 185.1.160.225 as 32934;
|
||||
neighbor 185.1.203.225 as 32934;
|
||||
}
|
||||
protocol bgp peer4_frysix_meta2 from peer_bgp4 {
|
||||
description "Meta 2 (on Frys-IX, IPv4)";
|
||||
neighbor 185.1.160.226 as 32934;
|
||||
neighbor 185.1.203.226 as 32934;
|
||||
}
|
||||
protocol bgp peer6_frysix_meta1 from peer_bgp6 {
|
||||
description "Meta 1 (on Frys-IX, IPv6)";
|
||||
@@ -347,36 +317,36 @@ in
|
||||
ipv6 { preference (PREFIXP-1); };
|
||||
}
|
||||
|
||||
# protocol bgp peer4_nlix_cloudflare1 from peer_bgp4 {
|
||||
# description "Cloudflare NL-ix 1 (IPv4)";
|
||||
# neighbor 193.239.117.14 as 13335;
|
||||
# ipv4 { preference (PREFPEER-1); };
|
||||
# }
|
||||
# protocol bgp peer4_nlix_cloudflare2 from peer_bgp4 {
|
||||
# description "Cloudflare NL-ix 2 (IPv4)";
|
||||
# neighbor 193.239.117.114 as 13335;
|
||||
# ipv4 { preference (PREFPEER-1); };
|
||||
# }
|
||||
# protocol bgp peer4_nlix_cloudflare3 from peer_bgp4 {
|
||||
# description "Cloudflare NL-ix 3 (IPv4)";
|
||||
# neighbor 193.239.118.138 as 13335;
|
||||
# ipv4 { preference (PREFPEER-1); };
|
||||
# }
|
||||
# protocol bgp peer6_nlix_cloudflare1 from peer_bgp6 {
|
||||
# description "Cloudflare NL-ix 1 (IPv6)";
|
||||
# neighbor 2001:7f8:13::a501:3335:1 as 13335;
|
||||
# ipv6 { preference (PREFPEER-1); };
|
||||
# }
|
||||
# protocol bgp peer6_nlix_cloudflare2 from peer_bgp6 {
|
||||
# description "Cloudflare NL-ix 2 (IPv6)";
|
||||
# neighbor 2001:7f8:13::a501:3335:2 as 13335;
|
||||
# ipv6 { preference (PREFPEER-1); };
|
||||
# }
|
||||
# protocol bgp peer6_nlix_cloudflare3 from peer_bgp6 {
|
||||
# description "Cloudflare NL-ix 3 (IPv6)";
|
||||
# neighbor 2001:7f8:13::a501:3335:3 as 13335;
|
||||
# ipv6 { preference (PREFPEER-1); };
|
||||
# }
|
||||
protocol bgp peer4_nlix_cloudflare1 from peer_bgp4 {
|
||||
description "Cloudflare NL-ix 1 (IPv4)";
|
||||
neighbor 193.239.117.14 as 13335;
|
||||
ipv4 { preference (PREFPEER-1); };
|
||||
}
|
||||
protocol bgp peer4_nlix_cloudflare2 from peer_bgp4 {
|
||||
description "Cloudflare NL-ix 2 (IPv4)";
|
||||
neighbor 193.239.117.114 as 13335;
|
||||
ipv4 { preference (PREFPEER-1); };
|
||||
}
|
||||
protocol bgp peer4_nlix_cloudflare3 from peer_bgp4 {
|
||||
description "Cloudflare NL-ix 3 (IPv4)";
|
||||
neighbor 193.239.118.138 as 13335;
|
||||
ipv4 { preference (PREFPEER-1); };
|
||||
}
|
||||
protocol bgp peer6_nlix_cloudflare1 from peer_bgp6 {
|
||||
description "Cloudflare NL-ix 1 (IPv6)";
|
||||
neighbor 2001:7f8:13::a501:3335:1 as 13335;
|
||||
ipv6 { preference (PREFPEER-1); };
|
||||
}
|
||||
protocol bgp peer6_nlix_cloudflare2 from peer_bgp6 {
|
||||
description "Cloudflare NL-ix 2 (IPv6)";
|
||||
neighbor 2001:7f8:13::a501:3335:2 as 13335;
|
||||
ipv6 { preference (PREFPEER-1); };
|
||||
}
|
||||
protocol bgp peer6_nlix_cloudflare3 from peer_bgp6 {
|
||||
description "Cloudflare NL-ix 3 (IPv6)";
|
||||
neighbor 2001:7f8:13::a501:3335:3 as 13335;
|
||||
ipv6 { preference (PREFPEER-1); };
|
||||
}
|
||||
protocol bgp peer4_nlix_jurrian from peer_bgp4 {
|
||||
description "AS212635 aka jurrian (on NL-ix, IPv4)";
|
||||
neighbor 193.239.117.55 as 212635;
|
||||
|
||||
@@ -9,7 +9,6 @@ in
|
||||
vpns = {
|
||||
l2 = {
|
||||
as211024 = {
|
||||
udpEncapsulation = true;
|
||||
vni = 211024;
|
||||
security.enable = true;
|
||||
peers = {
|
||||
@@ -104,9 +103,11 @@ in
|
||||
lvm = {
|
||||
dmeventd.enable = true;
|
||||
};
|
||||
resolved.settings.Resolve = {
|
||||
LLMNR = false;
|
||||
MulticastDNS = false;
|
||||
resolved = {
|
||||
llmnr = "false";
|
||||
extraConfig = ''
|
||||
MulticastDNS=false
|
||||
'';
|
||||
};
|
||||
netdata.enable = true;
|
||||
|
||||
@@ -162,47 +163,11 @@ in
|
||||
};
|
||||
wireguardPeers = [
|
||||
{
|
||||
PublicKey = "7N9YdQaCMWWIwAnW37vrthm9ZpbnG4Lx3gheHeRYz2E=";
|
||||
AllowedIPs = [ allAssignments.kelder.estuary.ipv4.address ];
|
||||
PersistentKeepalive = 25;
|
||||
}
|
||||
];
|
||||
};
|
||||
}
|
||||
{
|
||||
"30-hillcrest" = {
|
||||
netdevConfig = {
|
||||
Name = "hillcrest";
|
||||
Kind = "wireguard";
|
||||
};
|
||||
wireguardConfig = {
|
||||
PrivateKeyFile = config.age.secrets."estuary/hillcrest-wg.key".path;
|
||||
ListenPort = lib.my.c.hillcrest.vpn.port;
|
||||
};
|
||||
wireguardPeers = [
|
||||
{
|
||||
PublicKey = "+67Ks+ZRk1ssNCfg5BFKmIE9NtLasAxRE6XMqufx5GY=";
|
||||
AllowedIPs = [ (net.cidr.host 2 prefixes.hillcrest.v4) ];
|
||||
PersistentKeepalive = 25;
|
||||
}
|
||||
];
|
||||
};
|
||||
}
|
||||
{
|
||||
"30-john-valorant" = {
|
||||
netdevConfig = {
|
||||
Name = "john-valorant";
|
||||
Kind = "wireguard";
|
||||
};
|
||||
wireguardConfig = {
|
||||
PrivateKeyFile = config.age.secrets."estuary/john-valorant-wg.key".path;
|
||||
ListenPort = lib.my.c.john-valorant.vpn.port;
|
||||
};
|
||||
wireguardPeers = [
|
||||
{
|
||||
PublicKey = "xyqKF0yOAv1bObN1paL2vATFh77pdFfvN+JmuAxaTCk=";
|
||||
AllowedIPs = [ (net.cidr.host 2 prefixes.john-valorant.v4) ];
|
||||
PersistentKeepalive = 25;
|
||||
wireguardPeerConfig = {
|
||||
PublicKey = "7N9YdQaCMWWIwAnW37vrthm9ZpbnG4Lx3gheHeRYz2E=";
|
||||
AllowedIPs = [ allAssignments.kelder.estuary.ipv4.address ];
|
||||
PersistentKeepalive = 25;
|
||||
};
|
||||
}
|
||||
];
|
||||
};
|
||||
@@ -254,7 +219,7 @@ in
|
||||
in
|
||||
mkMerge
|
||||
[
|
||||
(mkIXPConfig "frys-ix" "185.1.160.196/23" "2001:7f8:10f::3:3850:196/64")
|
||||
(mkIXPConfig "frys-ix" "185.1.203.196/24" "2001:7f8:10f::3:3850:196/64")
|
||||
(mkIXPConfig "nl-ix" "193.239.116.145/22" "2001:7f8:13::a521:1024:1/64")
|
||||
(mkIXPConfig "fogixp" "185.1.147.159/24" "2001:7f8:ca:1::159/64")
|
||||
{
|
||||
@@ -312,51 +277,47 @@ in
|
||||
};
|
||||
ipv6Prefixes = [
|
||||
{
|
||||
Prefix = prefixes.base.v6;
|
||||
ipv6PrefixConfig.Prefix = prefixes.base.v6;
|
||||
}
|
||||
];
|
||||
routes = flatten ([
|
||||
{
|
||||
Destination = prefixes.vip1;
|
||||
Gateway = allAssignments.colony.routing.ipv4.address;
|
||||
}
|
||||
{
|
||||
Destination = prefixes.vip3;
|
||||
Gateway = allAssignments.colony.routing.ipv4.address;
|
||||
}
|
||||
{
|
||||
Destination = prefixes.darts.v4;
|
||||
Gateway = allAssignments.colony.routing.ipv4.address;
|
||||
}
|
||||
{
|
||||
Destination = prefixes.cust.v6;
|
||||
Gateway = allAssignments.colony.internal.ipv6.address;
|
||||
}
|
||||
routes = map (r: { routeConfig = r; }) (flatten
|
||||
([
|
||||
{
|
||||
Destination = prefixes.vip1;
|
||||
Gateway = allAssignments.colony.routing.ipv4.address;
|
||||
}
|
||||
{
|
||||
Destination = prefixes.vip3;
|
||||
Gateway = allAssignments.colony.routing.ipv4.address;
|
||||
}
|
||||
{
|
||||
Destination = prefixes.darts.v4;
|
||||
Gateway = allAssignments.colony.routing.ipv4.address;
|
||||
}
|
||||
{
|
||||
Destination = prefixes.cust.v6;
|
||||
Gateway = allAssignments.colony.internal.ipv6.address;
|
||||
}
|
||||
|
||||
{
|
||||
Destination = lib.my.c.tailscale.prefix.v4;
|
||||
Gateway = allAssignments.colony.routing.ipv4.address;
|
||||
}
|
||||
{
|
||||
Destination = lib.my.c.tailscale.prefix.v6;
|
||||
Gateway = allAssignments.colony.internal.ipv6.address;
|
||||
}
|
||||
|
||||
{
|
||||
Destination = prefixes.qclk.v4;
|
||||
Gateway = allAssignments.colony.routing.ipv4.address;
|
||||
}
|
||||
] ++
|
||||
(map (pName: [
|
||||
{
|
||||
Gateway = allAssignments.colony.routing.ipv4.address;
|
||||
Destination = prefixes."${pName}".v4;
|
||||
}
|
||||
{
|
||||
Destination = prefixes."${pName}".v6;
|
||||
Gateway = allAssignments.colony.internal.ipv6.address;
|
||||
}
|
||||
]) [ "vms" "ctrs" "oci" ]));
|
||||
{
|
||||
Destination = lib.my.c.tailscale.prefix.v4;
|
||||
Gateway = allAssignments.colony.routing.ipv4.address;
|
||||
}
|
||||
{
|
||||
Destination = lib.my.c.tailscale.prefix.v6;
|
||||
Gateway = allAssignments.colony.internal.ipv6.address;
|
||||
}
|
||||
] ++
|
||||
(map (pName: [
|
||||
{
|
||||
Gateway = allAssignments.colony.routing.ipv4.address;
|
||||
Destination = prefixes."${pName}".v4;
|
||||
}
|
||||
{
|
||||
Destination = prefixes."${pName}".v6;
|
||||
Gateway = allAssignments.colony.internal.ipv6.address;
|
||||
}
|
||||
]) [ "vms" "ctrs" "oci" ])));
|
||||
}
|
||||
];
|
||||
|
||||
@@ -365,7 +326,7 @@ in
|
||||
{
|
||||
matchConfig.Name = "as211024";
|
||||
networkConfig.IPv6AcceptRA = mkForce false;
|
||||
routes = [
|
||||
routes = map (r: { routeConfig = r; }) [
|
||||
{
|
||||
Destination = lib.my.c.home.prefixes.all.v4;
|
||||
Gateway = lib.my.c.home.vips.as211024.v4;
|
||||
@@ -377,28 +338,10 @@ in
|
||||
matchConfig.Name = "kelder";
|
||||
routes = [
|
||||
{
|
||||
Destination = allAssignments.kelder.estuary.ipv4.address;
|
||||
Scope = "link";
|
||||
}
|
||||
];
|
||||
};
|
||||
"95-hillcrest" = {
|
||||
matchConfig.Name = "hillcrest";
|
||||
address = [ "${net.cidr.host 1 prefixes.hillcrest.v4}/32" ];
|
||||
routes = [
|
||||
{
|
||||
Destination = net.cidr.host 2 prefixes.hillcrest.v4;
|
||||
Scope = "link";
|
||||
}
|
||||
];
|
||||
};
|
||||
"95-john-valorant" = {
|
||||
matchConfig.Name = "john-valorant";
|
||||
address = [ "${net.cidr.host 1 prefixes.john-valorant.v4}/32" ];
|
||||
routes = [
|
||||
{
|
||||
Destination = net.cidr.host 2 prefixes.john-valorant.v4;
|
||||
Scope = "link";
|
||||
routeConfig = {
|
||||
Destination = allAssignments.kelder.estuary.ipv4.address;
|
||||
Scope = "link";
|
||||
};
|
||||
}
|
||||
];
|
||||
};
|
||||
@@ -412,12 +355,6 @@ in
|
||||
"estuary/kelder-wg.key" = {
|
||||
owner = "systemd-network";
|
||||
};
|
||||
"estuary/hillcrest-wg.key" = {
|
||||
owner = "systemd-network";
|
||||
};
|
||||
"estuary/john-valorant-wg.key" = {
|
||||
owner = "systemd-network";
|
||||
};
|
||||
"l2mesh/as211024.key" = {};
|
||||
};
|
||||
};
|
||||
@@ -429,13 +366,7 @@ in
|
||||
};
|
||||
};
|
||||
firewall = {
|
||||
udp.allowed = [
|
||||
5353
|
||||
|
||||
lib.my.c.kelder.vpn.port
|
||||
lib.my.c.hillcrest.vpn.port
|
||||
lib.my.c.john-valorant.vpn.port
|
||||
];
|
||||
udp.allowed = [ 5353 lib.my.c.kelder.vpn.port ];
|
||||
tcp.allowed = [ 5353 "bgp" ];
|
||||
nat = {
|
||||
enable = true;
|
||||
@@ -462,28 +393,16 @@ in
|
||||
# Safe enough to allow all SSH
|
||||
tcp dport ssh accept
|
||||
|
||||
# jam-ctr forwards
|
||||
ip daddr ${aa.shill.internal.ipv4.address} tcp dport 60022 accept
|
||||
|
||||
ip6 daddr ${aa.middleman.internal.ipv6.address} tcp dport { http, https, 8448 } accept
|
||||
${matchInet "tcp dport { http, https } accept" "git"}
|
||||
ip6 daddr ${aa.simpcraft-oci.internal.ipv6.address} tcp dport 25565 accept
|
||||
ip6 daddr ${aa.simpcraft-oci.internal.ipv6.address} tcp dport { 25565, 25575 } accept
|
||||
ip6 daddr ${aa.simpcraft-staging-oci.internal.ipv6.address} tcp dport 25565 accept
|
||||
ip6 daddr ${aa.kevcraft-oci.internal.ipv6.address} tcp dport 25567 accept
|
||||
ip6 daddr ${aa.kinkcraft-oci.internal.ipv6.address} tcp dport 25568 accept
|
||||
ip6 daddr ${aa.graeme-oci.internal.ipv6.address} tcp dport 25569 accept
|
||||
ip6 daddr ${aa.gam.internal.ipv6.address} tcp dport 7777 accept
|
||||
return
|
||||
}
|
||||
chain routing-udp {
|
||||
ip6 daddr ${aa.valheim-oci.internal.ipv6.address} udp dport { 2456-2457 } accept
|
||||
ip6 daddr ${aa.waffletail.internal.ipv6.address} udp dport 41641 accept
|
||||
ip6 daddr ${aa.simpcraft-oci.internal.ipv6.address} udp dport 25565 accept
|
||||
ip6 daddr ${aa.enshrouded-oci.internal.ipv6.address} udp dport { 15636-15637 } accept
|
||||
ip6 daddr ${aa.kevcraft-oci.internal.ipv6.address} udp dport 25567 accept
|
||||
ip6 daddr ${aa.kinkcraft-oci.internal.ipv6.address} udp dport 25568 accept
|
||||
ip6 daddr ${aa.graeme-oci.internal.ipv6.address} udp dport 25569 accept
|
||||
ip6 daddr ${aa.gam.internal.ipv6.address} udp dport 7777 accept
|
||||
return
|
||||
}
|
||||
chain filter-routing {
|
||||
@@ -504,7 +423,7 @@ in
|
||||
iifname { wan, as211024, $ixps } oifname base jump filter-routing
|
||||
oifname $ixps jump ixp
|
||||
iifname base oifname { base, wan, $ixps } accept
|
||||
oifname { as211024, kelder, hillcrest, john-valorant } accept
|
||||
oifname { as211024, kelder } accept
|
||||
}
|
||||
chain output {
|
||||
oifname ifog ether type != vlan reject
|
||||
@@ -516,8 +435,6 @@ in
|
||||
${matchInet "meta l4proto { udp, tcp } th dport domain redirect to :5353" "estuary"}
|
||||
}
|
||||
chain postrouting {
|
||||
oifname hillcrest snat ip to ${net.cidr.host 1 prefixes.hillcrest.v4}
|
||||
oifname john-valorant snat ip to ${net.cidr.host 1 prefixes.john-valorant.v4}
|
||||
ip saddr ${prefixes.all.v4} oifname != as211024 snat to ${assignments.internal.ipv4.address}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
let
|
||||
inherit (builtins) attrNames;
|
||||
inherit (lib.my) net;
|
||||
inherit (lib.my.c.colony) prefixes custRouting;
|
||||
inherit (lib.my.c.colony) prefixes;
|
||||
|
||||
authZones = attrNames config.my.pdns.auth.bind.zones;
|
||||
in
|
||||
@@ -14,7 +14,7 @@ in
|
||||
owner = "pdns";
|
||||
group = "pdns";
|
||||
};
|
||||
"estuary/pdns/recursor.yml" = {
|
||||
"estuary/pdns/recursor.conf" = {
|
||||
owner = "pdns-recursor";
|
||||
group = "pdns-recursor";
|
||||
};
|
||||
@@ -31,7 +31,7 @@ in
|
||||
|
||||
pdns.recursor = {
|
||||
enable = true;
|
||||
extraSettingsFile = config.age.secrets."estuary/pdns/recursor.yml".path;
|
||||
extraSettingsFile = config.age.secrets."estuary/pdns/recursor.conf".path;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -44,55 +44,45 @@ in
|
||||
};
|
||||
|
||||
pdns-recursor = {
|
||||
dns = {
|
||||
address = [
|
||||
"127.0.0.1" "::1"
|
||||
assignments.base.ipv4.address assignments.base.ipv6.address
|
||||
];
|
||||
allowFrom = [
|
||||
"127.0.0.0/8" "::1/128"
|
||||
prefixes.all.v4 prefixes.all.v6
|
||||
] ++ (with lib.my.c.tailscale.prefix; [ v4 v6 ]);
|
||||
};
|
||||
|
||||
settings = {
|
||||
incoming = {
|
||||
listen = [
|
||||
"127.0.0.1" "::1"
|
||||
assignments.base.ipv4.address assignments.base.ipv6.address
|
||||
];
|
||||
allow_from = [
|
||||
"127.0.0.0/8" "::1/128"
|
||||
prefixes.all.v4 prefixes.all.v6
|
||||
] ++ (with lib.my.c.tailscale.prefix; [ v4 v6 ]);
|
||||
query-local-address = [
|
||||
assignments.internal.ipv4.address
|
||||
assignments.internal.ipv6.address
|
||||
assignments.base.ipv6.address
|
||||
];
|
||||
forward-zones = map (z: "${z}=127.0.0.1:5353") authZones;
|
||||
|
||||
# DNS NOTIFY messages override TTL
|
||||
allow_notify_for = authZones;
|
||||
allow_notify_from = [ "127.0.0.0/8" "::1/128" ];
|
||||
};
|
||||
# DNS NOTIFY messages override TTL
|
||||
allow-notify-for = authZones;
|
||||
allow-notify-from = [ "127.0.0.0/8" "::1/128" ];
|
||||
|
||||
outgoing = {
|
||||
source_address = [
|
||||
assignments.internal.ipv4.address
|
||||
assignments.internal.ipv6.address
|
||||
assignments.base.ipv6.address
|
||||
];
|
||||
};
|
||||
webserver = true;
|
||||
webserver-address = "::";
|
||||
webserver-allow-from = [ "127.0.0.1" "::1" ];
|
||||
|
||||
recursor = {
|
||||
forward_zones = map (z: {
|
||||
zone = z;
|
||||
forwarders = [ "127.0.0.1:5353" ];
|
||||
}) authZones;
|
||||
|
||||
lua_dns_script = pkgs.writeText "pdns-script.lua" ''
|
||||
function preresolve(dq)
|
||||
if dq.qname:equal("nix-cache.nul.ie") then
|
||||
dq:addAnswer(pdns.CNAME, "http.${config.networking.domain}.")
|
||||
dq.rcode = 0
|
||||
dq.followupFunction = "followCNAMERecords"
|
||||
return true
|
||||
end
|
||||
|
||||
return false
|
||||
lua-dns-script = pkgs.writeText "pdns-script.lua" ''
|
||||
function preresolve(dq)
|
||||
if dq.qname:equal("nix-cache.nul.ie") then
|
||||
dq:addAnswer(pdns.CNAME, "http.${config.networking.domain}.")
|
||||
dq.rcode = 0
|
||||
dq.followupFunction = "followCNAMERecords"
|
||||
return true
|
||||
end
|
||||
'';
|
||||
};
|
||||
|
||||
webservice = {
|
||||
webserver = true;
|
||||
address = "::";
|
||||
allow_from = [ "127.0.0.1" "::1" ];
|
||||
};
|
||||
return false
|
||||
end
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -163,15 +153,6 @@ in
|
||||
simpcraft IN AAAA ${allAssignments.simpcraft-oci.internal.ipv6.address}
|
||||
simpcraft-staging IN A ${assignments.internal.ipv4.address}
|
||||
simpcraft-staging IN AAAA ${allAssignments.simpcraft-staging-oci.internal.ipv6.address}
|
||||
enshrouded IN A ${assignments.internal.ipv4.address}
|
||||
kevcraft IN A ${assignments.internal.ipv4.address}
|
||||
kevcraft IN AAAA ${allAssignments.kevcraft-oci.internal.ipv6.address}
|
||||
kinkcraft IN A ${assignments.internal.ipv4.address}
|
||||
kinkcraft IN AAAA ${allAssignments.kinkcraft-oci.internal.ipv6.address}
|
||||
graeme IN A ${assignments.internal.ipv4.address}
|
||||
graeme IN AAAA ${allAssignments.graeme-oci.internal.ipv6.address}
|
||||
terraria IN A ${assignments.internal.ipv4.address}
|
||||
terraria IN AAAA ${allAssignments.gam.internal.ipv6.address}
|
||||
|
||||
mail-vm IN A ${net.cidr.host 0 prefixes.mail.v4}
|
||||
mail-vm IN AAAA ${net.cidr.host 1 prefixes.mail.v6}
|
||||
@@ -181,13 +162,6 @@ in
|
||||
|
||||
andrey-cust IN A ${allAssignments.kelder.estuary.ipv4.address}
|
||||
|
||||
jam-cust IN A ${net.cidr.host 0 prefixes.jam.v4}
|
||||
jam-fwd IN A ${allAssignments.shill.internal.ipv4.address}
|
||||
jam-cust IN AAAA ${net.cidr.host 1 prefixes.jam.v6}
|
||||
|
||||
hillcrest-tun IN A ${net.cidr.host 2 prefixes.hillcrest.v4}
|
||||
john-valorant-tun IN A ${net.cidr.host 2 prefixes.john-valorant.v4}
|
||||
|
||||
$TTL 3
|
||||
_acme-challenge IN LUA TXT @@FILE@@
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ let
|
||||
inherit (lib) mkMerge mkDefault;
|
||||
inherit (lib.my) net;
|
||||
inherit (lib.my.c) pubDomain;
|
||||
inherit (lib.my.c.colony) domain prefixes firewallForwards;
|
||||
inherit (lib.my.c.colony) domain prefixes;
|
||||
inherit (lib.my.c.nginx) baseHttpConfig proxyHeaders;
|
||||
in
|
||||
{
|
||||
@@ -95,13 +95,41 @@ in
|
||||
"*.${pubDomain}"
|
||||
];
|
||||
dnsProvider = "cloudflare";
|
||||
environmentFile = config.age.secrets."middleman/cloudflare-credentials.conf".path;
|
||||
credentialsFile = config.age.secrets."middleman/cloudflare-credentials.conf".path;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
services = {
|
||||
fstrim = lib.my.c.colony.fstrimConfig;
|
||||
# Hacks for Jsch (Minecraft FastBack) to work
|
||||
openssh = {
|
||||
hostKeys = [
|
||||
{
|
||||
bits = 4096;
|
||||
path = "/etc/ssh/ssh_host_rsa_key";
|
||||
type = "rsa";
|
||||
}
|
||||
{
|
||||
path = "/etc/ssh/ssh_host_ed25519_key";
|
||||
type = "ed25519";
|
||||
}
|
||||
|
||||
{
|
||||
type = "ecdsa-sha2-nistp256";
|
||||
path = "/etc/ssh/ssh_host_ecdsa_key";
|
||||
}
|
||||
];
|
||||
settings = {
|
||||
Macs = [
|
||||
"hmac-sha2-512-etm@openssh.com"
|
||||
"hmac-sha2-256-etm@openssh.com"
|
||||
"umac-128-etm@openssh.com"
|
||||
|
||||
"hmac-sha2-256"
|
||||
];
|
||||
};
|
||||
};
|
||||
netdata.enable = true;
|
||||
nginx = {
|
||||
enable = true;
|
||||
@@ -111,6 +139,7 @@ in
|
||||
recommendedTlsSettings = true;
|
||||
clientMaxBodySize = "0";
|
||||
serverTokens = true;
|
||||
sslDhparam = config.age.secrets."dhparams.pem".path;
|
||||
|
||||
# Based on recommended*Settings, but probably better to be explicit about these
|
||||
appendHttpConfig = ''
|
||||
@@ -181,6 +210,11 @@ in
|
||||
secrets = {
|
||||
key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIP+KINpHLMduBuW96JzfSRDLUzkI+XaCBghu5/wHiW5R";
|
||||
files = {
|
||||
"dhparams.pem" = {
|
||||
owner = "acme";
|
||||
group = "acme";
|
||||
mode = "440";
|
||||
};
|
||||
"middleman/cloudflare-credentials.conf" = {
|
||||
owner = "acme";
|
||||
group = "acme";
|
||||
@@ -191,7 +225,6 @@ in
|
||||
|
||||
firewall = {
|
||||
tcp.allowed = [ 19999 "http" "https" ];
|
||||
nat.forwardPorts."${allAssignments.estuary.internal.ipv4.address}" = firewallForwards allAssignments;
|
||||
extraRules = ''
|
||||
table inet filter {
|
||||
chain forward {
|
||||
|
||||
@@ -1,7 +1,19 @@
|
||||
{ lib, pkgs, config, ... }:
|
||||
let
|
||||
inherit (builtins) toJSON;
|
||||
inherit (lib) mkForce;
|
||||
inherit (lib.my.c) pubDomain;
|
||||
|
||||
cfgFile = pkgs.writeText "gitea-actions-runner.yaml" (toJSON {
|
||||
container = {
|
||||
network = "podman";
|
||||
privileged = true;
|
||||
};
|
||||
cache = {
|
||||
enabled = true;
|
||||
dir = "/var/cache/gitea-runner";
|
||||
};
|
||||
});
|
||||
in
|
||||
{
|
||||
config = {
|
||||
@@ -18,24 +30,11 @@ in
|
||||
enable = true;
|
||||
name = "main-docker";
|
||||
labels = [
|
||||
"debian-node-trixie:docker://node:24-trixie"
|
||||
"ubuntu-26.04:docker://git.nul.ie/dev/actions-ubuntu:26.04"
|
||||
"debian-node-bullseye:docker://node:18-bullseye"
|
||||
"ubuntu-22.04:docker://git.nul.ie/dev/actions-ubuntu:22.04"
|
||||
];
|
||||
url = "https://git.${pubDomain}";
|
||||
tokenFile = config.age.secrets."gitea/actions-runner.env".path;
|
||||
settings = {
|
||||
runner = {
|
||||
timeout = "8h";
|
||||
};
|
||||
container = {
|
||||
network = "podman";
|
||||
privileged = true;
|
||||
};
|
||||
cache = {
|
||||
enabled = true;
|
||||
dir = "/var/cache/gitea-runner";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -62,6 +61,7 @@ in
|
||||
DynamicUser = mkForce false;
|
||||
User = "gitea-runner";
|
||||
Group = "gitea-runner";
|
||||
ExecStart = mkForce "${config.services.gitea-actions-runner.package}/bin/act_runner -c ${cfgFile} daemon";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,106 +0,0 @@
|
||||
{ lib, pkgs, assignments, ... }:
|
||||
let
|
||||
inherit (lib.my) net;
|
||||
inherit (lib.my.c.colony) prefixes custRouting;
|
||||
in
|
||||
{
|
||||
fileSystems = {
|
||||
"/mnt/jam" = {
|
||||
device = "/dev/disk/by-label/jam";
|
||||
fsType = "ext4";
|
||||
};
|
||||
|
||||
"/var/lib/machines/jam" = {
|
||||
device = "/mnt/jam";
|
||||
options = [ "bind" ];
|
||||
fsType = "none";
|
||||
};
|
||||
};
|
||||
|
||||
systemd = {
|
||||
nspawn = {
|
||||
jam = {
|
||||
enable = true;
|
||||
execConfig = {
|
||||
Boot = true;
|
||||
PrivateUsers = "pick";
|
||||
LinkJournal = false;
|
||||
};
|
||||
networkConfig = {
|
||||
Private = true;
|
||||
VirtualEthernet = true;
|
||||
};
|
||||
};
|
||||
};
|
||||
network.networks = {
|
||||
"50-ve-jam" = {
|
||||
matchConfig = {
|
||||
Kind = "veth";
|
||||
Name = "ve-jam";
|
||||
};
|
||||
address = [
|
||||
custRouting.jam-ctr
|
||||
prefixes.jam.v6
|
||||
];
|
||||
networkConfig = {
|
||||
IPv6AcceptRA = false;
|
||||
IPv6SendRA = true;
|
||||
};
|
||||
ipv6Prefixes = [
|
||||
{
|
||||
Prefix = prefixes.jam.v6;
|
||||
}
|
||||
];
|
||||
routes = [
|
||||
{
|
||||
Destination = prefixes.jam.v4;
|
||||
Scope = "link";
|
||||
}
|
||||
];
|
||||
};
|
||||
};
|
||||
services = {
|
||||
"systemd-nspawn@jam" = {
|
||||
overrideStrategy = "asDropin";
|
||||
|
||||
serviceConfig = {
|
||||
CPUQuota = "400%";
|
||||
MemoryHigh = "infinity";
|
||||
MemoryMax = "4G";
|
||||
};
|
||||
|
||||
wantedBy = [ "machines.target" ];
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
my = {
|
||||
firewall =
|
||||
let
|
||||
jamIP = net.cidr.host 0 prefixes.jam.v4;
|
||||
in
|
||||
{
|
||||
nat.forwardPorts."${assignments.internal.ipv4.address}" = [
|
||||
{
|
||||
port = 60022;
|
||||
dst = jamIP;
|
||||
dstPort = "ssh";
|
||||
}
|
||||
];
|
||||
extraRules = ''
|
||||
table inet filter {
|
||||
chain forward {
|
||||
iifname { ve-jam } oifname vms accept
|
||||
iifname vms oifname { ve-jam } accept
|
||||
}
|
||||
}
|
||||
|
||||
table inet nat {
|
||||
chain postrouting {
|
||||
ip saddr ${jamIP} snat to ${assignments.internal.ipv4.address}
|
||||
}
|
||||
}
|
||||
'';
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -24,7 +24,7 @@ in
|
||||
|
||||
configuration = { lib, pkgs, config, assignments, allAssignments, ... }:
|
||||
let
|
||||
inherit (lib) genAttrs mkMerge mkIf mkForce;
|
||||
inherit (lib) mkMerge mkIf mkForce;
|
||||
inherit (lib.my) networkdAssignment;
|
||||
in
|
||||
{
|
||||
@@ -45,22 +45,9 @@ in
|
||||
owner = "matrix-synapse";
|
||||
group = "matrix-synapse";
|
||||
};
|
||||
"chatterbox/doublepuppet.yaml" = {
|
||||
owner = "matrix-synapse";
|
||||
group = "matrix-synapse";
|
||||
};
|
||||
|
||||
"chatterbox/mautrix-whatsapp.env" = {
|
||||
owner = "mautrix-whatsapp";
|
||||
group = "mautrix-whatsapp";
|
||||
};
|
||||
"chatterbox/mautrix-messenger.env" = {
|
||||
owner = "mautrix-meta-messenger";
|
||||
group = "mautrix-meta";
|
||||
};
|
||||
"chatterbox/mautrix-instagram.env" = {
|
||||
owner = "mautrix-meta-instagram";
|
||||
group = "mautrix-meta";
|
||||
"chatterbox/syncv3.env" = {
|
||||
owner = "matrix-syncv3";
|
||||
group = "matrix-syncv3";
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -72,24 +59,29 @@ in
|
||||
|
||||
users = with lib.my.c.ids; {
|
||||
users = {
|
||||
matrix-synapse.extraGroups = [
|
||||
"mautrix-whatsapp"
|
||||
];
|
||||
matrix-syncv3 = {
|
||||
isSystemUser = true;
|
||||
uid = uids.matrix-syncv3;
|
||||
group = "matrix-syncv3";
|
||||
};
|
||||
};
|
||||
groups = {
|
||||
matrix-syncv3.gid = gids.matrix-syncv3;
|
||||
};
|
||||
groups = { };
|
||||
};
|
||||
|
||||
systemd = {
|
||||
network.networks."80-container-host0" = networkdAssignment "host0" assignments.internal;
|
||||
services = { } // (genAttrs [ "mautrix-whatsapp" "mautrix-meta-messenger" "mautrix-meta-instagram" ] (_: {
|
||||
# ffmpeg needed to convert GIFs to video
|
||||
path = with pkgs; [ ffmpeg ];
|
||||
}));
|
||||
services = {
|
||||
matrix-sliding-sync.serviceConfig = {
|
||||
# Needs to be able to read its secrets
|
||||
DynamicUser = mkForce false;
|
||||
User = "matrix-syncv3";
|
||||
Group = "matrix-syncv3";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
# TODO/FIXME: https://github.com/NixOS/nixpkgs/issues/336052
|
||||
nixpkgs.config.permittedInsecurePackages = [ "olm-3.2.16" ];
|
||||
|
||||
services = {
|
||||
netdata.enable = true;
|
||||
matrix-synapse = {
|
||||
@@ -176,10 +168,18 @@ in
|
||||
|
||||
app_service_config_files = [
|
||||
"/var/lib/heisenbridge/registration.yml"
|
||||
config.age.secrets."chatterbox/doublepuppet.yaml".path
|
||||
];
|
||||
};
|
||||
|
||||
sliding-sync = {
|
||||
enable = true;
|
||||
createDatabase = false;
|
||||
environmentFile = config.age.secrets."chatterbox/syncv3.env".path;
|
||||
settings = {
|
||||
SYNCV3_BINDADDR = "[::]:8009";
|
||||
SYNCV3_SERVER = "http://localhost:8008";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
heisenbridge = {
|
||||
@@ -195,181 +195,6 @@ in
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
mautrix-whatsapp = {
|
||||
enable = true;
|
||||
# package = pkgs.mautrix-whatsapp.overrideAttrs (o: rec {
|
||||
# version = "26.05";
|
||||
# tag = "v0.2605.0";
|
||||
# src = pkgs.fetchFromGitHub {
|
||||
# owner = "mautrix";
|
||||
# repo = "whatsapp";
|
||||
# inherit tag;
|
||||
# hash = "sha256-WlVfGQoP9e/wl98hUJei8O2JMcOKijoEY8XuU/z69Qk=";
|
||||
# };
|
||||
# vendorHash = "sha256-Hi/dZHJHoTTCnxLXgbkcYzuzis4fl5kxb5wMd9fKTY8=";
|
||||
# });
|
||||
environmentFile = config.age.secrets."chatterbox/mautrix-whatsapp.env".path;
|
||||
settings = {
|
||||
database = {
|
||||
type = "postgres";
|
||||
uri = "$MAU_WAPP_PSQL_URI";
|
||||
};
|
||||
homeserver = {
|
||||
address = "http://localhost:8008";
|
||||
domain = "nul.ie";
|
||||
};
|
||||
appservice = {
|
||||
id = "whatsapp2";
|
||||
bot = {
|
||||
username = "whatsapp2";
|
||||
displayname = "WhatsApp Bridge Bot";
|
||||
};
|
||||
username_template = "wapp2_{{.}}";
|
||||
};
|
||||
bridge = {
|
||||
personal_filtering_spaces = true;
|
||||
command_prefix = "!wa";
|
||||
permissions = {
|
||||
"@dev:nul.ie" = "admin";
|
||||
};
|
||||
};
|
||||
double_puppet = {
|
||||
secrets."nul.ie" = "$MAU_WAPP_DOUBLE_PUPPET_TOKEN";
|
||||
};
|
||||
encryption = {
|
||||
allow = true;
|
||||
default = true;
|
||||
require = true;
|
||||
pickle_key = "maunium.net/go/mautrix-whatsapp";
|
||||
};
|
||||
matrix = {
|
||||
delivery_receipts = true;
|
||||
};
|
||||
network = {
|
||||
displayname_template = ''{{or .BusinessName .PushName .FullName .Phone "Unknown user"}} (WA)'';
|
||||
url_previews = true;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
# mautrix-meta.package = pkgs.mautrix-meta.overrideAttrs (o: rec {
|
||||
# version = "26.05.1";
|
||||
# tag = "v0.2605.1";
|
||||
# src = pkgs.fetchFromGitHub {
|
||||
# owner = "mautrix";
|
||||
# repo = "meta";
|
||||
# inherit tag;
|
||||
# hash = "sha256-zpolDtwGulDTiojJPnkj9O0D5b4rgPYQX6A28rvuvM0=";
|
||||
# };
|
||||
# vendorHash = "sha256-+i45bXBhlXPXX24VMS9IJLLX+i4VPnqy5RAH4j88sTA=";
|
||||
# });
|
||||
mautrix-meta.instances = {
|
||||
messenger = {
|
||||
enable = true;
|
||||
registerToSynapse = true;
|
||||
dataDir = "mautrix-messenger";
|
||||
environmentFile = config.age.secrets."chatterbox/mautrix-messenger.env".path;
|
||||
settings = {
|
||||
database = {
|
||||
type = "postgres";
|
||||
uri = "$MAU_FBM_PSQL_URI";
|
||||
};
|
||||
homeserver = {
|
||||
address = "http://localhost:8008";
|
||||
domain = "nul.ie";
|
||||
};
|
||||
appservice = {
|
||||
id = "fbm2";
|
||||
bot = {
|
||||
username = "messenger2";
|
||||
displayname = "Messenger Bridge Bot";
|
||||
avatar = "mxc://maunium.net/ygtkteZsXnGJLJHRchUwYWak";
|
||||
};
|
||||
username_template = "fbm2_{{.}}";
|
||||
};
|
||||
network = {
|
||||
mode = "messenger";
|
||||
displayname_template = ''{{or .DisplayName .Username "Unknown user"}} (FBM)'';
|
||||
};
|
||||
bridge = {
|
||||
personal_filtering_spaces = true;
|
||||
# management_room_text.welcome = "Hello, I'm a Messenger bridge bot.";
|
||||
command_prefix = "!fbm";
|
||||
backfill = {
|
||||
enabled = true;
|
||||
};
|
||||
permissions = {
|
||||
"@dev:nul.ie" = "admin";
|
||||
};
|
||||
};
|
||||
double_puppet = {
|
||||
secrets."nul.ie" = "$MAU_FBM_DOUBLE_PUPPET_TOKEN";
|
||||
};
|
||||
encryption = {
|
||||
allow = true;
|
||||
default = true;
|
||||
require = true;
|
||||
};
|
||||
matrix = {
|
||||
delivery_receipts = true;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
instagram = {
|
||||
enable = true;
|
||||
registerToSynapse = true;
|
||||
dataDir = "mautrix-instagram";
|
||||
environmentFile = config.age.secrets."chatterbox/mautrix-instagram.env".path;
|
||||
settings = {
|
||||
database = {
|
||||
type = "postgres";
|
||||
uri = "$MAU_IG_PSQL_URI";
|
||||
};
|
||||
homeserver = {
|
||||
address = "http://localhost:8008";
|
||||
domain = "nul.ie";
|
||||
};
|
||||
appservice = {
|
||||
id = "instagram";
|
||||
bot = {
|
||||
username = "instagram";
|
||||
displayname = "Instagram Bridge Bot";
|
||||
avatar = "mxc://maunium.net/JxjlbZUlCPULEeHZSwleUXQv";
|
||||
};
|
||||
username_template = "ig_{{.}}";
|
||||
};
|
||||
network = {
|
||||
mode = "instagram";
|
||||
displayname_template = ''{{or .DisplayName .Username "Unknown user"}} (IG)'';
|
||||
};
|
||||
bridge = {
|
||||
personal_filtering_spaces = true;
|
||||
# management_room_text.welcome = "Hello, I'm an Instagram bridge bot.";
|
||||
command_prefix = "!ig";
|
||||
backfill = {
|
||||
enabled = true;
|
||||
};
|
||||
permissions = {
|
||||
"@dev:nul.ie" = "admin";
|
||||
"@adzerq:nul.ie" = "user";
|
||||
};
|
||||
};
|
||||
double_puppet = {
|
||||
secrets."nul.ie" = "$MAU_IG_DOUBLE_PUPPET_TOKEN";
|
||||
};
|
||||
encryption = {
|
||||
allow = true;
|
||||
default = true;
|
||||
require = true;
|
||||
};
|
||||
matrix = {
|
||||
delivery_receipts = true;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
(mkIf config.my.build.isDevVM {
|
||||
|
||||
@@ -8,7 +8,5 @@
|
||||
./object.nix
|
||||
./toot.nix
|
||||
./waffletail.nix
|
||||
./qclk
|
||||
./gam.nix
|
||||
];
|
||||
}
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
{ lib, ... }:
|
||||
let
|
||||
inherit (lib.my) net;
|
||||
inherit (lib.my.c) pubDomain;
|
||||
inherit (lib.my.c.colony) domain prefixes;
|
||||
in
|
||||
{
|
||||
nixos.systems.gam = { config, ... }: {
|
||||
system = "x86_64-linux";
|
||||
nixpkgs = "mine";
|
||||
rendered = config.configuration.config.my.asContainer;
|
||||
|
||||
assignments = {
|
||||
internal = {
|
||||
name = "gam-ctr";
|
||||
inherit domain;
|
||||
ipv4.address = net.cidr.host 11 prefixes.ctrs.v4;
|
||||
ipv6 = {
|
||||
iid = "::11";
|
||||
address = net.cidr.host 11 prefixes.ctrs.v6;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
configuration = { lib, pkgs, config, assignments, allAssignments, ... }:
|
||||
let
|
||||
inherit (lib) mkMerge mkIf mkForce;
|
||||
inherit (lib.my) networkdAssignment;
|
||||
in
|
||||
{
|
||||
config = mkMerge [
|
||||
{
|
||||
my = {
|
||||
deploy.enable = false;
|
||||
server.enable = true;
|
||||
|
||||
secrets = {
|
||||
key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAvDlH3nT1kve741gBluYmn5KQs8yz7FAEt8qLt+f0K6";
|
||||
files = {
|
||||
"gam/terraria.conf" = {
|
||||
owner = "terraria";
|
||||
group = "terraria";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
systemd = {
|
||||
network.networks."80-container-host0" = networkdAssignment "host0" assignments.internal;
|
||||
};
|
||||
|
||||
services = {
|
||||
terraria = {
|
||||
enable = true;
|
||||
noUPnP = true;
|
||||
messageOfTheDay = "sup gamers";
|
||||
autoCreatedWorldSize = "large";
|
||||
worldPath = "/var/lib/terraria/NotWorld.wld";
|
||||
configFile = config.age.secrets."gam/terraria.conf".path;
|
||||
openFirewall = true;
|
||||
};
|
||||
};
|
||||
}
|
||||
(mkIf config.my.build.isDevVM {
|
||||
virtualisation = {
|
||||
forwardPorts = [ ];
|
||||
};
|
||||
})
|
||||
];
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -1,8 +1,6 @@
|
||||
{ lib, ... }:
|
||||
let
|
||||
inherit (lib) concatStringsSep;
|
||||
inherit (lib.my) net;
|
||||
inherit (lib.my.c) pubDomain;
|
||||
inherit (lib.my.c.colony) domain prefixes;
|
||||
in
|
||||
{
|
||||
@@ -23,7 +21,7 @@ in
|
||||
};
|
||||
};
|
||||
|
||||
configuration = { lib, pkgs, config, allAssignments, ... }:
|
||||
configuration = { lib, pkgs, config, ... }:
|
||||
let
|
||||
inherit (lib) mkForce;
|
||||
in
|
||||
@@ -37,19 +35,6 @@ in
|
||||
|
||||
secrets = {
|
||||
key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPUv1ntVrZv5ripsKpcOAnyDQX2PHjowzyhqWK10Ml53";
|
||||
files = {
|
||||
"jackflix/photoprism-pass.txt" = {};
|
||||
"jackflix/copyparty-pass.txt" = {
|
||||
owner = "copyparty";
|
||||
group = "copyparty";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
firewall = {
|
||||
tcp.allowed = [
|
||||
3923
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
@@ -60,26 +45,15 @@ in
|
||||
transmission.extraGroups = [ "media" ];
|
||||
radarr.extraGroups = [ "media" ];
|
||||
sonarr.extraGroups = [ "media" ];
|
||||
seerr = {
|
||||
jellyseerr = {
|
||||
isSystemUser = true;
|
||||
uid = uids.jellyseerr;
|
||||
group = "seerr";
|
||||
};
|
||||
photoprism = {
|
||||
isSystemUser = true;
|
||||
uid = uids.photoprism;
|
||||
group = "photoprism";
|
||||
};
|
||||
copyparty = {
|
||||
uid = uids.copyparty;
|
||||
extraGroups = [ "media" ];
|
||||
group = "jellyseerr";
|
||||
};
|
||||
};
|
||||
groups = {
|
||||
media.gid = 2000;
|
||||
seerr.gid = gids.jellyseerr;
|
||||
photoprism.gid = gids.photoprism;
|
||||
copyparty.gid = gids.copyparty;
|
||||
jellyseerr.gid = gids.jellyseerr;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -88,15 +62,13 @@ in
|
||||
jackett.bindsTo = [ "systemd-networkd-wait-online@vpn.service" ];
|
||||
transmission.bindsTo = [ "systemd-networkd-wait-online@vpn.service" ];
|
||||
|
||||
radarr.serviceConfig.UMask = mkForce "0002";
|
||||
radarr.path = with pkgs; [ ffmpeg ];
|
||||
sonarr.serviceConfig.UMask = mkForce "0002";
|
||||
sonarr.path = with pkgs; [ ffmpeg ];
|
||||
seerr.serviceConfig = {
|
||||
radarr.serviceConfig.UMask = "0002";
|
||||
sonarr.serviceConfig.UMask = "0002";
|
||||
jellyseerr.serviceConfig = {
|
||||
# Needs to be able to read its secrets
|
||||
DynamicUser = mkForce false;
|
||||
User = "seerr";
|
||||
Group = "seerr";
|
||||
User = "jellyseerr";
|
||||
Group = "jellyseerr";
|
||||
};
|
||||
|
||||
# https://github.com/NixOS/nixpkgs/issues/258793#issuecomment-1748168206
|
||||
@@ -104,10 +76,6 @@ in
|
||||
RootDirectoryStartOnly = lib.mkForce false;
|
||||
RootDirectory = lib.mkForce "";
|
||||
};
|
||||
photoprism.serviceConfig = {
|
||||
# Needs to be able to access its data
|
||||
DynamicUser = mkForce false;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
@@ -116,7 +84,6 @@ in
|
||||
|
||||
transmission = {
|
||||
enable = true;
|
||||
package = pkgs.transmission_4;
|
||||
downloadDirPermissions = null;
|
||||
performanceNetParameters = true;
|
||||
settings = {
|
||||
@@ -141,78 +108,15 @@ in
|
||||
};
|
||||
};
|
||||
|
||||
flaresolverr.enable = true;
|
||||
jackett.enable = true;
|
||||
radarr.enable = true;
|
||||
sonarr.enable = true;
|
||||
seerr = {
|
||||
jellyseerr = {
|
||||
enable = true;
|
||||
openFirewall = true;
|
||||
};
|
||||
|
||||
jellyfin.enable = true;
|
||||
|
||||
photoprism = {
|
||||
enable = true;
|
||||
address = "[::]";
|
||||
port = 2342;
|
||||
originalsPath = "/mnt/media/photoprism/originals";
|
||||
importPath = "/mnt/media/photoprism/import";
|
||||
passwordFile = config.age.secrets."jackflix/photoprism-pass.txt".path;
|
||||
settings = {
|
||||
PHOTOPRISM_AUTH_MODE = "password";
|
||||
PHOTOPRISM_ADMIN_USER = "dev";
|
||||
PHOTOPRISM_APP_NAME = "/dev/player0 Photos";
|
||||
PHOTOPRISM_SITE_URL = "https://photos.${pubDomain}/";
|
||||
PHOTOPRISM_SITE_TITLE = "/dev/player0 Photos";
|
||||
PHOTOPRISM_TRUSTED_PROXY = concatStringsSep "," (with prefixes.ctrs; [ v4 v6 ]);
|
||||
PHOTOPRISM_DATABASE_DRIVER = "sqlite";
|
||||
};
|
||||
};
|
||||
|
||||
copyparty = {
|
||||
enable = true;
|
||||
package = pkgs.copyparty.override {
|
||||
withMagic = true;
|
||||
};
|
||||
settings = {
|
||||
name = "dev-stuff";
|
||||
no-reload = true;
|
||||
j = 8; # cores
|
||||
http-only = true;
|
||||
xff-src =
|
||||
with allAssignments.middleman.internal;
|
||||
[ "${ipv4.address}/32" prefixes.ctrs.v6 ];
|
||||
rproxy = 1; # get if from x-forwarded-for
|
||||
magic = true; # enable checking file magic on upload
|
||||
hist = "/var/cache/copyparty";
|
||||
shr = "/share"; # enable share creation
|
||||
ed = true; # enable dotfiles
|
||||
chmod-f = "664";
|
||||
chmod-d = "775";
|
||||
e2dsa = true; # file indexing
|
||||
e2t = true; # metadata indexing
|
||||
og-ua = "(Discord|Twitter|Slack)bot"; # embeds
|
||||
theme = 6;
|
||||
};
|
||||
accounts.dev.passwordFile = config.age.secrets."jackflix/copyparty-pass.txt".path;
|
||||
volumes = {
|
||||
"/" = {
|
||||
path = "/mnt/media/public";
|
||||
access = {
|
||||
A = "dev";
|
||||
"r." = "*";
|
||||
};
|
||||
flags = {
|
||||
shr_who = "no"; # no reason to have shares here
|
||||
};
|
||||
};
|
||||
"/priv" = {
|
||||
path = "/mnt/media/stuff";
|
||||
access.A = "dev"; # dev has admin access
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
@@ -12,7 +12,7 @@ let
|
||||
};
|
||||
|
||||
# Forwarded in AirVPN config
|
||||
transmissionPeerPort = 28457;
|
||||
transmissionPeerPort = 47016;
|
||||
in
|
||||
{
|
||||
config = mkMerge [
|
||||
@@ -37,7 +37,7 @@ in
|
||||
tcp dport ${toString transmissionPeerPort} accept
|
||||
iifname vpn return
|
||||
|
||||
tcp dport { 19999, 9091, 9117, 7878, 8989, 8096, 2342 } accept
|
||||
tcp dport { 19999, 9091, 9117, 7878, 8989, 8096 } accept
|
||||
return
|
||||
}
|
||||
chain input {
|
||||
@@ -71,12 +71,14 @@ in
|
||||
RouteTable = routeTable;
|
||||
};
|
||||
wireguardPeers = [
|
||||
# AirVPN NL
|
||||
{
|
||||
Endpoint = "2a00:1678:1337:2329:e5f:35d4:4404:ef9f:1637";
|
||||
PublicKey = "PyLCXAQT8KkM4T+dUsOQfn+Ub3pGxfGlxkIApuig+hk=";
|
||||
PresharedKeyFile = config.age.secrets."${pskFile}".path;
|
||||
AllowedIPs = [ "0.0.0.0/0" "::/0" ];
|
||||
# AirVPN NL
|
||||
wireguardPeerConfig = {
|
||||
Endpoint = "2a00:1678:1337:2329:e5f:35d4:4404:ef9f:1637";
|
||||
PublicKey = "PyLCXAQT8KkM4T+dUsOQfn+Ub3pGxfGlxkIApuig+hk=";
|
||||
PresharedKeyFile = config.age.secrets."${pskFile}".path;
|
||||
AllowedIPs = [ "0.0.0.0/0" "::/0" ];
|
||||
};
|
||||
}
|
||||
];
|
||||
};
|
||||
@@ -92,7 +94,7 @@ in
|
||||
matchConfig.Name = "vpn";
|
||||
address = [ "10.182.97.37/32" "fd7d:76ee:e68f:a993:735d:ef5e:6907:b122/128" ];
|
||||
dns = [ "10.128.0.1" "fd7d:76ee:e68f:a993::1" ];
|
||||
routingPolicyRules = [
|
||||
routingPolicyRules = map (r: { routingPolicyRuleConfig = r; }) [
|
||||
{
|
||||
Family = "both";
|
||||
SuppressPrefixLength = 0;
|
||||
|
||||
@@ -40,6 +40,11 @@ in
|
||||
secrets = {
|
||||
key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAQM9U1e/XcUCyMJITrpAHjAGahpqkZCmtX6pJkYzuks";
|
||||
files = {
|
||||
"dhparams.pem" = {
|
||||
owner = "acme";
|
||||
group = "acme";
|
||||
mode = "440";
|
||||
};
|
||||
"pdns-file-records.key" = {
|
||||
owner = "acme";
|
||||
group = "acme";
|
||||
@@ -67,13 +72,6 @@ in
|
||||
|
||||
firewall = {
|
||||
tcp.allowed = [ "http" "https" 8448 ];
|
||||
extraRules = ''
|
||||
table inet nat {
|
||||
chain postrouting {
|
||||
oifname host0 snat ip6 to ${assignments.internal.ipv6.address}
|
||||
}
|
||||
}
|
||||
'';
|
||||
};
|
||||
|
||||
nginx-sso = {
|
||||
@@ -171,7 +169,7 @@ in
|
||||
"*.${config.networking.domain}"
|
||||
];
|
||||
dnsProvider = "exec";
|
||||
environmentFile =
|
||||
credentialsFile =
|
||||
let
|
||||
script = pkgs.writeShellScript "lego-update-int.sh" ''
|
||||
case "$1" in
|
||||
@@ -202,7 +200,7 @@ in
|
||||
"*.s3.${pubDomain}"
|
||||
];
|
||||
dnsProvider = "cloudflare";
|
||||
environmentFile = config.age.secrets."middleman/cloudflare-credentials.conf".path;
|
||||
credentialsFile = config.age.secrets."middleman/cloudflare-credentials.conf".path;
|
||||
postRun =
|
||||
let
|
||||
sshKey = config.age.secrets."middleman/mailcow-ssh.key".path;
|
||||
@@ -241,9 +239,6 @@ in
|
||||
];
|
||||
|
||||
recommendedTlsSettings = true;
|
||||
recommendedBrotliSettings = true;
|
||||
# Uh so nginx is hanging with zstd enabled... maybe let's not for now
|
||||
# recommendedZstdSettings = true;
|
||||
clientMaxBodySize = "0";
|
||||
serverTokens = true;
|
||||
resolver = {
|
||||
@@ -251,10 +246,8 @@ in
|
||||
valid = "5s";
|
||||
};
|
||||
proxyResolveWhileRunning = true;
|
||||
sslDhparam = config.age.secrets."dhparams.pem".path;
|
||||
|
||||
appendConfig = ''
|
||||
worker_processes auto;
|
||||
'';
|
||||
# Based on recommended*Settings, but probably better to be explicit about these
|
||||
appendHttpConfig = ''
|
||||
${baseHttpConfig}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
let
|
||||
inherit (builtins) mapAttrs toJSON;
|
||||
inherit (lib) mkMerge mkDefault genAttrs flatten concatStringsSep;
|
||||
inherit (lib.my.c) pubDomain home;
|
||||
inherit (lib.my.c) pubDomain;
|
||||
inherit (lib.my.c.nginx) proxyHeaders;
|
||||
inherit (config.networking) domain;
|
||||
|
||||
@@ -35,6 +35,7 @@ let
|
||||
# For clients
|
||||
(mkWellKnown "matrix/client" (toJSON {
|
||||
"m.homeserver".base_url = "https://matrix.nul.ie";
|
||||
"org.matrix.msc3575.proxy".url = "https://matrix-syncv3.nul.ie";
|
||||
}))
|
||||
];
|
||||
};
|
||||
@@ -49,7 +50,6 @@ let
|
||||
"/.well-known/webfinger".return = "301 https://toot.nul.ie$request_uri";
|
||||
"/.well-known/nodeinfo".return = "301 https://toot.nul.ie$request_uri";
|
||||
"/.well-known/host-meta".return = "301 https://toot.nul.ie$request_uri";
|
||||
"/.well-known/atproto-did".return = "301 https://pds.nul.ie$request_uri";
|
||||
};
|
||||
in
|
||||
{
|
||||
@@ -80,10 +80,6 @@ in
|
||||
sha256 = "018wh6ps19n7323fi44njzj9yd4wqslc90dykbwfyscv7bgxhlar";
|
||||
};
|
||||
}
|
||||
{
|
||||
name = "ssh.pub";
|
||||
path = lib.my.c.sshKeyFiles.me;
|
||||
}
|
||||
];
|
||||
}
|
||||
wellKnown
|
||||
@@ -149,7 +145,7 @@ in
|
||||
|
||||
"pass.${pubDomain}" =
|
||||
let
|
||||
upstream = "http://vaultwarden-ctr.${domain}:8080";
|
||||
upstream = "http://vaultwarden-ctr.${domain}";
|
||||
in
|
||||
{
|
||||
locations = {
|
||||
@@ -186,6 +182,10 @@ in
|
||||
];
|
||||
useACMEHost = pubDomain;
|
||||
};
|
||||
"matrix-syncv3.${pubDomain}" = {
|
||||
locations."/".proxyPass = "http://chatterbox-ctr.${domain}:8009";
|
||||
useACMEHost = pubDomain;
|
||||
};
|
||||
|
||||
"element.${pubDomain}" =
|
||||
let
|
||||
@@ -206,8 +206,7 @@ in
|
||||
# Currently it seems like single quotes aren't escaped like they should be...
|
||||
conf = {
|
||||
brand = "/dev/player0 Matrix";
|
||||
show_labs_settings = true;
|
||||
default_country_code = "IE";
|
||||
showLabsSettings = true;
|
||||
disable_guests = true;
|
||||
default_server_config = {
|
||||
"m.homeserver" = {
|
||||
@@ -215,8 +214,9 @@ in
|
||||
server_name = "nul.ie";
|
||||
};
|
||||
};
|
||||
room_directory.servers = [
|
||||
roomDirectory.servers = [
|
||||
"nul.ie"
|
||||
"netsoc.ie"
|
||||
"matrix.org"
|
||||
];
|
||||
};
|
||||
@@ -327,15 +327,6 @@ in
|
||||
useACMEHost = pubDomain;
|
||||
};
|
||||
|
||||
"pds.nul.ie" = {
|
||||
locations."/" = {
|
||||
proxyPass = "http://toot-ctr.${domain}:3000";
|
||||
proxyWebsockets = true;
|
||||
extraConfig = proxyHeaders;
|
||||
};
|
||||
useACMEHost = pubDomain;
|
||||
};
|
||||
|
||||
"share.${pubDomain}" = {
|
||||
locations."/" = {
|
||||
proxyPass = "http://object-ctr.${domain}:9090";
|
||||
@@ -347,13 +338,16 @@ in
|
||||
|
||||
"stuff.${pubDomain}" = {
|
||||
locations."/" = {
|
||||
proxyPass = "http://jackflix-ctr.${domain}:3923";
|
||||
basicAuthFile = config.age.secrets."middleman/htpasswd".path;
|
||||
root = "/mnt/media/stuff";
|
||||
extraConfig = ''
|
||||
fancyindex on;
|
||||
fancyindex_show_dotfiles on;
|
||||
'';
|
||||
};
|
||||
useACMEHost = pubDomain;
|
||||
};
|
||||
"public.${pubDomain}" = {
|
||||
onlySSL = false;
|
||||
addSSL = true;
|
||||
serverAliases = [ "p.${pubDomain}" ];
|
||||
locations."/" = {
|
||||
root = "/mnt/media/public";
|
||||
@@ -370,12 +364,7 @@ in
|
||||
useACMEHost = pubDomain;
|
||||
};
|
||||
"mc-rail.${pubDomain}" = {
|
||||
locations."/".proxyPass = "http://simpcraft-oci.${domain}:3876";
|
||||
useACMEHost = pubDomain;
|
||||
};
|
||||
|
||||
"mc-map-kink.${pubDomain}" = {
|
||||
locations."/".proxyPass = "http://kinkcraft-oci.${domain}:8100";
|
||||
locations."/".proxyPass = "http://simpcraft-staging-oci.${domain}:3876";
|
||||
useACMEHost = pubDomain;
|
||||
};
|
||||
|
||||
@@ -395,56 +384,6 @@ in
|
||||
};
|
||||
useACMEHost = pubDomain;
|
||||
};
|
||||
"pb.${pubDomain}" = {
|
||||
locations."/".proxyPass = "http://object-ctr.${domain}:8088";
|
||||
useACMEHost = pubDomain;
|
||||
};
|
||||
"photos.${pubDomain}" = {
|
||||
locations."/" = {
|
||||
proxyPass = "http://jackflix-ctr.${domain}:2342";
|
||||
proxyWebsockets = true;
|
||||
extraConfig = proxyHeaders;
|
||||
};
|
||||
useACMEHost = pubDomain;
|
||||
};
|
||||
|
||||
"pront.${pubDomain}" = mkMerge [
|
||||
{
|
||||
locations."/" = mkMerge [
|
||||
{
|
||||
proxyPass = "http://stream-hi.${home.domain}:5000";
|
||||
proxyWebsockets = true;
|
||||
extraConfig = proxyHeaders;
|
||||
}
|
||||
(ssoLoc "generic")
|
||||
];
|
||||
locations."~* ^/webcam/(.*)" = mkMerge [
|
||||
{
|
||||
proxyPass = "http://stream-hi.${home.domain}:5050/$1$is_args$args";
|
||||
extraConfig = proxyHeaders;
|
||||
}
|
||||
(ssoLoc "generic")
|
||||
];
|
||||
useACMEHost = pubDomain;
|
||||
}
|
||||
(ssoServer "generic")
|
||||
];
|
||||
"hass.${pubDomain}" = {
|
||||
locations."/" = {
|
||||
proxyPass = "http://hass-ctr.${home.domain}:8123";
|
||||
proxyWebsockets = true;
|
||||
extraConfig = proxyHeaders;
|
||||
};
|
||||
useACMEHost = pubDomain;
|
||||
};
|
||||
"hass-john.${pubDomain}" = {
|
||||
locations."/" = {
|
||||
proxyPass = "http://john-valorant-tun.${domain}:8123";
|
||||
proxyWebsockets = true;
|
||||
extraConfig = proxyHeaders;
|
||||
};
|
||||
useACMEHost = pubDomain;
|
||||
};
|
||||
};
|
||||
|
||||
minio =
|
||||
@@ -456,13 +395,10 @@ in
|
||||
ignore_invalid_headers off;
|
||||
'';
|
||||
|
||||
nixCacheableRegex = ''^\/(\S+\.narinfo|nar\/\S+\.nar.*|serve\/.+)$'';
|
||||
nixCacheableRegex = ''^\/(\S+\.narinfo|nar\/\S+\.nar\.\S+)$'';
|
||||
nixCacheHeaders = ''
|
||||
add_header Cache-Control $nix_cache_control;
|
||||
add_header Expires $nix_expires;
|
||||
|
||||
brotli on;
|
||||
brotli_types application/x-nix-archive;
|
||||
'';
|
||||
in
|
||||
{
|
||||
@@ -504,11 +440,9 @@ in
|
||||
|
||||
"nix-cache.${pubDomain}" = {
|
||||
locations = {
|
||||
"/" = {
|
||||
proxyPass = "http://${host}:5000";
|
||||
};
|
||||
"/".proxyPass = "http://${host}:8069";
|
||||
"~ ${nixCacheableRegex}" = {
|
||||
proxyPass = "http://${host}:5000";
|
||||
proxyPass = "http://${host}:8069";
|
||||
extraConfig = nixCacheHeaders;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -31,14 +31,6 @@ in
|
||||
{
|
||||
config = mkMerge [
|
||||
{
|
||||
fileSystems = {
|
||||
"/var/lib/harmonia" = {
|
||||
device = "/mnt/nix-cache";
|
||||
options = [ "bind" ];
|
||||
fsType = "none";
|
||||
};
|
||||
};
|
||||
|
||||
my = {
|
||||
deploy.enable = false;
|
||||
server.enable = true;
|
||||
@@ -56,9 +48,7 @@ in
|
||||
group = config.my.user.config.group;
|
||||
};
|
||||
"object/atticd.env" = {};
|
||||
"nix-cache.key" = {};
|
||||
"object/hedgedoc.env" = {};
|
||||
"object/wastebin.env" = {};
|
||||
};
|
||||
};
|
||||
|
||||
@@ -67,9 +57,7 @@ in
|
||||
9000 9001
|
||||
config.services.sharry.config.bind.port
|
||||
8069
|
||||
5000
|
||||
config.services.hedgedoc.settings.port
|
||||
8088
|
||||
];
|
||||
};
|
||||
|
||||
@@ -78,31 +66,14 @@ in
|
||||
};
|
||||
};
|
||||
|
||||
users = with lib.my.c.ids; mkMerge [
|
||||
(let inherit (config.services.atticd) user group; in {
|
||||
users."${user}" = {
|
||||
isSystemUser = true;
|
||||
uid = uids.atticd;
|
||||
group = group;
|
||||
};
|
||||
groups."${user}".gid = gids.atticd;
|
||||
})
|
||||
{
|
||||
users = {
|
||||
harmonia = {
|
||||
isSystemUser = true;
|
||||
group = "harmonia";
|
||||
shell = pkgs.bashInteractive;
|
||||
openssh.authorizedKeys.keyFiles = [
|
||||
lib.my.c.sshKeyFiles.harmonia
|
||||
];
|
||||
};
|
||||
};
|
||||
groups = {
|
||||
harmonia = { };
|
||||
};
|
||||
}
|
||||
];
|
||||
users = with lib.my.c.ids; let inherit (config.services.atticd) user group; in {
|
||||
users."${user}" = {
|
||||
isSystemUser = true;
|
||||
uid = uids.atticd;
|
||||
group = group;
|
||||
};
|
||||
groups."${user}".gid = gids.atticd;
|
||||
};
|
||||
|
||||
systemd = {
|
||||
network.networks."80-container-host0" = networkdAssignment "host0" assignments.internal;
|
||||
@@ -120,9 +91,7 @@ in
|
||||
MINIO_BROWSER_REDIRECT_URL = "https://minio.nul.ie";
|
||||
};
|
||||
};
|
||||
|
||||
sharry = awaitPostgres;
|
||||
|
||||
atticd = mkMerge [
|
||||
awaitPostgres
|
||||
{
|
||||
@@ -133,26 +102,6 @@ in
|
||||
};
|
||||
}
|
||||
];
|
||||
|
||||
harmonia-dev = {
|
||||
# environment.RUST_LOG = mkForce "trace";
|
||||
# serviceConfig = {
|
||||
# StateDirectory = "harmonia";
|
||||
# DynamicUser = mkForce false;
|
||||
# };
|
||||
};
|
||||
harmonia-daemon = {
|
||||
# environment.RUST_LOG = mkForce "trace";
|
||||
preStart = ''
|
||||
${config.nix.package}/bin/nix store info --store /var/lib/harmonia
|
||||
'';
|
||||
serviceConfig = {
|
||||
User = "harmonia";
|
||||
Group = "harmonia";
|
||||
StateDirectory = "harmonia";
|
||||
DynamicUser = mkForce false;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
@@ -162,9 +111,6 @@ in
|
||||
];
|
||||
};
|
||||
|
||||
# TODO/FIXME: this is bad...
|
||||
nixpkgs.config.permittedInsecurePackages = [ "minio-2025-10-15T17-29-55Z" ];
|
||||
|
||||
services = {
|
||||
minio = {
|
||||
enable = true;
|
||||
@@ -235,8 +181,8 @@ in
|
||||
};
|
||||
|
||||
atticd = {
|
||||
enable = false;
|
||||
environmentFile = config.age.secrets."object/atticd.env".path;
|
||||
enable = true;
|
||||
credentialsFile = config.age.secrets."object/atticd.env".path;
|
||||
settings = {
|
||||
listen = "[::]:8069";
|
||||
allowed-hosts = [ "nix-cache.${pubDomain}" ];
|
||||
@@ -255,23 +201,6 @@ in
|
||||
};
|
||||
};
|
||||
|
||||
harmonia-dev = {
|
||||
daemon = {
|
||||
enable = true;
|
||||
storeDir = "/nix/store";
|
||||
dbPath = "/var/lib/harmonia/nix/var/nix/db/db.sqlite";
|
||||
};
|
||||
cache = {
|
||||
enable = true;
|
||||
signKeyPaths = [ config.age.secrets."nix-cache.key".path ];
|
||||
settings = {
|
||||
priority = 30;
|
||||
virtual_nix_store = "/nix/store";
|
||||
real_nix_store = "/var/lib/harmonia/nix/store";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
hedgedoc = {
|
||||
enable = true;
|
||||
environmentFile = config.age.secrets."object/hedgedoc.env".path;
|
||||
@@ -291,15 +220,6 @@ in
|
||||
allowEmailRegister = false;
|
||||
};
|
||||
};
|
||||
|
||||
wastebin = {
|
||||
enable = true;
|
||||
settings = {
|
||||
WASTEBIN_MAX_BODY_SIZE = 67108864; # 16 MiB
|
||||
WASTEBIN_PASSWORD_SALT = "TeGhaemeer0Siez3";
|
||||
};
|
||||
secretFile = config.age.secrets."object/wastebin.env".path;
|
||||
};
|
||||
};
|
||||
}
|
||||
(mkIf config.my.build.isDevVM {
|
||||
|
||||
@@ -1,115 +0,0 @@
|
||||
{ lib, ... }:
|
||||
let
|
||||
inherit (lib.my) net;
|
||||
inherit (lib.my.c.colony) domain prefixes qclk;
|
||||
in
|
||||
{
|
||||
nixos.systems.qclk = { config, ... }: {
|
||||
system = "x86_64-linux";
|
||||
nixpkgs = "mine";
|
||||
rendered = config.configuration.config.my.asContainer;
|
||||
|
||||
assignments = {
|
||||
internal = {
|
||||
name = "qclk-ctr";
|
||||
inherit domain;
|
||||
ipv4.address = net.cidr.host 10 prefixes.ctrs.v4;
|
||||
ipv6 = {
|
||||
iid = "::a";
|
||||
address = net.cidr.host 10 prefixes.ctrs.v6;
|
||||
};
|
||||
};
|
||||
qclk = {
|
||||
ipv4 = {
|
||||
address = net.cidr.host 1 prefixes.qclk.v4;
|
||||
gateway = null;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
configuration = { lib, pkgs, config, assignments, ... }:
|
||||
let
|
||||
inherit (lib) concatStringsSep mkMerge mkIf mkForce;
|
||||
inherit (lib.my) networkdAssignment;
|
||||
|
||||
apiPort = 8080;
|
||||
|
||||
instances = [
|
||||
{
|
||||
host = 2;
|
||||
wgKey = "D7z1FhcdxpnrGCE0wBW5PZb5BKuhCu6tcZ/5ZaYxdwQ=";
|
||||
}
|
||||
];
|
||||
ipFor = i: net.cidr.host i.host prefixes.qclk.v4;
|
||||
in
|
||||
{
|
||||
config = {
|
||||
environment = {
|
||||
systemPackages = with pkgs; [
|
||||
wireguard-tools
|
||||
];
|
||||
};
|
||||
|
||||
my = {
|
||||
deploy.enable = false;
|
||||
server.enable = true;
|
||||
|
||||
secrets = {
|
||||
key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIC1kcfvahYmSk8IJKaUIcGkhxf/8Yse2XnU7Qqgcglyq";
|
||||
files = {
|
||||
"qclk/wg.key" = {
|
||||
group = "systemd-network";
|
||||
mode = "440";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
firewall = {
|
||||
udp.allowed = [ qclk.wgPort ];
|
||||
extraRules = ''
|
||||
table inet filter {
|
||||
chain input {
|
||||
iifname management tcp dport ${toString apiPort} accept
|
||||
}
|
||||
chain forward {
|
||||
iifname host0 oifname management ip saddr { ${concatStringsSep ", " lib.my.c.as211024.trusted.v4} } accept
|
||||
}
|
||||
}
|
||||
table inet nat {
|
||||
chain postrouting {
|
||||
iifname host0 oifname management snat ip to ${assignments.qclk.ipv4.address}
|
||||
}
|
||||
}
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
systemd = {
|
||||
network = {
|
||||
netdevs."30-management" = {
|
||||
netdevConfig = {
|
||||
Name = "management";
|
||||
Kind = "wireguard";
|
||||
};
|
||||
wireguardConfig = {
|
||||
PrivateKeyFile = config.age.secrets."qclk/wg.key".path;
|
||||
ListenPort = qclk.wgPort;
|
||||
};
|
||||
wireguardPeers = map (i: {
|
||||
PublicKey = i.wgKey;
|
||||
AllowedIPs = [ (ipFor i) ];
|
||||
}) instances;
|
||||
};
|
||||
networks = {
|
||||
"30-container-host0" = networkdAssignment "host0" assignments.internal;
|
||||
|
||||
"30-management" = networkdAssignment "management" assignments.qclk;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
services = { };
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -26,8 +26,6 @@ in
|
||||
let
|
||||
inherit (lib) mkMerge mkIf genAttrs;
|
||||
inherit (lib.my) networkdAssignment systemdAwaitPostgres;
|
||||
|
||||
pdsPort = 3000;
|
||||
in
|
||||
{
|
||||
config = mkMerge [
|
||||
@@ -38,7 +36,7 @@ in
|
||||
|
||||
secrets = {
|
||||
key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILSslLkDe54AKYzxdtKD70zcU72W0EpYsfbdJ6UFq0QK";
|
||||
files = (genAttrs
|
||||
files = genAttrs
|
||||
(map (f: "toot/${f}") [
|
||||
"postgres-password.txt"
|
||||
"secret-key.txt"
|
||||
@@ -50,12 +48,7 @@ in
|
||||
(_: with config.services.mastodon; {
|
||||
owner = user;
|
||||
inherit group;
|
||||
})) // {
|
||||
"toot/pds.env" = {
|
||||
owner = "pds";
|
||||
group = "pds";
|
||||
};
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
firewall = {
|
||||
@@ -63,7 +56,6 @@ in
|
||||
19999
|
||||
|
||||
"http"
|
||||
pdsPort
|
||||
];
|
||||
};
|
||||
};
|
||||
@@ -87,7 +79,7 @@ in
|
||||
netdata.enable = true;
|
||||
mastodon = mkMerge [
|
||||
rec {
|
||||
enable = false;
|
||||
enable = true;
|
||||
localDomain = extraConfig.WEB_DOMAIN; # for nginx config
|
||||
extraConfig = {
|
||||
LOCAL_DOMAIN = "nul.ie";
|
||||
@@ -95,9 +87,7 @@ in
|
||||
};
|
||||
|
||||
secretKeyBaseFile = config.age.secrets."toot/secret-key.txt".path;
|
||||
# TODO: This was removed at some point.
|
||||
# If we want to bring Mastodon back, this will probably need to be addressd.
|
||||
# otpSecretFile = config.age.secrets."toot/otp-secret.txt".path;
|
||||
otpSecretFile = config.age.secrets."toot/otp-secret.txt".path;
|
||||
vapidPrivateKeyFile = config.age.secrets."toot/vapid-key.txt".path;
|
||||
vapidPublicKeyFile = toString (pkgs.writeText
|
||||
"vapid-pubkey.txt"
|
||||
@@ -165,32 +155,6 @@ in
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
bluesky-pds = {
|
||||
enable = true;
|
||||
environmentFiles = [ config.age.secrets."toot/pds.env".path ];
|
||||
settings = {
|
||||
PDS_HOSTNAME = "pds.nul.ie";
|
||||
PDS_PORT = pdsPort;
|
||||
|
||||
PDS_BLOBSTORE_DISK_LOCATION = null;
|
||||
PDS_BLOBSTORE_S3_BUCKET = "pds";
|
||||
PDS_BLOBSTORE_S3_ENDPOINT = "https://s3.nul.ie/";
|
||||
PDS_BLOBSTORE_S3_REGION = "eu-central-1";
|
||||
PDS_BLOBSTORE_S3_ACCESS_KEY_ID = "pds";
|
||||
PDS_BLOB_UPLOAD_LIMIT = "52428800";
|
||||
|
||||
PDS_EMAIL_FROM_ADDRESS = "pds@nul.ie";
|
||||
|
||||
PDS_DID_PLC_URL = "https://plc.directory";
|
||||
PDS_INVITE_REQUIRED = "true";
|
||||
PDS_BSKY_APP_VIEW_URL = "https://api.bsky.app";
|
||||
PDS_BSKY_APP_VIEW_DID = "did:web:api.bsky.app";
|
||||
PDS_REPORT_SERVICE_URL = "https://mod.bsky.app";
|
||||
PDS_REPORT_SERVICE_DID = "did:plc:ar7c4by46qjdydhdevvrndac";
|
||||
PDS_CRAWLERS = "https://bsky.network";
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
(mkIf config.my.build.isDevVM {
|
||||
|
||||
@@ -83,7 +83,7 @@ in
|
||||
DOMAIN = "https://pass.${lib.my.c.pubDomain}";
|
||||
|
||||
ROCKET_ADDRESS = "::";
|
||||
ROCKET_PORT = 8080;
|
||||
ROCKET_PORT = 80;
|
||||
|
||||
SMTP_HOST = "mail.nul.ie";
|
||||
SMTP_FROM = "pass@nul.ie";
|
||||
@@ -99,8 +99,6 @@ in
|
||||
};
|
||||
|
||||
borgbackup.jobs.vaultwarden = {
|
||||
readWritePaths = [ "/var/lib/borgbackup" "/var/cache/borgbackup" ];
|
||||
|
||||
paths = [ vwData ];
|
||||
repo = "zh2855@zh2855.rsync.net:borg/vaultwarden2";
|
||||
doInit = true;
|
||||
|
||||
@@ -86,7 +86,7 @@ in
|
||||
interfaceName = "tailscale0";
|
||||
extraUpFlags = [
|
||||
"--operator=${config.my.user.config.name}"
|
||||
"--login-server=https://hs.nul.ie"
|
||||
"--login-server=https://ts.nul.ie"
|
||||
"--netfilter-mode=off"
|
||||
"--advertise-exit-node"
|
||||
"--advertise-routes=${advRoutes}"
|
||||
|
||||
@@ -49,11 +49,7 @@ in
|
||||
inherit (lib.my) networkdAssignment;
|
||||
in
|
||||
{
|
||||
imports = [
|
||||
"${modulesPath}/profiles/qemu-guest.nix"
|
||||
|
||||
./containers-ext.nix
|
||||
];
|
||||
imports = [ "${modulesPath}/profiles/qemu-guest.nix" ];
|
||||
|
||||
config = mkMerge [
|
||||
{
|
||||
@@ -94,8 +90,8 @@ in
|
||||
device = "/dev/disk/by-label/minio";
|
||||
fsType = "xfs";
|
||||
};
|
||||
"/mnt/nix-cache" = {
|
||||
device = "/dev/disk/by-label/nix-cache";
|
||||
"/mnt/atticd" = {
|
||||
device = "/dev/disk/by-label/atticd";
|
||||
fsType = "ext4";
|
||||
};
|
||||
};
|
||||
@@ -140,10 +136,10 @@ in
|
||||
};
|
||||
ipv6Prefixes = [
|
||||
{
|
||||
Prefix = prefixes.ctrs.v6;
|
||||
ipv6PrefixConfig.Prefix = prefixes.ctrs.v6;
|
||||
}
|
||||
];
|
||||
routes = [
|
||||
routes = map (r: { routeConfig = r; }) [
|
||||
{
|
||||
Destination = lib.my.c.tailscale.prefix.v4;
|
||||
Gateway = allAssignments.waffletail.internal.ipv4.address;
|
||||
@@ -152,11 +148,6 @@ in
|
||||
Destination = lib.my.c.tailscale.prefix.v6;
|
||||
Gateway = allAssignments.waffletail.internal.ipv6.address;
|
||||
}
|
||||
|
||||
{
|
||||
Destination = prefixes.qclk.v4;
|
||||
Gateway = allAssignments.qclk.internal.ipv4.address;
|
||||
}
|
||||
];
|
||||
}
|
||||
];
|
||||
@@ -211,13 +202,11 @@ in
|
||||
object = {
|
||||
bindMounts = {
|
||||
"/mnt/minio".readOnly = false;
|
||||
"/mnt/nix-cache".readOnly = false;
|
||||
"/mnt/atticd".readOnly = false;
|
||||
};
|
||||
};
|
||||
toot = {};
|
||||
waffletail = {};
|
||||
qclk = {};
|
||||
gam = {};
|
||||
};
|
||||
in
|
||||
mkMerge [
|
||||
|
||||
@@ -52,10 +52,6 @@ in
|
||||
valheim-oci = 2;
|
||||
simpcraft-oci = 3;
|
||||
simpcraft-staging-oci = 4;
|
||||
enshrouded-oci = 5;
|
||||
kevcraft-oci = 6;
|
||||
kinkcraft-oci = 7;
|
||||
graeme-oci = 8;
|
||||
};
|
||||
|
||||
configuration = { lib, pkgs, modulesPath, config, assignments, allAssignments, ... }:
|
||||
@@ -70,7 +66,6 @@ in
|
||||
|
||||
./valheim.nix
|
||||
./minecraft
|
||||
# ./enshrouded.nix
|
||||
];
|
||||
|
||||
config = mkMerge [
|
||||
@@ -113,30 +108,45 @@ in
|
||||
oci-containers = {
|
||||
backend = "podman";
|
||||
};
|
||||
containers.containersConf.settings.network = {
|
||||
network_backend = "netavark";
|
||||
firewall_driver = mkForce "none";
|
||||
};
|
||||
# NixOS has switched to using netavark, which is native to podman. It's currently missing an option to
|
||||
# disable iptables rules generation, which is very annoying.
|
||||
containers.containersConf.settings.network.network_backend = mkForce "cni";
|
||||
};
|
||||
|
||||
environment = {
|
||||
etc = {
|
||||
"containers/networks/colony.json".text = toJSON {
|
||||
"cni/net.d/90-colony.conflist".text = toJSON {
|
||||
cniVersion = "0.4.0";
|
||||
name = "colony";
|
||||
id = "0000000000000000000000000000000000000000000000000000000000000001";
|
||||
driver = "bridge";
|
||||
network_interface = "oci";
|
||||
ipv6_enabled = true;
|
||||
internal = false;
|
||||
dns_enabled = false;
|
||||
subnets = [
|
||||
plugins = [
|
||||
{
|
||||
subnet = prefixes.oci.v4;
|
||||
gateway = net.cidr.host 1 prefixes.oci.v4;
|
||||
}
|
||||
{
|
||||
subnet = prefixes.oci.v6;
|
||||
gateway = net.cidr.host 1 prefixes.oci.v6;
|
||||
type = "bridge";
|
||||
bridge = "oci";
|
||||
isGateway = true;
|
||||
ipMasq = false;
|
||||
hairpinMode = true;
|
||||
ipam = {
|
||||
type = "host-local";
|
||||
routes = [
|
||||
{ dst = "0.0.0.0/0"; }
|
||||
{ dst = "::/0"; }
|
||||
];
|
||||
ranges = [
|
||||
[
|
||||
{
|
||||
subnet = prefixes.oci.v4;
|
||||
gateway = net.cidr.host 1 prefixes.oci.v4;
|
||||
}
|
||||
]
|
||||
[
|
||||
{
|
||||
subnet = prefixes.oci.v6;
|
||||
gateway = net.cidr.host 1 prefixes.oci.v6;
|
||||
}
|
||||
]
|
||||
];
|
||||
};
|
||||
capabilities.ips = true;
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
{ lib, config, allAssignments, ... }:
|
||||
let
|
||||
inherit (lib) concatStringsSep;
|
||||
inherit (lib.my) dockerNetAssignment;
|
||||
in
|
||||
{
|
||||
config = {
|
||||
virtualisation.oci-containers.containers = {
|
||||
enshrouded = {
|
||||
image = "sknnr/enshrouded-dedicated-server@sha256:f163e8ba9caa2115d8a0a7b16c3696968242fb6fba82706d9a77a882df083497";
|
||||
|
||||
environment = {
|
||||
SERVER_NAME = "UWUshrouded";
|
||||
# SERVER_IP = "::"; # no IPv6?? :(
|
||||
TZ = "Europe/Dublin";
|
||||
};
|
||||
environmentFiles = [ config.age.secrets."whale2/enshrouded.env".path ];
|
||||
|
||||
volumes = [
|
||||
"enshrouded:/home/steam/enshrouded/savegame"
|
||||
];
|
||||
|
||||
extraOptions = [
|
||||
''--network=colony:${dockerNetAssignment allAssignments "enshrouded-oci"}''
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
my = {
|
||||
secrets.files = {
|
||||
"whale2/enshrouded.env" = {};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -5,13 +5,12 @@ let
|
||||
|
||||
# devplayer0
|
||||
op = "6d7d971b-ce10-435b-85c5-c99c0d8d288c";
|
||||
kev = "703b378a-09f9-4c1d-9876-1c9305728c49";
|
||||
whitelist = concatStringsSep "," [
|
||||
op
|
||||
"dcd2ecb9-2b5e-49cb-9d4f-f5a76162df56" # Elderlypug
|
||||
"fcb26db2-c3ce-41aa-b588-efec79d37a8a" # Jesthral_
|
||||
"1d366062-12c0-4e29-aba7-6ab5d8c6bb05" # shr3kas0ras
|
||||
kev
|
||||
"703b378a-09f9-4c1d-9876-1c9305728c49" # OROURKEIRE
|
||||
"f105bbe6-eda6-4a13-a8cf-894e77cab77b" # Adzerq
|
||||
"1fc94979-41fb-497a-81e9-34ae24ca537a" # johnnyscrims
|
||||
"d53c91df-b6e6-4463-b106-e8427d7a8d01" # BossLonus
|
||||
@@ -26,20 +25,22 @@ let
|
||||
email = "simpcraft@nul.ie"
|
||||
name = "Simpcraft bot"
|
||||
'';
|
||||
knownHosts = pkgs.writeText "known_hosts" ''
|
||||
git.nul.ie ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBD023ECzYmLeXIpcGVaciPjq6UN/Sjmsys5HP/Nei5GkrUZqPa3OJ2uSXKLUSKGYdeNhxaFTPJe8Yx3TsZxMme8=
|
||||
'';
|
||||
};
|
||||
in
|
||||
{
|
||||
config = {
|
||||
virtualisation.oci-containers.containers = {
|
||||
simpcraft = {
|
||||
image = "git.nul.ie/dev/craftblock:2024.1.0-java17-alpine";
|
||||
image = "ghcr.io/itzg/minecraft-server:2023.12.2-java17-alpine";
|
||||
|
||||
environment = {
|
||||
TYPE = "MODRINTH";
|
||||
|
||||
EULA = "true";
|
||||
ENABLE_QUERY = "true";
|
||||
ENABLE_RCON = "true";
|
||||
MOTD = "§4§k----- §9S§ai§bm§cp§dc§er§fa§6f§5t §4§k-----";
|
||||
ICON = "/ext/icon.png";
|
||||
|
||||
@@ -51,17 +52,15 @@ in
|
||||
SPAWN_PROTECTION = "0";
|
||||
VIEW_DISTANCE = "20";
|
||||
|
||||
MAX_MEMORY = "8G";
|
||||
MODRINTH_MODPACK = "https://cdn.modrinth.com/data/CIYf3Hk8/versions/NGutsQSd/Simpcraft-0.2.1.mrpack";
|
||||
MAX_MEMORY = "6G";
|
||||
MODRINTH_MODPACK = "https://cdn.modrinth.com/data/CIYf3Hk8/versions/cdj2bSKg/Simpcraft-0.1.2.mrpack";
|
||||
|
||||
TZ = "Europe/Dublin";
|
||||
};
|
||||
environmentFiles = [ config.age.secrets."whale2/simpcraft.env".path ];
|
||||
|
||||
volumes = [
|
||||
"minecraft_data:/data"
|
||||
"${./icon.png}:/ext/icon.png:ro"
|
||||
"${fastback.gitConfig}:/data/.config/git/config:ro"
|
||||
];
|
||||
|
||||
extraOptions = [
|
||||
@@ -69,96 +68,16 @@ in
|
||||
];
|
||||
};
|
||||
|
||||
# simpcraft-staging = {
|
||||
# image = "git.nul.ie/dev/craftblock:2024.1.0-java17-alpine";
|
||||
|
||||
# environment = {
|
||||
# TYPE = "MODRINTH";
|
||||
|
||||
# EULA = "true";
|
||||
# ENABLE_QUERY = "true";
|
||||
# ENABLE_RCON = "true";
|
||||
# MOTD = "§4§k----- §9S§ai§bm§cp§dc§er§fa§6f§5t [staging] §4§k-----";
|
||||
# ICON = "/ext/icon.png";
|
||||
|
||||
# EXISTING_WHITELIST_FILE = "SYNCHRONIZE";
|
||||
# WHITELIST = whitelist;
|
||||
# EXISTING_OPS_FILE = "SYNCHRONIZE";
|
||||
# OPS = op;
|
||||
# DIFFICULTY = "normal";
|
||||
# SPAWN_PROTECTION = "0";
|
||||
# VIEW_DISTANCE = "20";
|
||||
|
||||
# MAX_MEMORY = "4G";
|
||||
# MODRINTH_MODPACK = "https://cdn.modrinth.com/data/CIYf3Hk8/versions/Ym3sIi6H/Simpcraft-0.2.0.mrpack";
|
||||
|
||||
# TZ = "Europe/Dublin";
|
||||
# };
|
||||
# environmentFiles = [ config.age.secrets."whale2/simpcraft.env".path ];
|
||||
|
||||
# volumes = [
|
||||
# "minecraft_staging_data:/data"
|
||||
# "${./icon.png}:/ext/icon.png:ro"
|
||||
# ];
|
||||
|
||||
# extraOptions = [
|
||||
# ''--network=colony:${dockerNetAssignment allAssignments "simpcraft-staging-oci"}''
|
||||
# ];
|
||||
# };
|
||||
|
||||
kevcraft = {
|
||||
# 2025.2.1-java21-alpine
|
||||
image = "itzg/minecraft-server@sha256:57e319c15e9fee63f61029a65a33acc3de85118b21a2b4bb29f351cf4a915027";
|
||||
|
||||
environment = {
|
||||
TYPE = "VANILLA";
|
||||
VERSION = "1.20.1";
|
||||
SERVER_PORT = "25567";
|
||||
QUERY_PORT = "25567";
|
||||
|
||||
EULA = "true";
|
||||
ENABLE_QUERY = "true";
|
||||
ENABLE_RCON = "true";
|
||||
MOTD = "§4§k----- §9K§ae§bv§cc§dr§ea§ff§6t §4§k-----";
|
||||
ICON = "/ext/icon.png";
|
||||
|
||||
EXISTING_WHITELIST_FILE = "SYNCHRONIZE";
|
||||
WHITELIST = whitelist;
|
||||
EXISTING_OPS_FILE = "SYNCHRONIZE";
|
||||
OPS = concatStringsSep "," [ op kev ];
|
||||
DIFFICULTY = "normal";
|
||||
SPAWN_PROTECTION = "0";
|
||||
# VIEW_DISTANCE = "20";
|
||||
|
||||
MAX_MEMORY = "4G";
|
||||
|
||||
TZ = "Europe/Dublin";
|
||||
};
|
||||
environmentFiles = [ config.age.secrets."whale2/simpcraft.env".path ];
|
||||
|
||||
volumes = [
|
||||
"kevcraft_data:/data"
|
||||
"${./kev.png}:/ext/icon.png:ro"
|
||||
];
|
||||
|
||||
extraOptions = [
|
||||
''--network=colony:${dockerNetAssignment allAssignments "kevcraft-oci"}''
|
||||
];
|
||||
};
|
||||
|
||||
kinkcraft = {
|
||||
# 2025.5.1-java21-alpine
|
||||
image = "itzg/minecraft-server@sha256:de26c7128e3935f3be48fd30283f0b5a6da1b3d9f1a10c9f92502ee1ba072f7b";
|
||||
simpcraft-staging = {
|
||||
image = "git.nul.ie/dev/craftblock:2024.1.0-java17-alpine";
|
||||
|
||||
environment = {
|
||||
TYPE = "MODRINTH";
|
||||
SERVER_PORT = "25568";
|
||||
QUERY_PORT = "25568";
|
||||
|
||||
EULA = "true";
|
||||
ENABLE_QUERY = "true";
|
||||
ENABLE_RCON = "true";
|
||||
MOTD = "§4§k----- §9K§ai§bn§ck§dc§er§fa§6f§5t §4§k-----";
|
||||
MOTD = "§4§k----- §9S§ai§bm§cp§dc§er§fa§6f§5t [staging] §4§k-----";
|
||||
ICON = "/ext/icon.png";
|
||||
|
||||
EXISTING_WHITELIST_FILE = "SYNCHRONIZE";
|
||||
@@ -169,102 +88,33 @@ in
|
||||
SPAWN_PROTECTION = "0";
|
||||
VIEW_DISTANCE = "20";
|
||||
|
||||
MAX_MEMORY = "6G";
|
||||
MODRINTH_MODPACK = "https://cdn.modrinth.com/data/CIYf3Hk8/versions/NGutsQSd/Simpcraft-0.2.1.mrpack";
|
||||
MAX_MEMORY = "4G";
|
||||
MODRINTH_MODPACK = "https://cdn.modrinth.com/data/CIYf3Hk8/versions/Ym3sIi6H/Simpcraft-0.2.0.mrpack";
|
||||
|
||||
TZ = "Europe/Dublin";
|
||||
};
|
||||
environmentFiles = [ config.age.secrets."whale2/simpcraft.env".path ];
|
||||
|
||||
volumes = [
|
||||
"kinkcraft_data:/data"
|
||||
"minecraft_staging_data:/data"
|
||||
"${./icon.png}:/ext/icon.png:ro"
|
||||
"${fastback.gitConfig}:/data/.config/git/config:ro"
|
||||
"${fastback.knownHosts}:/data/.ssh/known_hosts:ro"
|
||||
"${config.age.secrets."whale2/simpcraft-git.key".path}:/data/.ssh/id_rsa"
|
||||
];
|
||||
|
||||
extraOptions = [
|
||||
''--network=colony:${dockerNetAssignment allAssignments "kinkcraft-oci"}''
|
||||
''--network=colony:${dockerNetAssignment allAssignments "simpcraft-staging-oci"}''
|
||||
];
|
||||
};
|
||||
|
||||
graeme = {
|
||||
# 2026.2.1-java21-alpine
|
||||
image = "itzg/minecraft-server@sha256:82adaddfe0156f07c34228f1c1065cdbd298abc174de0a9961abb068b11beebb";
|
||||
|
||||
environment = {
|
||||
TYPE = "VANILLA";
|
||||
SERVER_PORT = "25569";
|
||||
QUERY_PORT = "25569";
|
||||
|
||||
EULA = "true";
|
||||
ENABLE_QUERY = "true";
|
||||
ENABLE_RCON = "false";
|
||||
MOTD = "§4§k----- §9G§ar§ba§ce§dm§ee §4§k-----";
|
||||
ICON = "/ext/icon.png";
|
||||
|
||||
EXISTING_WHITELIST_FILE = "SYNCHRONIZE";
|
||||
WHITELIST = concatStringsSep "," [
|
||||
op
|
||||
"fffa146c-0bc8-421c-9e3a-3635c0aca2ea" # Scarlehh
|
||||
"1ea05f48-76cc-4034-bcd3-2fa1fc5a7375" # Dario
|
||||
"4bf837b1-01db-4491-a0e0-700d98542833" # JoeSpencer
|
||||
"d07a9554-1b05-4b0b-b558-27e4a86e1f53" # AmyClover
|
||||
];
|
||||
EXISTING_OPS_FILE = "SYNCHRONIZE";
|
||||
OPS = op;
|
||||
DIFFICULTY = "hard";
|
||||
SPAWN_PROTECTION = "0";
|
||||
VIEW_DISTANCE = "20";
|
||||
|
||||
MAX_MEMORY = "4G";
|
||||
|
||||
TZ = "Europe/Dublin";
|
||||
};
|
||||
|
||||
volumes = [
|
||||
"graeme_data:/data"
|
||||
"${./graeme.png}:/ext/icon.png:ro"
|
||||
];
|
||||
|
||||
extraOptions = [
|
||||
''--network=colony:${dockerNetAssignment allAssignments "graeme-oci"}''
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
services = {
|
||||
borgbackup.jobs.simpcraft =
|
||||
let
|
||||
rconCommand = cmd: ''${pkgs.mcrcon}/bin/mcrcon -H simpcraft-oci -p "$RCON_PASSWORD" "${cmd}"'';
|
||||
in
|
||||
{
|
||||
paths = [ "/var/lib/containers/storage/volumes/minecraft_data/_data/world" ];
|
||||
repo = "/var/lib/containers/backup/simpcraft";
|
||||
doInit = true;
|
||||
encryption.mode = "none";
|
||||
compression = "zstd,10";
|
||||
# every ~15 minutes offset from 5 minute intervals (Minecraft seems to save at precise times?)
|
||||
startAt = "*:03,17,33,47";
|
||||
prune.keep = {
|
||||
within = "12H";
|
||||
hourly = 48;
|
||||
};
|
||||
readWritePaths = [ "/var/lib/borgbackup" "/var/cache/borgbackup" ];
|
||||
|
||||
# Avoid Minecraft poking the files while we back up
|
||||
preHook = rconCommand "save-off";
|
||||
postHook = rconCommand "save-on";
|
||||
};
|
||||
};
|
||||
|
||||
systemd = {
|
||||
services = {
|
||||
borgbackup-job-simpcraft.serviceConfig.EnvironmentFile = [ config.age.secrets."whale2/simpcraft.env".path ];
|
||||
};
|
||||
};
|
||||
|
||||
my = {
|
||||
secrets.files = {
|
||||
"whale2/simpcraft.env" = {};
|
||||
"whale2/simpcraft-git.key" = {
|
||||
owner = "1000";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 8.9 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 10 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 4.2 MiB |
@@ -94,7 +94,7 @@ in
|
||||
extraOptions = [ "-A /var/log/smartd/" "--interval=600" ];
|
||||
};
|
||||
udev.extraRules = ''
|
||||
ACTION=="add", SUBSYSTEM=="net", ENV{ID_NET_DRIVER}=="mlx5_core", ENV{ID_PATH}=="pci-0000:44:00.0", ATTR{device/sriov_numvfs}="4"
|
||||
ACTION=="add", SUBSYSTEM=="net", ENV{ID_NET_DRIVER}=="mlx5_core", ENV{ID_PATH}=="pci-0000:44:00.0", ATTR{device/sriov_numvfs}="3"
|
||||
'';
|
||||
};
|
||||
|
||||
@@ -188,13 +188,6 @@ in
|
||||
VLANId=${toString vlans.hi}
|
||||
LinkState=yes
|
||||
MACAddress=52:54:00:ac:15:a9
|
||||
|
||||
# sfh bridge
|
||||
[SR-IOV]
|
||||
VirtualFunction=3
|
||||
VLANId=${toString vlans.hi}
|
||||
LinkState=yes
|
||||
MACAddress=52:54:00:90:34:95
|
||||
'';
|
||||
};
|
||||
"60-lan-hi" = networkdAssignment "lan-hi" assignments.hi;
|
||||
|
||||
@@ -66,7 +66,6 @@ in
|
||||
];
|
||||
|
||||
services = {
|
||||
fstrim.enable = true;
|
||||
netdata.enable = true;
|
||||
};
|
||||
|
||||
|
||||
@@ -129,12 +129,6 @@ in
|
||||
hostnqn =
|
||||
"nqn.2014-08.org.nvmexpress:uuid:2230b066-a674-4f45-a1dc-f7727b3a9e7b";
|
||||
serial = "SPDK00000000000002";
|
||||
}) ++ (nvmfBdev {
|
||||
bdev = "NVMeRaidp3";
|
||||
nqn = "nqn.2016-06.io.spdk:sfh";
|
||||
hostnqn =
|
||||
"nqn.2014-08.org.nvmexpress:uuid:85d7df36-0de0-431b-b06e-51f7c0a455b4";
|
||||
serial = "SPDK00000000000003";
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
imports = [
|
||||
./cellar
|
||||
./river.nix
|
||||
./sfh
|
||||
];
|
||||
|
||||
nixos.systems.palace.configuration = { lib, pkgs, config, systems, allAssignments, ... }:
|
||||
@@ -58,11 +57,11 @@
|
||||
|
||||
systemd.services =
|
||||
let
|
||||
awaitVM = system: {
|
||||
after = [ "vm@${system}.service" ];
|
||||
bindsTo = [ "vm@${system}.service" ];
|
||||
awaitCellar = {
|
||||
after = [ "vm@cellar.service" ];
|
||||
bindsTo = [ "vm@cellar.service" ];
|
||||
preStart = ''
|
||||
until ${pkgs.netcat}/bin/nc -w1 -z ${allAssignments.${system}.hi.ipv4.address} 22; do
|
||||
until ${pkgs.netcat}/bin/nc -w1 -z ${allAssignments.cellar.hi.ipv4.address} 22; do
|
||||
sleep 1
|
||||
done
|
||||
'';
|
||||
@@ -82,13 +81,13 @@
|
||||
vtapUnit = "sys-subsystem-net-devices-vm\\x2det1g0.device";
|
||||
in
|
||||
mkMerge [
|
||||
(awaitVM "cellar")
|
||||
awaitCellar
|
||||
{
|
||||
requires = [ vtapUnit ];
|
||||
after = [ vtapUnit ];
|
||||
}
|
||||
];
|
||||
"vm@sfh" = (awaitVM "river");
|
||||
"vm@sfh" = awaitCellar;
|
||||
};
|
||||
|
||||
my = {
|
||||
@@ -183,18 +182,7 @@
|
||||
index = 0;
|
||||
hostBDF = "44:00.3";
|
||||
};
|
||||
et100g0vf3 = {
|
||||
index = 1;
|
||||
hostBDF = "44:00.4";
|
||||
};
|
||||
};
|
||||
qemuFlags = [
|
||||
"device qemu-xhci,id=xhci"
|
||||
# Front-right port?
|
||||
"device usb-host,hostbus=1,hostport=4"
|
||||
# Front-left port
|
||||
"device usb-host,hostbus=1,hostport=3"
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
@@ -10,7 +10,18 @@
|
||||
let
|
||||
inherit (lib.my) networkdAssignment mkVLAN;
|
||||
inherit (lib.my.c) networkd;
|
||||
inherit (lib.my.c.home) vlans domain prefixes roceBootModules;
|
||||
inherit (lib.my.c.home) vlans;
|
||||
|
||||
lanLink = {
|
||||
matchConfig = {
|
||||
Driver = "mlx5_core";
|
||||
PermanentMACAddress = "52:54:00:8a:8a:f2";
|
||||
};
|
||||
linkConfig = {
|
||||
Name = "lan";
|
||||
MTUBytes = toString lib.my.c.home.hiMTU;
|
||||
};
|
||||
};
|
||||
in
|
||||
{
|
||||
imports = [
|
||||
@@ -19,17 +30,29 @@
|
||||
|
||||
config = {
|
||||
boot = {
|
||||
kernelModules = [ "kvm-amd" ];
|
||||
kernelModules = [ "kvm-intel" ];
|
||||
kernelParams = [ "console=ttyS0,115200n8" ];
|
||||
initrd = {
|
||||
availableKernelModules = [
|
||||
"virtio_pci" "ahci" "sr_mod" "virtio_blk"
|
||||
"8021q"
|
||||
] ++ roceBootModules;
|
||||
kernelModules = [ "dm-snapshot" ];
|
||||
"ib_core" "ib_uverbs" "mlx5_core" "mlx5_ib" "8021q"
|
||||
"rdma_cm" "iw_cm" "ib_cm" "nvme_core" "nvme_rdma"
|
||||
];
|
||||
kernelModules = [ "dm-snapshot" "nvme-fabrics" ];
|
||||
systemd = {
|
||||
extraBin = with pkgs; {
|
||||
dmesg = "${util-linux}/bin/dmesg";
|
||||
ip = "${iproute2}/bin/ip";
|
||||
};
|
||||
extraConfig = ''
|
||||
DefaultTimeoutStartSec=50
|
||||
DefaultDeviceTimeoutSec=50
|
||||
'';
|
||||
network = {
|
||||
# Don't need to put the link config here, they're copied from main config
|
||||
enable = true;
|
||||
wait-online.enable = true;
|
||||
|
||||
links."10-lan" = lanLink;
|
||||
netdevs = mkVLAN "lan-hi" vlans.hi;
|
||||
networks = {
|
||||
"20-lan" = {
|
||||
@@ -47,6 +70,9 @@
|
||||
|
||||
hardware = {
|
||||
enableRedistributableFirmware = true;
|
||||
cpu = {
|
||||
intel.updateMicrocode = true;
|
||||
};
|
||||
};
|
||||
|
||||
fileSystems = {
|
||||
@@ -70,7 +96,6 @@
|
||||
boot.thin.enable = true;
|
||||
dmeventd.enable = true;
|
||||
};
|
||||
fstrim.enable = true;
|
||||
};
|
||||
|
||||
systemd.network = {
|
||||
@@ -89,16 +114,7 @@
|
||||
};
|
||||
};
|
||||
|
||||
"10-lan" = {
|
||||
matchConfig = {
|
||||
Driver = "mlx5_core";
|
||||
PermanentMACAddress = "52:54:00:8a:8a:f2";
|
||||
};
|
||||
linkConfig = {
|
||||
Name = "lan";
|
||||
MTUBytes = toString lib.my.c.home.hiMTU;
|
||||
};
|
||||
};
|
||||
"10-lan" = lanLink;
|
||||
};
|
||||
|
||||
# So we don't drop the IP we use to connect to NVMe-oF!
|
||||
@@ -118,14 +134,6 @@
|
||||
};
|
||||
};
|
||||
|
||||
netboot.server = {
|
||||
enable = true;
|
||||
ip = assignments.lo.ipv4.address;
|
||||
host = "boot.${domain}";
|
||||
allowedPrefixes = with prefixes; [ hi.v4 hi.v6 lo.v4 lo.v6 ];
|
||||
instances = [ "sfh" "castle" ];
|
||||
};
|
||||
|
||||
deploy.node.hostname = "192.168.68.1";
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
{
|
||||
imports = [
|
||||
# ./unifi.nix
|
||||
./hass.nix
|
||||
];
|
||||
}
|
||||
@@ -1,263 +0,0 @@
|
||||
{ lib, ... }:
|
||||
let
|
||||
inherit (lib.my) net;
|
||||
inherit (lib.my.c) pubDomain;
|
||||
inherit (lib.my.c.home) domain prefixes vips hiMTU;
|
||||
in
|
||||
{
|
||||
nixos.systems.hass = { config, ... }: {
|
||||
system = "x86_64-linux";
|
||||
nixpkgs = "mine";
|
||||
rendered = config.configuration.config.my.asContainer;
|
||||
|
||||
assignments = {
|
||||
hi = {
|
||||
name = "hass-ctr";
|
||||
altNames = [ "frigate" ];
|
||||
inherit domain;
|
||||
mtu = hiMTU;
|
||||
ipv4 = {
|
||||
address = net.cidr.host 103 prefixes.hi.v4;
|
||||
mask = 22;
|
||||
gateway = vips.hi.v4;
|
||||
};
|
||||
ipv6 = {
|
||||
iid = "::5:3";
|
||||
address = net.cidr.host (65536*5+3) prefixes.hi.v6;
|
||||
};
|
||||
};
|
||||
lo = {
|
||||
name = "hass-ctr-lo";
|
||||
inherit domain;
|
||||
mtu = 1500;
|
||||
ipv4 = {
|
||||
address = net.cidr.host 103 prefixes.lo.v4;
|
||||
mask = 21;
|
||||
gateway = null;
|
||||
};
|
||||
ipv6 = {
|
||||
iid = "::5:3";
|
||||
address = net.cidr.host (65536*5+3) prefixes.lo.v6;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
configuration = { lib, config, pkgs, assignments, allAssignments, ... }:
|
||||
let
|
||||
inherit (lib) mkMerge mkIf mkForce;
|
||||
inherit (lib.my) networkdAssignment;
|
||||
|
||||
hassCli = pkgs.writeShellScriptBin "hass-cli" ''
|
||||
export HASS_SERVER="http://localhost:${toString config.services.home-assistant.config.http.server_port}"
|
||||
export HASS_TOKEN="$(< ${config.age.secrets."hass/cli-token.txt".path})"
|
||||
exec ${pkgs.home-assistant-cli}/bin/hass-cli "$@"
|
||||
'';
|
||||
in
|
||||
{
|
||||
config = {
|
||||
my = {
|
||||
deploy.enable = false;
|
||||
server.enable = true;
|
||||
|
||||
secrets = {
|
||||
key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGpYX2WbYwUqHp8bFFf0eHFrqrR8xp8IheguA054F8V4";
|
||||
files = {
|
||||
"hass/cli-token.txt" = {
|
||||
owner = config.my.user.config.name;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
firewall = {
|
||||
tcp.allowed = [ "http" 1883 ];
|
||||
};
|
||||
};
|
||||
|
||||
environment = {
|
||||
systemPackages = with pkgs; [
|
||||
usbutils
|
||||
hassCli
|
||||
];
|
||||
};
|
||||
|
||||
systemd = {
|
||||
network.networks = {
|
||||
"80-container-host0" = networkdAssignment "host0" assignments.hi;
|
||||
"80-container-lan-lo" = networkdAssignment "lan-lo" assignments.lo;
|
||||
};
|
||||
};
|
||||
|
||||
services = {
|
||||
mosquitto = {
|
||||
enable = true;
|
||||
listeners = [
|
||||
{
|
||||
omitPasswordAuth = true;
|
||||
settings = {
|
||||
allow_anonymous = true;
|
||||
};
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
go2rtc = {
|
||||
enable = true;
|
||||
settings = {
|
||||
streams = {
|
||||
reolink_living_room = [
|
||||
# "http://reolink-living-room.${domain}/flv?port=1935&app=bcs&stream=channel0_main.bcs&user=admin#video=copy#audio=copy#audio=opus"
|
||||
"rtsp://admin:@reolink-living-room:554/h264Preview_01_main"
|
||||
];
|
||||
webcam_office = [
|
||||
"ffmpeg:device?video=/dev/video0&video_size=1024x576#video=h264"
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
frigate = {
|
||||
enable = true;
|
||||
hostname = "frigate.${domain}";
|
||||
settings = {
|
||||
mqtt = {
|
||||
enabled = true;
|
||||
host = "localhost";
|
||||
topic_prefix = "frigate";
|
||||
};
|
||||
|
||||
cameras = {
|
||||
reolink_living_room = {
|
||||
ffmpeg.inputs = [
|
||||
{
|
||||
path = "rtsp://127.0.0.1:8554/reolink_living_room";
|
||||
input_args = "preset-rtsp-restream";
|
||||
roles = [ "record" "detect" ];
|
||||
}
|
||||
];
|
||||
detect = {
|
||||
enabled = false;
|
||||
};
|
||||
record = {
|
||||
enabled = true;
|
||||
retain.days = 1;
|
||||
};
|
||||
};
|
||||
|
||||
webcam_office = {
|
||||
ffmpeg.inputs = [
|
||||
{
|
||||
path = "rtsp://127.0.0.1:8554/webcam_office";
|
||||
input_args = "preset-rtsp-restream";
|
||||
roles = [ "record" "detect" ];
|
||||
}
|
||||
];
|
||||
detect.enabled = false;
|
||||
record = {
|
||||
enabled = true;
|
||||
retain.days = 1;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
home-assistant =
|
||||
let
|
||||
cfg = config.services.home-assistant;
|
||||
|
||||
pyirishrail = ps: ps.buildPythonPackage rec {
|
||||
pname = "pyirishrail";
|
||||
version = "0.0.2";
|
||||
src = pkgs.fetchFromGitHub {
|
||||
owner = "ttroy50";
|
||||
repo = "pyirishrail";
|
||||
tag = version;
|
||||
hash = "sha256-NgARqhcXP0lgGpgBRiNtQaSn9JcRNtCcZPljcL7t3Xc=";
|
||||
};
|
||||
|
||||
dependencies = with ps; [
|
||||
requests
|
||||
];
|
||||
|
||||
pyproject = true;
|
||||
build-system = [ ps.setuptools ];
|
||||
};
|
||||
in
|
||||
{
|
||||
enable = true;
|
||||
|
||||
extraComponents = [
|
||||
"default_config"
|
||||
"esphome"
|
||||
"google_translate"
|
||||
|
||||
"met"
|
||||
"zha"
|
||||
"denonavr"
|
||||
"webostv"
|
||||
"androidtv_remote"
|
||||
"heos"
|
||||
"mqtt"
|
||||
"wled"
|
||||
];
|
||||
extraPackages = python3Packages: with python3Packages; [
|
||||
zlib-ng
|
||||
isal
|
||||
|
||||
gtts
|
||||
(pyirishrail python3Packages)
|
||||
];
|
||||
customComponents = with pkgs.home-assistant-custom-components; [
|
||||
alarmo
|
||||
frigate
|
||||
west_wood_club
|
||||
];
|
||||
|
||||
configWritable = false;
|
||||
openFirewall = true;
|
||||
config = {
|
||||
default_config = {};
|
||||
homeassistant = {
|
||||
name = "Home";
|
||||
unit_system = "metric";
|
||||
currency = "EUR";
|
||||
country = "IE";
|
||||
time_zone = "Europe/Dublin";
|
||||
external_url = "https://hass.${pubDomain}";
|
||||
internal_url = "http://hass-ctr.${domain}:${toString cfg.config.http.server_port}";
|
||||
};
|
||||
http = {
|
||||
use_x_forwarded_for = true;
|
||||
trusted_proxies = with allAssignments.middleman.internal; [
|
||||
ipv4.address
|
||||
ipv6.address
|
||||
];
|
||||
ip_ban_enabled = false;
|
||||
};
|
||||
automation = "!include automations.yaml";
|
||||
script = "!include scripts.yaml";
|
||||
scene = "!include scenes.yaml";
|
||||
|
||||
sensor = [
|
||||
{
|
||||
platform = "irish_rail_transport";
|
||||
name = "To Work from Home";
|
||||
station = "Glenageary";
|
||||
stops_at = "Dublin Connolly";
|
||||
direction = "Northbound";
|
||||
}
|
||||
{
|
||||
platform = "irish_rail_transport";
|
||||
name = "To Home from Work";
|
||||
station = "Dublin Connolly";
|
||||
stops_at = "Glenageary";
|
||||
direction = "Southbound";
|
||||
}
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
{ lib, ... }:
|
||||
let
|
||||
inherit (lib.my) net;
|
||||
inherit (lib.my.c.home) domain prefixes vips hiMTU;
|
||||
in
|
||||
{
|
||||
nixos.systems.unifi = { config, ... }: {
|
||||
system = "x86_64-linux";
|
||||
nixpkgs = "mine";
|
||||
rendered = config.configuration.config.my.asContainer;
|
||||
|
||||
assignments = {
|
||||
hi = {
|
||||
name = "unifi-ctr";
|
||||
inherit domain;
|
||||
mtu = hiMTU;
|
||||
ipv4 = {
|
||||
address = net.cidr.host 100 prefixes.hi.v4;
|
||||
mask = 22;
|
||||
gateway = vips.hi.v4;
|
||||
};
|
||||
ipv6 = {
|
||||
iid = "::5:1";
|
||||
address = net.cidr.host (65536*5+1) prefixes.hi.v6;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
configuration = { lib, config, pkgs, assignments, ... }:
|
||||
let
|
||||
inherit (lib) mkMerge mkIf mkForce;
|
||||
inherit (lib.my) networkdAssignment;
|
||||
in
|
||||
{
|
||||
config = {
|
||||
my = {
|
||||
deploy.enable = false;
|
||||
server.enable = true;
|
||||
|
||||
secrets = {
|
||||
key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKdgcziQki/RH7E+NH2bYnzSVKaJ27905Yo5TcOjSh/U";
|
||||
files = { };
|
||||
};
|
||||
|
||||
firewall = {
|
||||
tcp.allowed = [ 8443 ];
|
||||
};
|
||||
};
|
||||
|
||||
systemd = {
|
||||
network.networks."80-container-host0" = networkdAssignment "host0" assignments.hi;
|
||||
};
|
||||
|
||||
services = {
|
||||
unifi = {
|
||||
enable = true;
|
||||
openFirewall = true;
|
||||
unifiPackage = pkgs.unifi;
|
||||
mongodbPackage = pkgs.mongodb-7_0;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -1,200 +0,0 @@
|
||||
{ lib, ... }:
|
||||
let
|
||||
inherit (lib.my) net;
|
||||
inherit (lib.my.c.home) domain prefixes vips hiMTU roceBootModules;
|
||||
in
|
||||
{
|
||||
imports = [ ./containers ];
|
||||
|
||||
config.nixos.systems.sfh = {
|
||||
system = "x86_64-linux";
|
||||
nixpkgs = "mine";
|
||||
home-manager = "mine";
|
||||
|
||||
assignments = {
|
||||
hi = {
|
||||
inherit domain;
|
||||
mtu = hiMTU;
|
||||
ipv4 = {
|
||||
address = net.cidr.host 81 prefixes.hi.v4;
|
||||
mask = 22;
|
||||
gateway = vips.hi.v4;
|
||||
};
|
||||
ipv6 = {
|
||||
iid = "::4:2";
|
||||
address = net.cidr.host (65536*4+2) prefixes.hi.v6;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
configuration = { lib, modulesPath, pkgs, config, assignments, allAssignments, ... }:
|
||||
let
|
||||
inherit (lib) mapAttrs mkMerge mkForce;
|
||||
inherit (lib.my) networkdAssignment;
|
||||
inherit (lib.my.c) networkd;
|
||||
inherit (lib.my.c.home) domain;
|
||||
in
|
||||
{
|
||||
imports = [
|
||||
"${modulesPath}/profiles/qemu-guest.nix"
|
||||
];
|
||||
|
||||
config = {
|
||||
boot = {
|
||||
kernelModules = [ "kvm-amd" ];
|
||||
kernelParams = [ "console=ttyS0,115200n8" ];
|
||||
initrd = {
|
||||
availableKernelModules = [
|
||||
"virtio_pci" "ahci" "sr_mod" "virtio_blk"
|
||||
] ++ roceBootModules;
|
||||
kernelModules = [ "dm-snapshot" ];
|
||||
systemd = {
|
||||
network = {
|
||||
networks = {
|
||||
"20-lan-hi" = networkdAssignment "lan-hi" assignments.hi;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
hardware = {
|
||||
enableRedistributableFirmware = true;
|
||||
};
|
||||
|
||||
fileSystems = {
|
||||
"/nix" = {
|
||||
device = "/dev/main/nix";
|
||||
fsType = "ext4";
|
||||
};
|
||||
"/persist" = {
|
||||
device = "/dev/main/persist";
|
||||
fsType = "ext4";
|
||||
neededForBoot = true;
|
||||
};
|
||||
};
|
||||
|
||||
networking = { inherit domain; };
|
||||
|
||||
services = {
|
||||
lvm = {
|
||||
boot.thin.enable = true;
|
||||
dmeventd.enable = true;
|
||||
};
|
||||
};
|
||||
|
||||
environment = {
|
||||
systemPackages = with pkgs; [
|
||||
usbutils
|
||||
];
|
||||
};
|
||||
|
||||
systemd.network = {
|
||||
links = {
|
||||
"10-lan-hi" = {
|
||||
matchConfig = {
|
||||
Driver = "mlx5_core";
|
||||
PermanentMACAddress = "52:54:00:ac:15:a9";
|
||||
};
|
||||
linkConfig = {
|
||||
Name = "lan-hi";
|
||||
MTUBytes = toString lib.my.c.home.hiMTU;
|
||||
};
|
||||
};
|
||||
"10-lan-hi-ctrs" = {
|
||||
matchConfig = {
|
||||
Driver = "mlx5_core";
|
||||
PermanentMACAddress = "52:54:00:90:34:95";
|
||||
};
|
||||
linkConfig = {
|
||||
Name = "lan-hi-ctrs";
|
||||
MTUBytes = toString lib.my.c.home.hiMTU;
|
||||
};
|
||||
};
|
||||
"10-lan-lo-ctrs" = {
|
||||
matchConfig = {
|
||||
Driver = "virtio_net";
|
||||
PermanentMACAddress = "52:54:00:a5:7e:93";
|
||||
};
|
||||
linkConfig.Name = "lan-lo-ctrs";
|
||||
};
|
||||
};
|
||||
|
||||
networks = {
|
||||
"30-lan-hi" = mkMerge [
|
||||
(networkdAssignment "lan-hi" assignments.hi)
|
||||
# So we don't drop the IP we use to connect to NVMe-oF!
|
||||
{ networkConfig.KeepConfiguration = "static"; }
|
||||
];
|
||||
"30-lan-hi-ctrs" = {
|
||||
matchConfig.Name = "lan-hi-ctrs";
|
||||
linkConfig.RequiredForOnline = "no";
|
||||
networkConfig = networkd.noL3;
|
||||
};
|
||||
"30-lan-lo-ctrs" = {
|
||||
matchConfig.Name = "lan-lo-ctrs";
|
||||
linkConfig.RequiredForOnline = "no";
|
||||
networkConfig = networkd.noL3;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
systemd.nspawn = {
|
||||
hass = {
|
||||
networkConfig = {
|
||||
MACVLAN = mkForce "lan-hi-ctrs:host0 lan-lo-ctrs:lan-lo";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
systemd.services = {
|
||||
"systemd-nspawn@hass".serviceConfig.DeviceAllow = [
|
||||
"char-ttyUSB rw"
|
||||
"char-video4linux rw"
|
||||
];
|
||||
};
|
||||
|
||||
my = {
|
||||
secrets = {
|
||||
key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAAaav5Se1E/AbqEXmADryVszYfNDscyP6jrWioN57R7";
|
||||
};
|
||||
server.enable = true;
|
||||
|
||||
netboot.client = {
|
||||
enable = true;
|
||||
};
|
||||
nvme = {
|
||||
uuid = "85d7df36-0de0-431b-b06e-51f7c0a455b4";
|
||||
boot = {
|
||||
nqn = "nqn.2016-06.io.spdk:sfh";
|
||||
address = "192.168.68.80";
|
||||
};
|
||||
};
|
||||
|
||||
containers.instances =
|
||||
let
|
||||
instances = {
|
||||
# unifi = {};
|
||||
hass = {
|
||||
bindMounts = {
|
||||
"/dev/bus/usb/001/002".readOnly = false;
|
||||
"/dev/video0".readOnly = false;
|
||||
"/dev/serial/by-id/usb-Nabu_Casa_Home_Assistant_Connect_ZBT-1_ce549704fe38ef11a2c2e5d154516304-if00-port0" = {
|
||||
readOnly = false;
|
||||
mountPoint = "/dev/ttyUSB0";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
in
|
||||
mkMerge [
|
||||
instances
|
||||
(mapAttrs (n: i: {
|
||||
networking.macVLAN = "lan-hi-ctrs";
|
||||
}) instances)
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user