19 Commits
Author SHA1 Message Date
jackos1998 73e538ad1f nixos/whale2: Refresh Valheim server and world
CI / Check, build and cache nixfiles (push) Successful in 55m5s
Update docs / update (push) Successful in 1m10s
Pin the community image and start simpland3 while retaining
simpland2. Correct the server-files volume name and reuse the
admin ID in both access lists.
2026-09-20 21:23:01 +01:00
github-actions[bot] 9e9027a250 docs: Update generated references 2026-08-25 19:59:15 +00:00
jackos1998 b904922356 ci/docs: Fix repository URL for push
CI / Check, build and cache nixfiles (push) Has been cancelled
Update docs / update (push) Successful in 1m4s
2026-08-25 20:57:26 +01:00
jackos1998 02c965dd96 ci/cache: Keep collection in preflight
CI / Check, build and cache nixfiles (push) Successful in 47m34s
Update docs / update (push) Failing after 1m4s
Group cache collection separately in the Actions log and avoid repeating
it after updating the CI profile.
2026-08-25 08:58:57 +01:00
jackos1998 adea4bd9e5 ci/cache: Prune profile generations
Update docs / update (push) Failing after 1m5s
CI / Check, build and cache nixfiles (push) Failing after 3h5m51s
Explicitly remove expired generations from the nonstandard Harmonia
profile before collecting garbage. This keeps old CI closures from
remaining rooted until the cache exhausts its inodes.
2026-08-25 08:46:45 +01:00
jackos1998 e875971237 nixos/hass: Use packaged pyirishrail
CI / Check, build and cache nixfiles (push) Failing after 29m48s
Update docs / update (push) Failing after 1m9s
Fix pyirishrail metadata in the nixpkgs fork, update the mine pin,
and remove the local package now supplied by the Home Assistant
component.
2026-08-25 00:12:01 +01:00
jackos1998 1a9c601c2b ci/cache: Collect garbage before pushes
Update docs / update (push) Failing after 1m3s
CI / Check, build and cache nixfiles (push) Failing after 26m9s
Run retention cleanup before uploading build results so inode
exhaustion cannot prevent CI from reaching its only garbage-collection
step.
2026-08-24 22:57:10 +01:00
jackos1998 92855606a4 pkgs/firefox-memory-control: React to swap-outs
CI / Check, build and cache nixfiles (push) Failing after 20m49s
Update docs / update (push) Failing after 1m9s
Firefox can exhaust swap while `MemAvailable` remains above the unload threshold. Track new `pswpout` pages so active swapping triggers tab unloading without treating stale swap occupancy as permanent pressure.
2026-08-24 15:19:23 +01:00
jackos1998 5171a10079 nixpkgs: Refresh channels and inputs
CI / Check, build and cache nixfiles (push) Failing after 59m32s
Update docs / update (push) Failing after 1m12s
Rebase the fork branches and refresh nixpkgs, home-manager, and the
approved ancillary inputs. Update kernel and release metadata, adapt
removed package and Home Assistant options, and keep Determinate Nix
on its tested nixpkgs revision to avoid duplicate Boost patches.

Retire Sharry and its public endpoint because copyparty replaces it.
Document the GitHub mirror gate and require real devshell and system
builds in the upgrade validation workflow.
2026-08-24 00:10:18 +01:00
jackos1998 e93e9f7a08 openwrt: Pin vendored feed indexes
Update docs / update (push) Failing after 1m12s
CI / Check, build and cache nixfiles (push) Successful in 58m0s
Pin `openwrt-feeds` to repository state that includes vendored APK
indexes, keeping image builds independent of mutable upstream indexes.

Document the corresponding refresh workflow.
2026-08-23 23:08:41 +01:00
jackos1998 5e036d17c4 docs/nixpkgs: Expand upgrade workflow
Move the skill to the shared agent location while retaining Claude
compatibility. Document kernel refreshes and the release metadata
policy as part of each upgrade, and make commit-message wrapping
explicit and verifiable.
2026-08-23 21:44:07 +01:00
jackos1998 57b94b64bb openwrt: Keep SFP LuCI app snapshot-only
CI / Check, build and cache nixfiles (push) Failing after 7m19s
Update docs / update (push) Failing after 1m13s
2026-08-23 21:27:32 +01:00
jackos1998andClaude Opus 5 bf411e03e2 nixos/portcullis: Tune NICs, IOMMU and EEE
Router-sized 4096-entry rings on every port with GRO kept across
forwarding, the IOMMU in passthrough mode, and EEE pinned off on the
I226-V ports as one trigger for their link-drop erratum. The ring and
GRO settings are .link files, so they land on a device add event rather
than at switch time.

Also document the tuning deliberately not done -- coalescing and PCIe
ASPM -- and what measuring the NICs' ESP offload found, since the
esp4_offload modules are software batching and easy to mistake for it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-23 10:54:22 +01:00
jackos1998andClaude Opus 5 81b7ca9d05 nixos/l2mesh: Load the ESP offload modules
CI / Check, build and cache nixfiles (push) Failing after 6m55s
Update docs / update (push) Failing after 1m11s
esp4_offload/esp6_offload provide GSO/GRO batching for ESP and are not
autoloaded when an SA is created, costing around a third of the mesh's
encrypted throughput. Load the one matching each secured mesh's
underlay family.

Also document the per-SA single-core limit and pcrypt as an option.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-22 14:09:23 +01:00
jackos1998andClaude Opus 5 dd0b318a44 nixos/castle: Set lan-lo to the standard MTU
lan-lo is a VLAN on et100g, which carries hi's jumbo frames, so it
inherited 9000 rather than the 1500 the lo VLAN runs at. MSS clamping
hid this from TCP; UDP without working PMTUD was silently dropped.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-22 14:09:01 +01:00
jackos1998andClaude Opus 5 d24d71113f docs: Clarify what a logically distinct commit is
CI / Check, build and cache nixfiles (push) Failing after 6m50s
Update docs / update (push) Failing after 1m7s
"Keep logically distinct changes in separate commits" was being read as
split anything separable, which turns one piece of work into several
commits that only make sense read together.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-22 01:34:15 +01:00
jackos1998andClaude Opus 5 87cdfdbd97 nixos/portcullis: Bring up 10G on the home hi VLAN
portcullis is wired over 10G to fergal, which uplinks to jim's spare
SFP+ port. That uplink is untagged VLAN 1, so hi is carried tagged on a
lan-hi VLAN interface: a static assignment at 192.168.68.41 / ::6:1,
resolving through the router VIPs like any other hi client. Its gateway
route outranks the DHCP default, making 10G the preferred path while the
2.5G bootstrap stays as a fallback. Deploy now targets that address.

The hi MTU goes on the .network rather than the .link, since a .link is
only applied at udev device-add -- with it there, et10g-0 stays at 1500
across a switch and lan-hi cannot take 9000.

jim's sfp-spare was tagged into hi and lo out of band to match.

fergal turns out to belong with portcullis rather than to the home
fabric -- it goes to Nikhef when the box does -- so its documentation
moves to the colony site, leaving home/switches.md a short section on
what it borrows from that fabric.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-22 01:30:50 +01:00
jackos1998andClaude Opus 5 7bebac194c docs: Note commit trailer and body conventions
Co-Authored-By is the only trailer wanted here; session links are not.
Also spell out that bodies should stay concise.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-22 01:17:11 +01:00
jackos1998andClaude Opus 5 cdc5d9c1db openwrt: Build fergal's firmware in the flake
fergal is an 8-port SFP+ switch on a Realtek RTL9303, running OpenWrt
rather than RouterOS or UniFi. It is not part of the fabric yet, but
its firmware is now built here via astro's nix-openwrt-imagebuilder.
Packages are baked into the image: OpenWrt's package server keeps only
the current build of each feed, so installing at runtime stops working
as soon as the feed moves past the running firmware.

Those feed indexes rotate constantly, and upstream pins only the
indexes -- a mismatch drops evaluation into import-from-derivation,
putting this flake's eval on the network. The openwrt-feeds input pins
expanded per-package hashes instead, in a repository of its own
because they run to hundreds of thousands of generated lines.

Flashing gets a procedure doc and a thin skill pointing at it, the
same split as the box installation and nixpkgs upgrade procedures.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-22 01:16:53 +01:00
40 changed files with 671 additions and 576 deletions
+33
View File
@@ -0,0 +1,33 @@
---
name: upgrade-nixpkgs
description: >-
Upgrade all four nixpkgs channels (unstable, stable, mine, mine-stable) and home-manager for this
flake: check for a NixOS stable bump, rebase the devplayer0 nixpkgs fork, update kernel and
release metadata, refresh pins, sweep version-gated TODOs, and review other inputs. Use when the
user wants to update/bump nixpkgs, refresh the pins, or do the periodic nixpkgs/home-manager
upgrade.
---
# Upgrade nixpkgs
Read [`docs/nixpkgs-upgrade.md`](../../../docs/nixpkgs-upgrade.md), the canonical procedure, and
follow its phases in order.
Key reminders (see the doc for the full steps):
- It is **guided, not automated** — do the mechanical and investigative work but stop at the ⏸
points: pushing the fork, resolving rebase conflicts, applying a stable-channel bump, choosing a
new release codename, and deleting version guards. Report the findings and let the user decide.
- **Check the current NixOS stable first** (Phase 1) — the fork's `devplayer0-stable` rebase target
and the `flake.nix` stable pins must agree on one release.
- **Re-verify the patch stack against freshly fetched upstream**, not stale refs — enumerate it with
`git log`; don't assume a remembered list.
- After pushing the rebased fork branches, **wait for the GitHub mirror to catch up** before
refreshing flake pins. The `nixpkgs-mine*` inputs fetch from GitHub, not the fork's primary
remote; verify both GitHub branch tips match the pushed local tips first.
- After refreshing the pins, update `lib/constants.nix` to the current explicit LTS and latest
kernel package attributes, and update the `lib/default.nix` version overlay's `YY.MM` prefix to
the current month. Change its codename only when the stable channel advances.
- After the cheap evaluations pass, build the actual devshell and one representative NixOS system
(prefer the local box). `nix flake check --no-build` does not expose dependency build failures;
this is especially important when updating build-tool inputs such as Determinate Nix.
+1
View File
@@ -0,0 +1 @@
../../.agents/skills/upgrade-nixpkgs
-25
View File
@@ -1,25 +0,0 @@
---
name: upgrade-nixpkgs
description: >-
Upgrade all four nixpkgs channels (unstable, stable, mine, mine-stable) and home-manager for this
flake: check for a NixOS stable bump, rebase the devplayer0 nixpkgs fork against upstream, run the
update commands, sweep version-gated TODOs, and review flake inputs. Use when the user wants to
update/bump nixpkgs, refresh the pins, or do the periodic nixpkgs/home-manager upgrade.
---
# Upgrade nixpkgs
The canonical, agent-agnostic procedure lives in the repo at
[`docs/nixpkgs-upgrade.md`](../../../docs/nixpkgs-upgrade.md). Read it and follow the phases in
order.
Key reminders (see the doc for the full steps):
- It is **guided, not automated** — do the mechanical/investigative work but stop at the ⏸ points:
pushing the fork, resolving rebase conflicts, editing the `flake.nix` stable pins, and deleting
version guards. Report and let the user decide.
- **Check the current NixOS stable first** (Phase 1) — the fork's `devplayer0-stable` rebase target
and the `flake.nix` stable pins must agree on one release.
- **Re-verify the patch stack against freshly fetched upstream**, not stale refs — enumerate it with
`git log`, don't assume a remembered list (stale `upstream/*` refs make already-upstreamed commits
masquerade as fork-only patches).
+4
View File
@@ -37,6 +37,10 @@ jobs:
env:
HARMONIA_SSH_KEY: ${{ secrets.HARMONIA_SSH_KEY }}
run: |
echo "::group::Collect cache garbage"
ci/push-to-cache.sh --gc
echo "::endgroup::"
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"
+1 -1
View File
@@ -39,10 +39,10 @@ jobs:
- name: Commit and push if changed
env:
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
REPO_URL: ${{ gitea.repositoryUrl }}
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
REPO_URL="${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}"
git remote set-url origin "${REPO_URL/https:\/\//https:\/\/oauth2:${GITEA_TOKEN}@}"
git add docs/
if ! git diff --cached --quiet; then
+15 -6
View File
@@ -65,8 +65,9 @@ Common ones:
`$INSTALLER`. For bringing up a new box end to end follow the guided procedure in
[`docs/install-box.md`](docs/install-box.md).
- `update-nixpkgs` / `update-home-manager` — bump pinned inputs. For the full periodic upgrade
(rebasing the `devplayer0` nixpkgs fork, stable-release bumps, version-gate sweep, input review)
follow the guided procedure in [`docs/nixpkgs-upgrade.md`](docs/nixpkgs-upgrade.md).
(rebasing the `devplayer0` nixpkgs fork, stable-release bumps, kernel and release-metadata
refreshes, version-gate sweep, input review) follow the guided procedure in
[`docs/nixpkgs-upgrade.md`](docs/nixpkgs-upgrade.md).
Use the narrowest relevant evaluation while iterating: `check-system <host>` for a box config,
`nix eval .#nixfiles.config.nixos.allAssignments --json` for assignment generation, or
@@ -211,10 +212,18 @@ in churn.
command, option or upstream technical term such as QEMU's machine type.
- Commit subjects follow `area/scope: Capitalized summary` (e.g. `nixos/home: ...`); keep logically
distinct changes in separate commits. Aim for 50-character subjects and do not exceed 72
characters. Wrap commit bodies at 72 columns. A concise body describing the change and its
rationale is welcome when the subject alone does not provide enough context — keep it to the
essentials rather than restating the diff. `Co-Authored-By` is the only trailer used here; do
**not** add a `Claude-Session` link (or any other session/tooling trailer).
characters. Hard-wrap commit body lines at 72 characters; Git preserves an unwrapped `-m`
argument as one long line, so include literal line breaks or use a commit-message file. Before
reporting a commit, inspect `git show -s --format=%B HEAD` and amend it if any line exceeds 72
characters. A concise body describing the change and its rationale is welcome when the subject
alone does not provide enough context — keep it to the essentials rather than restating the diff.
`Co-Authored-By` is the only trailer used here; do **not** add a `Claude-Session` link (or any
other session/tooling trailer).
- **"Logically distinct" means unrelated** — two different applications, two boxes that have nothing
to do with each other, a drive-by fix that happens to sit in a file you were editing anyway. One
piece of work stays in one commit even when it touches a config, several docs and a switch: if the
parts only make sense together, splitting them just makes each half unreviewable. Err towards one
commit and split when a reader would ask why two things arrived together.
## Documentation
+14 -3
View File
@@ -10,6 +10,14 @@ remote_cmd() {
ssh -i "$SSH_KEY" "$SSH_HOST" env HOME=/run/harmonia NIX_REMOTE="$REMOTE_STORE" "$@"
}
collect_garbage() {
echo "Collecting garbage..."
remote_cmd nix-env \
-p "$REMOTE_STORE"/nix/var/nix/profiles/nixfiles \
--delete-generations 60d
remote_cmd nix-collect-garbage
}
umask_old=$(umask)
umask 0066
echo "$HARMONIA_SSH_KEY" | base64 -d > "$SSH_KEY"
@@ -17,6 +25,12 @@ umask $umask_old
mkdir -p ~/.ssh
cp ci/known_hosts ~/.ssh/
if [ "${1-}" = "--gc" ]; then
collect_garbage
exit
fi
path="$1"
echo "Pushing $path to cache..."
@@ -25,7 +39,4 @@ 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
+2 -1
View File
@@ -47,7 +47,7 @@ colony (physical VM host, ams1)
│ ├── colony-psql (shared PostgreSQL)
│ ├── chatterbox (Matrix Synapse + bridges)
│ ├── jackflix (media stack)
│ ├── object (MinIO, Harmonia Nix cache, Sharry, HedgeDoc, wastebin)
│ ├── object (MinIO, Harmonia Nix cache, HedgeDoc, wastebin)
│ ├── toot (Bluesky PDS; Mastodon disabled)
│ ├── waffletail (Tailscale subnet router / exit node)
│ ├── qclk (WireGuard management appliance)
@@ -59,6 +59,7 @@ colony (physical VM host, ams1)
└── darts ──── third-party/customer VM (opaque, not NixOS)
portcullis (bare-metal edge box for Nikhef — staged, not yet in service)
└── fergal OpenWrt SFP+ switch, staged and moving with it
```
## Site: home
+1 -1
View File
@@ -175,7 +175,7 @@ descriptions) see [`reference/nixos-options.md`](reference/nixos-options.md).
| Module | Provides |
|---|---|
| `common` | Baseline for all boxes: imports the impermanence, ragenix (age), sharry, copyparty and harmonia NixOS modules; pins `system.stateVersion`; `doas` instead of `sudo`; immutable users; nix settings (flakes, `ca-derivations`, the `nix-cache.nul.ie` substituter); declares the `my` option root. |
| `common` | Baseline for all boxes: imports the impermanence, ragenix (age), copyparty and harmonia NixOS modules; pins `system.stateVersion`; `doas` instead of `sudo`; immutable users; nix settings (flakes, `ca-derivations`, the `nix-cache.nul.ie` substituter); declares the `my` option root. |
| `user` | `my.user` — the primary user: `users.users` + matching `home-manager.users` entry, wheel/doas, SSH authorized key from `.keys/me.pub`, shell taken from the home config, home persistence under tmproot. |
| `build` | `my.build` — alternate build targets via `extendModules`: `my.buildAs.devVM` (QEMU dev VM), `iso`, `container`, `kexecTree`, `netbootTree`/`netbootArchive`; `my.build.isDevVM` marker; `allHardware` profile toggle. |
| `dynamic-motd` | `my.dynamic-motd` — runs a script via `pam_exec` to generate the MOTD on login/ssh. |
+15 -11
View File
@@ -181,7 +181,7 @@ image, with no cross-toolchain involved.
| Output | Box | Release |
|---|---|---|
| `openwrt-fergal` | [fergal](sites/home/switches.md#fergal-the-openwrt-switch) | `snapshot` |
| `openwrt-fergal` | [fergal](sites/colony/fergal.md) | `snapshot` |
| `openwrt-fergal-release` | The same, on the release branch | pinned in `openwrt/default.nix` |
Both are in `ci`, so images are built and pushed to the Harmonia cache like everything else. Build
@@ -202,11 +202,14 @@ point release. Building straight against it fails on hash mismatches and, worse,
package list by import-from-derivation — which would drag *evaluation* of this flake onto the
network and let an OpenWrt feed rebuild break `check-system` for unrelated boxes.
The `openwrt-feeds` input exists to stop that. It holds expanded per-package hashes, so every `.apk`
is a plain pinned `fetchurl` and no import-from-derivation is involved. Its generated files run to
hundreds of thousands of lines and are rewritten wholesale on each refresh, which is why they live
in their own repository rather than here. Refresh the pin with `nix flake update openwrt-feeds`;
adding a release or target means adding it to that repo's `pins` and regenerating there first.
The `openwrt-feeds` input exists to stop that. It holds expanded per-package metadata and vendors
the repository indexes themselves, so every `.apk` is a plain pinned `fetchurl`, image builds read
the indexes from the flake rather than OpenWrt's mutable URLs, and no import-from-derivation is
involved. Its generated files run to hundreds of thousands of lines and are rewritten wholesale on
each refresh, which is why they live in their own repository rather than here. Regenerate and push
that repository with `nix run .#update`, then refresh this flake's pin with
`nix flake update openwrt-feeds`; adding a release or target means adding it to that repository's
`pins` first.
## CI
@@ -219,13 +222,14 @@ On pushes to `master`, this installs Determinate Nix on the runner (via
Harmonia substituter as the boxes), runs `nix flake check --no-build`, then builds every attribute
of `.#ci.x86_64-linux`: systems as `system-<name>`, homes as `home-<name>` (with `@` changed to
`-at-`), packages as `package-<name>`, and the development `shell`. Each result is pushed to the
Harmonia cache with [`ci/push-to-cache.sh`](../ci/push-to-cache.sh).
Harmonia cache with [`ci/push-to-cache.sh`](../ci/push-to-cache.sh). Before the first push, the
workflow deletes cache-profile generations older than its retention period, then collects
unreachable paths so a full cache cannot prevent collection from being reached.
It then builds `.#ciDrv.x86_64-linux`, a `linkFarm` of all CI attributes, and pushes it with
`UPDATE_PROFILE=1`. That updates the `nixfiles` profile on the cache box and collects old paths
according to the workflow's retention setting. The SSH store uses `/var/lib/harmonia`,
`HARMONIA_SSH_KEY`, and pinned `ci/known_hosts`; clients use `https://nix-cache.nul.ie` through
`lib.my.c.nix.cache`.
`UPDATE_PROFILE=1`. That updates the `nixfiles` profile on the cache box. The SSH store uses
`/var/lib/harmonia`, `HARMONIA_SSH_KEY`, and pinned `ci/known_hosts`; clients use
`https://nix-cache.nul.ie` through `lib.my.c.nix.cache`.
### `installer.yaml`
+70 -2
View File
@@ -159,6 +159,7 @@ edit prose there, never the other generated cells.
| [`cellar`](sites/home/cellar.md) | `192.168.68.80/22 gw 192.168.71.254` | `2a0e:97c0:4d0:1::4:1/64` | h.nul.ie | |
| [`hass`](sites/home/sfh/containers/hass.md) | `192.168.68.103/22 gw 192.168.71.254` | `2a0e:97c0:4d0:1::5:3/64` | h.nul.ie | |
| [`palace`](sites/home/palace.md) | `192.168.68.22/22 gw 192.168.71.254` | `2a0e:97c0:4d0:1::2:1/64` | h.nul.ie | |
| [`portcullis`](sites/colony/portcullis.md) | `192.168.68.41/22 gw 192.168.71.254` | `2a0e:97c0:4d0:1::6:1/64` | h.nul.ie | |
| [`river`](sites/home/river.md) | `192.168.68.1/22` | `2a0e:97c0:4d0:1::1/64` | h.nul.ie | |
| `router-hi` | `192.168.71.254/22 gw 192.168.68.1` | `2a0e:97c0:4d0:1::ffff/64` | h.nul.ie | Floating VIP shared by [`river`](sites/home/river.md) and [`stream`](sites/home/stream.md) |
| [`sfh`](sites/home/sfh/README.md) | `192.168.68.81/22 gw 192.168.71.254` | `2a0e:97c0:4d0:1::4:2/64` | h.nul.ie | |
@@ -267,8 +268,9 @@ public blocks and the per-customer `mail` / `darts` / `jam` prefixes carry custo
services with their own public addresses (announced by BGP, routed via the host).
This layout is expected to change: [`portcullis`](sites/colony/portcullis.md) is bare-metal edge
hardware headed for Nikhef that will take over most of `estuary`'s routing. It has no assignments
yet and the replacement topology is still being designed.
hardware headed for Nikhef that will take over most of `estuary`'s routing. It has no colony
assignments yet (only a home `hi` one, from being staged at home) and the replacement topology is
still being designed.
## home
@@ -387,6 +389,72 @@ Libreswan transport mode. It authenticates without encryption by default; `secur
switches ESP from `null-sha256` to AES-GCM. The shared `l2mesh/as211024.key` PSK is expanded into
`/run/l2mesh.secrets` when `ipsec` starts.
### ESP throughput
ESP is the mesh's throughput limit rather than VXLAN — encapsulation is close to free, and on a
small box the crypto is what costs. The kernel processes a single SA on a single core, so per-peer
throughput is capped by one core however many the box has. That limit binds per peer rather than
per box, so a router spreads naturally across its peers.
Two things follow for configuration:
- The `esp4_offload` / `esp6_offload` modules provide GSO/GRO batching for ESP and are **not**
autoloaded when an SA is created. The [`l2mesh` module](../nixos/modules/l2mesh.nix) loads the
one matching each secured mesh's underlay family; without them throughput is around a third
lower, for more CPU.
- `security.encrypt = false` does not save CPU on hardware with AES-NI — it measured slower than
AES-GCM. GCM resolves to a single fused accelerated implementation, while the authenticate-only
path falls back to a generic `authenc(hmac(sha256),ecb(cipher_null))` composition.
#### NIC crypto offload
No mesh uses it, and the `esp4_offload` / `esp6_offload` modules above are unrelated to it — those
are software GSO/GRO batching. Hardware ESP offload is a separate XFRM feature that Libreswan only
requests for connections setting `nic-offload=yes`, which the
[`l2mesh` module](../nixos/modules/l2mesh.nix) does not.
[`portcullis`](sites/colony/portcullis.md)'s 82599ES ports advertise `esp-hw-offload` and the
offload does work, but not for anything the meshes could use. Measured on the box by installing
SAs directly with `ip xfrm` and watching `ixgbe`'s `tx_ipsec` counter:
| SA | Result |
|---|---|
| `et10g-0`, transport, AES-GCM-128 | offload active — `mode crypto` against the physical port |
| `et10g-0`, transport, AES-GCM-256 | rejected: *"IPsec hw offload only supports keys up to 128 bits with a 32 bit salt"* |
| `et10g-0`, **tunnel** mode | rejected: *"Unsupported mode for ipsec offload"* |
| `lan-hi` (a VLAN on `et10g-0`) | **accepted with the offload silently dropped** — software crypto |
| `et2g5-0` (I226-V) | accepted, offload silently dropped — `igc` has none |
Two traps are worth knowing. Binding an SA to a device that cannot offload is **not** an error:
`xfrm_dev_state_add` returns success having cleared the device, so the SA looks fine and quietly
runs in software. And a VLAN interface never offloads — it carries no `xfrmdev_ops`, and
`esp-hw-offload` reads `off [fixed]` on it even when its parent supports the feature.
The second trap is the decisive one here. Even with the offload genuinely active against
`et10g-0`, driving traffic through the SA left `tx_ipsec` at zero, because the packets egress
`lan-hi` and the kernel only offloads when the SA's device matches the egress device. Every address
`portcullis` holds is on a VLAN, so an SA would have to be bound to an untagged physical port to
see the hardware at all.
So adopting it would mean dropping to a 128-bit key to suit one NIC family, keeping the underlay
off VLANs, and forgoing `udpEncapsulation` — `xfrm_dev_offload_ok` refuses any SA carrying
`encap`. That is not a trade worth making for a mesh that has to run across boxes with no offload
at all.
#### pcrypt
`pcrypt` parallelises an SA's crypto across cores via padata and does lift the per-SA ceiling. It
is not enabled on any box here, and is recorded as an option rather than a recommendation:
- It cannot be named in the SA — `ip xfrm` and the kernel both validate AEAD names against a fixed
list. It is engaged instead by registering a `pcrypt(...)`-wrapped instance under the standard
algorithm name at a higher priority, over `NETLINK_CRYPTO` (`CONFIG_CRYPTO_USER`). `crconf` is
the usual tool for that and is not packaged in nixpkgs.
- The registration is global: it redirects every user of that algorithm on the box, not just the
mesh, and would have to run before `ipsec` starts.
- A single-threaded submit path remains, so it does not scale with core count, and it trades
latency and packet ordering for throughput.
### Overlay addressing
The overlay uses `10.100.50.0/24` / `2a0e:97c0:4df::/64`. Each router holds `10.100.50.<n>`:
+49 -13
View File
@@ -2,13 +2,14 @@
Procedure for the periodic upgrade of all four nixpkgs channels (`unstable`, `stable`, `mine`,
`mine-stable`) and home-manager. Written to be followed by a person or any coding agent; a
Claude Code entry point exists at `.claude/skills/upgrade-nixpkgs/` but the steps below are the
canonical source.
shared agent-skill entry point exists at `.agents/skills/upgrade-nixpkgs/`, but the steps below are
the canonical source.
The upgrade is **guided, not automated**: do the mechanical and investigative steps, but stop at
the judgment points (marked ⏸) — pushing the fork, resolving rebase conflicts, editing the
`flake.nix` stable pins, and deleting version guards. Report findings and let the maintainer
decide. Keep a running summary and present it before any push or commit.
stable-channel configuration, choosing a new release codename, and deleting version guards. Report
findings and let the maintainer decide. Keep a running summary and present it before any push or
commit.
Work the phases in order; skip one only if explicitly scoped to a subset.
@@ -43,6 +44,7 @@ stable pins) has to agree on one NixOS stable release, so establish it up front.
the pieces must all move to the same release together:
- Rebase `devplayer0-stable` onto the new `upstream/release-YY.NN` (Phase 2 uses this target).
- Edit `flake.nix`: `nixpkgs-stable.url` and `home-manager-stable.url` → the new release.
- Choose a new `lib/default.nix` `versionOverlay` codename (Phase 4 updates it).
- Bump each system's `stateVersion` / `home.stateVersion` only if the maintainer explicitly
wants to — that is a separate, deliberate decision; never auto-bump.
Don't edit `flake.nix` here without confirmation.
@@ -70,6 +72,12 @@ For **both** branches — `devplayer0` onto `upstream/nixos-unstable`, and `devp
conflicted.
6. ⏸ **Push:** only after confirmation. `git push --force-with-lease origin devplayer0
devplayer0-stable` (force needed — rebase rewrites history).
7. Wait for the GitHub mirror used by the flake inputs to catch up with the primary fork remote.
Compare the local branch tips with
`git ls-remote https://github.com/devplayer0/nixpkgs.git refs/heads/devplayer0
refs/heads/devplayer0-stable` and do not continue until both match. Updating sooner can leave
`nixpkgs-mine` and `nixpkgs-mine-stable` pinned to the pre-rebase commits even though the push
succeeded.
## Phase 3 — Update the pinned inputs
@@ -83,7 +91,28 @@ update-home-manager
Then show the `flake.lock` diff for the nixpkgs/home-manager entries so the old→new revisions are
visible.
## Phase 4 — Sweep version-gated behavior
## Phase 4 — Refresh kernels and release metadata
Update the repository values that deliberately move with nixpkgs upgrades:
1. In `lib/constants.nix`, inspect the kernel attributes available from the refreshed nixpkgs pins
and update both explicit selections:
- `kernel.lts` → the newest upstream long-term-support kernel carried by nixpkgs.
- `kernel.latest` → the newest kernel series carried by nixpkgs.
Keep explicit `pkgs.linuxKernel.packages.linux_X_Y` attributes rather than replacing them with
moving aliases. Confirm both attributes exist in the unstable and stable package sets used by
the boxes; if the newest choice is unavailable on stable, report that instead of breaking the
shared constant.
2. In `lib/default.nix`, update the `versionOverlay` values:
- Set the leading `YY.MM` in `trivial.release` to the current year and month. Preserve the
`:u-${prev.trivial.release}` suffix.
- If Phase 1 found a new NixOS stable release, ⏸ ask the maintainer to choose or approve a new
`trivial.codeName`, then update it as part of the coordinated stable bump. Otherwise retain the
existing codename.
Show these edits alongside the input changes in the upgrade summary.
## Phase 5 — Sweep version-gated behavior
The repo carries branch-conditional logic and TODOs keyed to specific nixpkgs versions; some become
removable after an upgrade, especially after a stable bump. Surface them:
@@ -97,29 +126,36 @@ Known example: `nixos/modules/common.nix` carries a `# TODO: Remove if-else when
guard. For each hit, evaluate whether the now-current versions make the guard removable and list
candidates. ⏸ Don't delete guards without confirmation — some protect the still-supported stable.
## Phase 5 — Review remaining flake inputs
## Phase 6 — Review remaining flake inputs
Don't blanket-update. Walk the other inputs deliberately:
1. List inputs and locked revisions from `flake.lock` (or `nix flake metadata`).
2. For each meaningful input (`libnetRepo`, `devshell`, `determinate-nix`, `ragenix`, `deploy-rs`,
`impermanence`, and the packaged apps like `boardie`, `harmonia`, `copyparty`, `sharry`, …),
`impermanence`, and the packaged apps like `boardie`, `harmonia`, and `copyparty`),
compare the locked revision to upstream and summarize notable changes (breaking changes,
relevant fixes). Many inputs `follows` `nixpkgs-unstable` and already moved in Phase 3.
3. Propose a per-input update list with reasons; update the approved ones with targeted
`nix flake update <input>`, not a global update.
## Phase 6 — Validate
## Phase 7 — Validate
1. `nix flake check --no-build` (broad eval; reproduces CI's cheap checks).
2. `check-system <host>` on a representative box, and one exercising the stable channel if the
boxes mix channels.
3. Report eval/build results honestly. On failure, surface the error and stop rather than papering
boxes mix channels. This must exercise the refreshed kernel constants on both channels.
3. Build the actual devshell with
`nix build --no-link --print-out-paths .#devShells.x86_64-linux.default`. Evaluation does not
build its dependencies, so it cannot catch packaging conflicts introduced by inputs such as
Determinate Nix.
4. After the evaluations pass, run `build-system <host>` for one representative NixOS box. Prefer
the local box when it is managed by this flake: its full closure is likely to exercise the most
relevant packages, home-manager configuration and upgraded kernel. Build only; do not switch.
5. Report eval/build results honestly. On failure, surface the error and stop rather than papering
over it.
## Wrap-up
Present a final summary: fork rebase outcome (patches kept/dropped/conflicted), whether a stable
bump is pending or was applied, the lock diff, version-gate cleanup candidates, inputs updated, and
validation results. Leave committing to the maintainer unless asked; if committing, follow the
repo's `area/scope: Capitalized summary` convention.
bump is pending or was applied, kernel and release-metadata changes, the lock diff, version-gate
cleanup candidates, inputs updated, and validation results. Leave committing to the maintainer
unless asked; if committing, follow the repo's `area/scope: Capitalized summary` convention.
+3 -3
View File
@@ -3,7 +3,7 @@
Guided procedure for putting a flake-built OpenWrt image onto a box. The images themselves are
declared in [`openwrt/default.nix`](../openwrt/default.nix) and described in
[`deployment.md`](deployment.md#openwrt-images); the boxes are listed on their site pages (today
that is [fergal](sites/home/switches.md#fergal-the-openwrt-switch)).
that is [fergal](sites/colony/fergal.md)).
Packages are baked into the image, so this runs whenever the package list changes — not only for
version upgrades. Work through the phases in order; ⏸ marks the point to stop and confirm.
@@ -32,7 +32,7 @@ df -h /tmp # room for the image
**Flash or RAM matters.** A box booted normally shows a squashfs `/rom` plus a jffs2 `/overlay`;
one booted from an initramfs has `/` on tmpfs. The initramfs case has its own hazards — see
[Flashing from an initramfs](sites/home/switches.md#flashing-notes).
[Flashing from an initramfs](sites/colony/fergal.md#flashing-notes).
**Check the address is in UCI**, not just present on the interface. An address added by hand with
`ip` disappears on reboot and the box comes back unreachable.
@@ -53,7 +53,7 @@ protocol if you prefer it.)
For a box being flashed off its **vendor** firmware for the first time, back up the whole flash
first — the vendor partitions hold per-unit MAC addresses and licence data that cannot be
regenerated. See [fergal's flash layout](sites/home/switches.md#flash-layout).
regenerated. See [fergal's flash layout](sites/colony/fergal.md#flash-layout).
## Phase 4 — Stage and validate
+4 -4
View File
@@ -24,7 +24,7 @@
| `my.borgthin.jobs.<name>.repo` | string | `null` | borg repository URL |
| `my.borgthin.jobs.<name>.timer.at` | string or list of string | `"5:00"` | systemd calendar time(s) to run backup at |
| `my.borgthin.jobs.<name>.timer.persistent` | boolean | `false` | Persistent systemd timer |
| `my.borgthin.lvmPackage` | package | `<derivation lvm2-2.03.39>` | Packge containing LVM tools |
| `my.borgthin.lvmPackage` | package | `<derivation lvm2-2.03.41>` | Packge containing LVM tools |
| `my.borgthin.package` | package | `inputs.borgthin.packages.${system}.borgthin` | borgthin package |
| `my.borgthin.thinToolsPackage` | package | `<derivation thin-provisioning-tools-1.3.2>` | Package containing thin-provisioning-tools |
@@ -167,7 +167,7 @@
| `my.nginx-sso.includes.instances.<name>.auth.redirect` | string | `"$scheme://$http_host$request_uri"` | URL to redirect to upon successful login. |
| `my.nginx-sso.includes.instances.<name>.logout.path` | string | `"/sso-logout"` | HTTP path for SSO logout. |
| `my.nginx-sso.includes.instances.<name>.logout.redirect` | string | `"$scheme://$http_host/"` | URL to redirect to upon successful logout. |
| `my.nginx-sso.package` | package | `<derivation nginx-sso-0.27.7>` | nginx-sso package to use. |
| `my.nginx-sso.package` | package | `<derivation nginx-sso-0.27.8>` | nginx-sso package to use. |
## `nvme` — [`nixos/modules/nvme`](../../nixos/modules/nvme)
@@ -276,11 +276,11 @@
| `my.vms.instances.<name>.networks.<name>.model` | string | `"virtio-net"` | Device type for network interface. |
| `my.vms.instances.<name>.networks.<name>.tapFD` | null or (unsigned integer, meaning >=0) | `null` | FD to use to pass existing TAP device. |
| `my.vms.instances.<name>.networks.<name>.waitOnline` | boolean or string | `true` | Whether to wait for networkd to consider the bridge / existing TAP device online. Pass a string to set the OPERSTATE will wait for. |
| `my.vms.instances.<name>.qemuBin` | absolute path | `"/nix/store/w4yhckm5wyvml3pqw8ai5fl174j14nrb-qemu-host-cpu-only-11.0.0/bin/qemu-kvm"` | Path to QEMU executable. |
| `my.vms.instances.<name>.qemuBin` | absolute path | `"/nix/store/2lkr0v29a67jybn8ckjawpg08y0yizfp-qemu-host-cpu-only-11.1.0/bin/qemu-kvm"` | Path to QEMU executable. |
| `my.vms.instances.<name>.qemuFlags` | list of string | `[ ]` | Additional flags to pass to QEMU. |
| `my.vms.instances.<name>.smp.cpus` | unsigned integer, meaning >=0 | `1` | Number of CPU cores. |
| `my.vms.instances.<name>.smp.threads` | unsigned integer, meaning >=0 | `1` | Number of threads per core. |
| `my.vms.instances.<name>.spice.enable` | boolean | `true` | Whether to enable SPICE. |
| `my.vms.instances.<name>.uuid` | string | `null` | QEMU machine UUID. |
| `my.vms.instances.<name>.vga` | string | `"virtio"` | VGA card type. |
| `my.vms.ovmfPackage` | package | `<derivation OVMF-202602>` | OVMF package. |
| `my.vms.ovmfPackage` | package | `<derivation OVMF-202605>` | OVMF package. |
+4 -1
View File
@@ -33,4 +33,7 @@ The applications running on `shill` are listed on its own page — see
pages document only what this repository controls.
`portcullis` is new hardware headed for Nikhef that will take over most of `estuary`'s edge routing.
It is not deployed yet and the resulting topology is still being worked out.
It is not deployed yet and the resulting topology is still being worked out. It travels with
[`fergal`](fergal.md), an OpenWrt SFP+ switch whose firmware this flake builds; both are staged at
home for now, borrowing the home fabric through
[jim](../home/switches.md#fergal-portculliss-switch).
+104
View File
@@ -0,0 +1,104 @@
# fergal
An 8-port SFP+ switch running OpenWrt, bought to sit in front of
[`portcullis`](portcullis.md) at Nikhef. It is physically at home for now, on the bench alongside
`portcullis` while that box is staged.
- **Source:** firmware built by this flake — [`openwrt/default.nix`](../../../openwrt/default.nix)
- **Host:** bare metal
- **OS:** OpenWrt (snapshot), configured through UCI rather than RouterOS or a UniFi controller
## Hardware
| Component | Inventory |
|---|---|
| Platform | XikeStor SKS8300-8X; the board itself is branded ONTi ONT-S508CL-8S |
| SoC | Realtek RTL9303 (MIPS 34Kc) |
| Memory | 512 MB |
| Storage | 32 MiB SPI NOR (`spi0.0`) |
| Network | 8×SFP+ (`lan1`…`lan8`) |
## Role
`portcullis`'s 10G switch. Nothing else depends on it, and it is not part of the home fabric — it
is expected to travel to Nikhef with `portcullis` rather than stay behind.
While staged at home it hangs off jim's spare SFP+ port, so `portcullis` can reach the home `hi`
VLAN over 10G: `lan1` uplinks to jim's `sfp-spare`, `lan2` goes to `portcullis`, and the other six
cages are empty. See [the home switches](../home/switches.md) for the fabric it borrows.
## Network assignments
fergal has no assignments — it is not managed by the flake. Its management address is
`192.168.64.30` on the home `core` VLAN, set in UCI as `network.lan`, with no DNS record; reach it
as `ssh root@192.168.64.30`.
## VLAN configuration
One bridge (`switch`), with VLAN 1 as the untagged PVID on every port — that's the native VLAN on
jim's `sfp-spare`, and `switch.1` is where fergal's own management address lives. `hi` (100) and
`lo` (110) are **tagged** members of every port, so a box on any cage can pick them up:
```
uci show network | grep bridge-vlan
```
Tagging all eight rather than just `lan1`/`lan2` keeps a spare cage usable without a reconfigure;
there is nothing sensitive behind it while fergal is on the bench.
**Jumbo frames pass, despite what `ip link` says.** Every DSA port and the `switch` bridge read
`mtu 1500`, but the RTL9303 forwards between ports in hardware and isn't bound by those — a
`ping -M do -s 8972` from `portcullis` to the `hi` VIP crosses fergal intact, which is what makes
the 9000-MTU `hi` VLAN usable over this path. The 1500 does apply to traffic punted to the CPU,
i.e. fergal's own management on `switch.1`.
## Firmware
The image is built by this flake — see [OpenWrt images](../../deployment.md#openwrt-images) for the
outputs and the feed pin. Packages are baked into the image, so adding tooling means editing
[`openwrt/default.nix`](../../../openwrt/default.nix) and reflashing rather than installing on the
box.
### Flash layout
A single 32 MiB SPI NOR chip (`spi0.0`, 64 KiB erase blocks). `kernel` and `rootfs` are
sub-partitions of `firmware`, and OpenWrt adds `rootfs_data` as the JFFS2 overlay after a real
flash.
| Partition | Device | Offset | Size |
|---|---|---|---|
| `u-boot` | `mtd0` | `0x000000` | 1 MiB |
| `board-info` | `mtd1` | `0x100000` | 192 KiB |
| `syslog` | `mtd2` | `0x130000` | 832 KiB |
| `firmware` | `mtd3` | `0x200000` | 30 MiB |
**`board-info` is irreplaceable.** It holds the unit's MAC addresses (`[vlanmac]` / `[cpumac]`), its
`[license]` hash, the stock boot pointers and an SSH host key — only about 1.3 KiB of it is
non-blank, and none of it can be regenerated. A full dump of all four partitions, taken before
OpenWrt was flashed, is kept outside this repo — 33 MB of images, with per-partition checksums and
restore notes. Never write `u-boot` or `board-info` without a confirmed serial/TFTP recovery path.
### Flashing notes
The procedure itself is in [`openwrt-flash.md`](../../openwrt-flash.md); what follows is specific to
this board.
Stock u-boot boots `flash:/nos.img` from a JFFS2 filesystem, so OpenWrt's sysupgrade image is
itself a JFFS2 image containing `nos.img` rather than a raw kernel + squashfs. Two things bite when
flashing from an initramfs, as during the initial install:
- **`sysupgrade -c` does not work.** It needs `/overlay/upper/etc`, which doesn't exist when running
from RAM, and it aborts *after* `mtd erase firmware` has already run — leaving the box with no
bootable firmware until the job is finished. Pass the config as an explicit tarball instead
(`tar czf`, then `sysupgrade -f <tarball> …`).
- **The working management address may not be in UCI.** If it was set by hand with `ip` while UCI
still held the stock address, the box comes back unreachable. Write it into `network.lan` and
commit before flashing.
Neither applies to an ordinary flash-to-flash upgrade, where `sysupgrade` keeps `/etc/config` and
the files listed in `/lib/upgrade/keep.d/` by default. Dropbear host keys are regenerated by a flash
that doesn't preserve them, so clear the old `known_hosts` entry afterwards.
## Notable config files
- [`openwrt/default.nix`](../../../openwrt/default.nix) — image definition and baked-in package list.
+68 -8
View File
@@ -18,6 +18,11 @@ routing currently done by the [`estuary`](estuary.md) VM.
| Network | Four Intel I226-V 2.5 GbE ports (`et2g5-0`…`et2g5-3`) and one dual-port Intel 82599ES 10 GbE SFP+ card (`et10g-0`, `et10g-1`) |
| Management | JetKVM (HDMI/USB KVM with virtual media) |
The PCIe layout constrains what the cards can reach. The 82599ES sits behind a gen2 x4 link giving
16 Gb/s for **both** its ports together, so one port runs at line rate but the pair is
oversubscribed. The NVMe is on a x1 root port, capped near 7.9 Gb/s regardless of the drive. Each
I226-V has its own x1 link and is not constrained.
## Role
Not yet in service. The eventual job is to be the physical edge for the colony site at Nikhef,
@@ -28,19 +33,74 @@ this repository covers only what is needed to boot and reach the box.
## Network assignments
`portcullis` has no static assignments yet. It is being staged at home before it is racked, so it
takes DHCP on the home `lo` VLAN; the colony assignments land alongside the routing config once the
topology is decided.
`portcullis` has no colony assignments yet — those land alongside the routing config once the
topology is decided. While it is staged at home it holds a single home `hi` assignment, listed in
[`networking.md#box-assignments`](../../networking.md#box-assignments).
## Networking
- The four I226-V ports are named `et2g5-0`…`et2g5-3` and the 82599ES SFP+ ports `et10g-0` /
`et10g-1`, pinned by permanent MAC address in `.link` files.
- Bootstrap only: a single `.network` matches every `et2g5-*` port and takes DHCP, so whichever
port happens to be patched in brings the box up. `wait-online.anyInterface` keeps boot from
blocking on the unpatched ports.
- kea registers the DHCP hostname, so while staged the box answers to `portcullis.dyn.h.nul.ie` —
which is also what `my.deploy.node.hostname` points at, since there is no colony FQDN for it yet.
- Bootstrap: a single `.network` matches every `et2g5-*` port and takes DHCP on the home `lo` VLAN,
so whichever port happens to be patched in brings the box up. `wait-online.anyInterface` keeps
boot from blocking on the unpatched ports.
- kea registers the DHCP hostname, so while staged the box also answers to `portcullis.dyn.h.nul.ie`.
- `my.deploy.node.hostname` is the `hi` address, taken from the assignment rather than written out,
since there is no colony FQDN for the box yet.
### 10G to the home `hi` VLAN
`et10g-0` runs over fibre to [`fergal`](fergal.md), which uplinks to jim's `sfp-spare` port. That
uplink is untagged VLAN 1, so `hi` is carried tagged on a `lan-hi` VLAN interface rather than on the
port itself; the physical link takes the `hi` jumbo MTU so the whole path is consistent with the
rest of the VLAN. `lan-hi` carries the static assignment, resolves through the router VIPs like
every other `hi` client, and its gateway route outranks the DHCP default, so the 10G path is
preferred while the 2.5G one stays as a fallback.
Both jim and `fergal` tag `hi` and `lo` along that path. It exists only while the box is staged at
home — `fergal` goes to Nikhef with it.
The other SFP+ port, `et10g-1`, is unused.
### Interface tuning
Every port takes router-sized 4096-entry rings rather than the driver defaults, matching the other
routers here, and enables `GenericReceiveOffloadUDPForwarding` so GRO batching survives forwarding
once the box carries UDP-encapsulated traffic. Both are `.link` settings, so they apply on the next
device event rather than at switch time — a reboot is the reliable way to land a change to them.
Interrupt coalescing is deliberately left alone. `igc` reports `rx-usecs` 3 and `ixgbe` reports 1,
which are the drivers' markers for dynamic ITR rather than literal microseconds; writing a
plausible-looking value there replaces adaptive moderation with a fixed one.
### I226-V erratum
The I226-V link-drop erratum is driven by PCIe ASPM, Energy Efficient Ethernet and stale NIC
firmware. ASPM, the dominant cause, is off across the whole box for the reason in
[Power](#power) below. EEE is held off by a udev rule invoking `ethtool`, as `systemd.link` has no
knob for it. `igc` already leaves EEE off on these ports, so the rule pins a driver default rather
than correcting one, and keeps it from drifting on a kernel bump. Firmware is the remaining item:
the ports report NVM `2.13` (EEPROM version word `0x2013`, which `igc` prints as the `2013` in
`ethtool -i`), behind the `2.29`/`2.32` images that circulate. Intel does not publish the I226-V
NVM image, so updating means third-party firmware and is best attempted while the box is at home
and the JetKVM is attached.
## Power
The SoC side is already at its floor and needs no tuning: the package draws around 0.75 W idle with
cores in C10 essentially all the time, under `intel_pstate` on the `powersave` governor.
Platform idle is capped instead, and deliberately left that way. The ACPI FADT declares that the
system does not support PCIe ASPM, so the OS defers to firmware, every root port advertises ASPM as
unsupported and every endpoint sits with it disabled. Deep package C-states need every PCIe link in
L1, so the package never leaves C3. `pcie_aspm=force` is the usual answer and is **not** used here:
the 82599ES advertises only L0s with an unlimited exit latency, so no amount of forcing reaches the
deep states while that card is fitted, and the only links it would actually change are the four
I226-V ones — the exact configuration behind the erratum above. Recovering that power is a firmware
question for the mini PC, and only worthwhile once the 82599ES is gone.
`iommu=pt` puts host devices in passthrough so the forwarding path does not pay DMA translation,
while leaving the IOMMU available.
## Storage
+1 -1
View File
@@ -53,7 +53,7 @@ their current addresses. Each container has its own page:
| [`colony-psql`](containers/colony-psql.md) | Shared PostgreSQL (14) |
| [`chatterbox`](containers/chatterbox.md) | Matrix Synapse + bridges |
| [`jackflix`](containers/jackflix.md) | Media stack |
| [`object`](containers/object.md) | MinIO, Harmonia Nix cache, Sharry, HedgeDoc, wastebin |
| [`object`](containers/object.md) | MinIO, Harmonia Nix cache, HedgeDoc, wastebin |
| [`toot`](containers/toot.md) | Bluesky PDS (Mastodon disabled) |
| [`waffletail`](containers/waffletail.md) | Tailscale subnet router / exit node |
| [`qclk`](containers/qclk.md) | WireGuard management appliance |
@@ -15,7 +15,7 @@ database, the containers (and the `git` VM) connect here over the `ctrs` network
the ident map.
- **netdata** with the Python PostgreSQL collector.
- Consumers wait for the database to accept connections with the `lib.my.systemdAwaitPostgres`
helper (e.g. `sharry`, `atticd`, `mastodon-init-db`, and `middleman`'s nginx as a DNS
helper (e.g. `atticd`, `mastodon-init-db`, and `middleman`'s nginx as a DNS
bootstrap hack).
## Network assignments
@@ -27,7 +27,7 @@ use as the database hostname.
## Consumers
- [object](object.md) — `sharry` and `hedgedoc` (and `atticd` when enabled) over
- [object](object.md) — `hedgedoc` (and `atticd` when enabled) over
`colony-psql:5432`
- [toot](toot.md) — Mastodon's database (Mastodon currently disabled)
- [chatterbox](chatterbox.md) — the mautrix bridges (WhatsApp, Messenger, Instagram) via
@@ -71,7 +71,6 @@ all vhosts are `onlySSL`, kTLS and HTTP/2. "SSO" = gated behind nginx-sso (`gene
| `jackflix.nul.ie` | `jackflix-ctr:8096` | Jellyfin; `/socket` websockets; `/` redirects to `/web/` |
| `toot.nul.ie` | `toot-ctr:80` | Mastodon — **upstream currently disabled**, see [toot](toot.md) |
| `pds.nul.ie` | `toot-ctr:3000` | Bluesky PDS ([toot](toot.md)); websockets |
| `share.nul.ie` | `object-ctr:9090` | Sharry ([object](object.md)); websockets |
| `stuff.nul.ie` | `jackflix-ctr:3923` | copyparty |
| `public.nul.ie` (+ alias `p.nul.ie`) | static `/mnt/media/public` | fancyindex file listing; `addSSL` so plain HTTP also works |
| `mc-map.nul.ie` | `simpcraft-oci:8100` | Minecraft map (OCI container on [`whale2`](../../whale2.md#game-servers)) |
+3 -5
View File
@@ -1,7 +1,6 @@
# object
Object storage and the Nix binary cache, plus a few small self-hosted web apps (Sharry,
HedgeDoc, wastebin).
Object storage and the Nix binary cache, plus HedgeDoc and wastebin.
- **Source:** [`shill/containers/object.nix`](../../../../../nixos/boxes/colony/vms/shill/containers/object.nix)
- **Host:** NixOS container on [`shill`](../README.md) (bind-mounts `/mnt/minio` and
@@ -14,7 +13,6 @@ HedgeDoc, wastebin).
| --- | --- | --- |
| MinIO | `9000` (S3) / `9001` (console) | S3-compatible object storage, `s3.nul.ie` + `*.s3.nul.ie` (virtual-host style via `MINIO_DOMAIN`), console at `minio.nul.ie`; region `eu-central-1`; data on the `/mnt/minio` XFS volume |
| Harmonia | `5000` | Nix binary cache at `nix-cache.nul.ie` — `harmonia-dev` cache serves `shill`'s `/nix/store` out of a dedicated store view rooted at `/var/lib/harmonia` (bind-mounted from `/mnt/nix-cache`), signed with the `nix-cache.key` secret; a `harmonia` user with authorized keys exists for cache pushes |
| Sharry | `9090` | file sharing at `share.nul.ie`; Postgres on [colony-psql](colony-psql.md), files stored in the `share` MinIO bucket; fixed `dev` account + invite signup; mail via `mail.nul.ie`; configured share-size limit |
| HedgeDoc | `3000` | collaborative markdown notes at `md.nul.ie`; Postgres on [colony-psql](colony-psql.md); anonymous edits but no anonymous notes, email login, no open email registration |
| wastebin | `8088` | pastebin at `pb.nul.ie` |
| atticd | `8069` | **currently disabled** (`services.atticd.enable = false`) — an alternative Nix cache that would store locally and sit behind `nix-cache.nul.ie`; config (including the `object/atticd.env` secret) is kept around |
@@ -29,10 +27,10 @@ See the consolidated [network assignments](../../../../networking.md#box-assignm
## Backing services
- [colony-psql](colony-psql.md) — Sharry and HedgeDoc databases (atticd too, when enabled).
- [colony-psql](colony-psql.md) — HedgeDoc's database (atticd too, when enabled).
- MinIO buckets back other boxes' services: Gitea LFS/packages (with the `middleman` MIME hack
for Docker manifests), Mastodon's `mastodon` bucket and the Bluesky PDS `pds` bucket on
[toot](toot.md), and Sharry's `share` bucket.
[toot](toot.md).
## Notable config files
+2 -1
View File
@@ -48,7 +48,8 @@ forwarded by `estuary`.
| `graeme` | `25569` tcp+udp | running |
- **valheim** ([`valheim.nix`](../../../nixos/boxes/colony/vms/whale2/valheim.nix)) —
`lloesche/valheim-server`, public server "amogus sus", world `simpland2`,
`community-valheim-tools/valheim-server`, public server "amogus sus", world `simpland3`
(previous world `simpland2` retained in the `valheim_data` volume),
allow-listed Steam IDs, password from agenix.
- **simpcraft** ([`minecraft/`](../../../nixos/boxes/colony/vms/whale2/minecraft)) —
`itzg/minecraft-server` (self-built `git.nul.ie/dev/craftblock` image),
+22 -62
View File
@@ -11,9 +11,8 @@ carried untranslated because a single ONT makes it unique on the fabric — see
[the WAN path](#the-digiweb-wan-path-trunked-vlan-10--pvid-140) and
[why not translation](#why-not-translation-for-one-ont). The router side lives in
[river.md](river.md); the logical network map in [networking.md](../../networking.md). The Wi-Fi
APs that hang off these switches are in [aps.md](aps.md). A fourth switch, **fergal**, runs OpenWrt
and is on the bench rather than in the production path — see
[fergal](#fergal-the-openwrt-switch).
APs that hang off these switches are in [aps.md](aps.md). A fourth switch, **fergal**, hangs off jim
but belongs to the colony site — see [fergal](#fergal-portculliss-switch).
## The switches
@@ -36,13 +35,14 @@ chips); brian cannot rewrite tags, only trunk/PVID them.
The two WAN sources enter at the top: the Virgin Media modem lands on **jim** (VLAN 130), and the
Digiweb **ONT** lands on **brian**. Both `jim` and `brian` are edge switches that uplink down into
the **dave** core; the home boxes hang off dave's 100G ports, with backup links up to jim. jim's
`wan-pon-in` (`sfp-sfpplus2`) is a spare SFP+ port, unused today.
second SFP+ port (`sfp-spare`, `sfp-sfpplus2`) feeds [fergal](#fergal-portculliss-switch), which
[`portcullis`](../colony/portcullis.md) hangs off while it is staged at home.
```
Virgin Media cable modem Digiweb ONT
stream WAN, VLAN 130 river WAN, management + VLAN 10
| |
jim brian
jim ---- 10G ---- fergal ---- portcullis brian
| 10G trunk 802.3ad LAG |
+--------------------+ +---------------+
| |
@@ -148,8 +148,13 @@ VLAN 140 also spans `brian-downlink,palace` (it carries a few other members too)
this is plain tagged bridging.
**jim (RouterOS)** — carries **none** of the Digiweb WAN path: no translation rules, and no VLAN
10/140/141 rows. `wan-pon-in` (`sfp-sfpplus2`) sits at `pvid=1` as a spare port. jim only handles
stream's VLAN-130 WAN and the LAN VLANs.
10/140/141 rows. jim only handles stream's VLAN-130 WAN and the LAN VLANs. `sfp-spare`
(`sfp-sfpplus2`) stays at `pvid=1` — the switch feeding `portcullis` is reached over VLAN 1
untagged — and is a **tagged** member of `hi` (100) and `lo` (110) so those reach `portcullis`:
```
/interface bridge vlan set [find bridge=main vlan-ids=100] tagged=...,sfp-spare
/interface bridge vlan set [find bridge=main vlan-ids=110] tagged=...,sfp-spare
```
## Switches must not route
@@ -202,66 +207,21 @@ Each ONT port must also be a tagged member of bridge VLAN 10 for correct egress
piece that otherwise shows up as pppd "Timeout waiting for PADO"). The pins bypass the FDB, so the
two ISP sessions never mix.
**Why a new switch:** jim (the only box with spare SFP+ *and* the translation feature) has just
**one** free SFP+ port, so it can't host two ONTs. The plan is a dedicated
**Why a new switch:** jim (the only box with spare SFP+ *and* the translation feature) had just
**one** free SFP+ port — now taken by fergal — so it can't host two ONTs. The plan is a dedicated
**CRS305-1G-4S+** (4×SFP+, same Marvell rule support) to land multiple ONTs and do the per-port
translation there, feeding distinct fabric VLANs up to dave.
## fergal, the OpenWrt switch
## fergal (portcullis's switch)
An 8-port SFP+ switch — **XikeStor SKS8300-8X**, the board itself branded **ONTi ONT-S508CL-8S** —
on a Realtek RTL9303 (MIPS 34Kc, 512 MB RAM, 32 MiB SPI NOR). Unlike jim, dave and brian it runs
**OpenWrt**, so it is configured through UCI rather than RouterOS or a UniFi controller.
**fergal** is an 8-port SFP+ switch running OpenWrt, hanging off jim's `sfp-spare` port. It belongs
to [`portcullis`](../colony/portcullis.md) rather than to the home fabric — it is here only while
that box is staged at home, and goes to Nikhef with it. Nothing in the home fabric depends on it.
fergal is **not yet part of the fabric**: it sits at `192.168.64.30` on core (no DNS record yet),
still has the stock single-VLAN bridge with all eight ports untagged, and only one SFP+ cage is
populated. Treat it as bench equipment until that changes.
Its firmware *is* built by this flake — see
[OpenWrt images](../../deployment.md#openwrt-images) for the outputs and the feed pin. Packages are
baked into the image, so adding tooling means editing
[`openwrt/default.nix`](../../../openwrt/default.nix) and reflashing rather than installing on the
box.
### Flash layout
A single 32 MiB SPI NOR chip (`spi0.0`, 64 KiB erase blocks). `kernel` and `rootfs` are
sub-partitions of `firmware`, and OpenWrt adds `rootfs_data` as the JFFS2 overlay after a real
flash.
| Partition | Device | Offset | Size |
|---|---|---|---|
| `u-boot` | `mtd0` | `0x000000` | 1 MiB |
| `board-info` | `mtd1` | `0x100000` | 192 KiB |
| `syslog` | `mtd2` | `0x130000` | 832 KiB |
| `firmware` | `mtd3` | `0x200000` | 30 MiB |
**`board-info` is irreplaceable.** It holds the unit's MAC addresses (`[vlanmac]` / `[cpumac]`), its
`[license]` hash, the stock boot pointers and an SSH host key — only about 1.3 KiB of it is
non-blank, and none of it can be regenerated. A full dump of all four partitions, taken before
OpenWrt was flashed, is kept outside this repo — 33 MB of images, with per-partition checksums and
restore notes. Never write `u-boot` or `board-info` without a confirmed serial/TFTP recovery path.
### Flashing notes
The procedure itself is in [`openwrt-flash.md`](../../openwrt-flash.md); what follows is specific to
this board.
Stock u-boot boots `flash:/nos.img` from a JFFS2 filesystem, so OpenWrt's sysupgrade image is
itself a JFFS2 image containing `nos.img` rather than a raw kernel + squashfs. Two things bite when
flashing from an initramfs, as during the initial install:
- **`sysupgrade -c` does not work.** It needs `/overlay/upper/etc`, which doesn't exist when running
from RAM, and it aborts *after* `mtd erase firmware` has already run — leaving the box with no
bootable firmware until the job is finished. Pass the config as an explicit tarball instead
(`tar czf`, then `sysupgrade -f <tarball> …`).
- **The working management address may not be in UCI.** If it was set by hand with `ip` while UCI
still held the stock address, the box comes back unreachable. Write it into `network.lan` and
commit before flashing.
Neither applies to an ordinary flash-to-flash upgrade, where `sysupgrade` keeps `/etc/config` and
the files listed in `/lib/upgrade/keep.d/` by default. Dropbear host keys are regenerated by a flash
that doesn't preserve them, so clear the old `known_hosts` entry afterwards.
What it borrows from home is VLAN 1 untagged on the jim uplink (fergal's own management sits on it,
at `192.168.64.30` on core) plus tagged `hi` (100) and `lo` (110), so `portcullis` can reach those
over 10G. The switch itself — VLAN layout, flash layout, firmware and flashing notes — is
documented in [sites/colony/fergal.md](../colony/fergal.md).
## Accessing the switches
Generated
+90 -254
View File
@@ -75,11 +75,11 @@
]
},
"locked": {
"lastModified": 1781351267,
"narHash": "sha256-86HFs1K+LRlx8t4AjaMdU5qlg4O7kLz1VlnNapKZIuY=",
"lastModified": 1787524125,
"narHash": "sha256-P48TOQdIbB0PKMn4FTk6X0utbf0LemNlJ+bFcSbveGA=",
"owner": "9001",
"repo": "copyparty",
"rev": "90639de9840d7dcc2d9000026fe547f666c1d550",
"rev": "9de090265f8d063056320f41d984830839017a2f",
"type": "github"
},
"original": {
@@ -90,11 +90,11 @@
},
"crane": {
"locked": {
"lastModified": 1780532242,
"narHash": "sha256-D+BsdpxmtUwtqGoY0IXPhHgTlmqgcZKCEo1oMyn7ep0=",
"lastModified": 1787326676,
"narHash": "sha256-lWhBbBvC05/xwivKBBiM2YNizpmgqCgyOIzomvRuwxs=",
"owner": "ipetkov",
"repo": "crane",
"rev": "59a82a1222dd3b2080b5cc52a1a2e8d5f1b77f37",
"rev": "692f7e9ef2ece8125b466f66f2af532b3edaed0d",
"type": "github"
},
"original": {
@@ -150,11 +150,11 @@
"utils": "utils"
},
"locked": {
"lastModified": 1781023725,
"narHash": "sha256-Gt+qFANcrDRjl3xzidLYrAUQCd3808iuAsLwZbYYAEU=",
"lastModified": 1786361680,
"narHash": "sha256-IxaZkb9rCGEZ+yGndxKXONeIEcKMzoFUsvLTB5G/caw=",
"owner": "serokell",
"repo": "deploy-rs",
"rev": "2ce9051767ee4d1a3c43b52ba327431783bfd463",
"rev": "16901271e5b30b591e56f7a84f25f186fb20f3e1",
"type": "github"
},
"original": {
@@ -167,19 +167,17 @@
"inputs": {
"flake-parts": "flake-parts",
"git-hooks-nix": "git-hooks-nix",
"nixpkgs": [
"nixpkgs-unstable"
],
"nixpkgs": "nixpkgs_4",
"nixpkgs-23-11": "nixpkgs-23-11",
"nixpkgs-regression": "nixpkgs-regression"
},
"locked": {
"lastModified": 1785428605,
"narHash": "sha256-wfaiSRLM1wDb4MV+NEzbyheK9Y03/oe56NR2I84UF7E=",
"rev": "0ff46631f69584c9f76792cae595ea253bd482c3",
"revCount": 26288,
"lastModified": 1787334067,
"narHash": "sha256-wmwgSBcAGJe/e+FrLwJxlghYV12F7UkIodm0j6cosYg=",
"rev": "c407745c8b9b616bebf7288697699c45794e31ac",
"revCount": 27248,
"type": "tarball",
"url": "https://api.flakehub.com/f/pinned/DeterminateSystems/nix-src/3.21.9/019fb409-4d6e-7243-8a88-23ceee2520e9/source.tar.gz"
"url": "https://api.flakehub.com/f/pinned/DeterminateSystems/nix-src/3.22.2/01a02595-e77f-7e43-a616-5bbc77a2dc07/source.tar.gz"
},
"original": {
"type": "tarball",
@@ -205,25 +203,6 @@
"type": "github"
}
},
"devshell-tools": {
"inputs": {
"flake-utils": "flake-utils_10",
"nixpkgs": "nixpkgs_5"
},
"locked": {
"lastModified": 1710099997,
"narHash": "sha256-WmBKTLdth6I/D+0//9enbIXohGsBjepbjIAm9pCYj0U=",
"owner": "eikek",
"repo": "devshell-tools",
"rev": "e82faf976d318b3829f6f7f6785db6f3c7b65267",
"type": "github"
},
"original": {
"owner": "eikek",
"repo": "devshell-tools",
"type": "github"
}
},
"devshell_2": {
"inputs": {
"flake-utils": "flake-utils_3",
@@ -282,15 +261,15 @@
"flake-compat_2": {
"flake": false,
"locked": {
"lastModified": 1696426674,
"narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
"owner": "edolstra",
"lastModified": 1767039857,
"narHash": "sha256-vNpUSpF5Nuw8xvDLj2KCwwksIbjua2LZCqhV1LNRDns=",
"owner": "NixOS",
"repo": "flake-compat",
"rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
"rev": "5edf11c44bc78a0d334f6334cdaf7d60d732daab",
"type": "github"
},
"original": {
"owner": "edolstra",
"owner": "NixOS",
"repo": "flake-compat",
"type": "github"
}
@@ -303,12 +282,12 @@
]
},
"locked": {
"lastModified": 1748821116,
"narHash": "sha256-F82+gS044J1APL0n4hH50GYdPRv/5JWm34oCJYmVKdE=",
"rev": "49f0870db23e8c1ca0b5259734a02cd9e1e371a1",
"revCount": 377,
"lastModified": 1782949081,
"narHash": "sha256-vp6Y/Grm98ESt6ceOkWiHWyZRDV3J1RID4w+6NWK9yA=",
"rev": "17c9d6cdfc60c64f4ee8d306f9bc0b4ccb51481e",
"revCount": 480,
"type": "tarball",
"url": "https://api.flakehub.com/f/pinned/hercules-ci/flake-parts/0.1.377%2Brev-49f0870db23e8c1ca0b5259734a02cd9e1e371a1/01972f28-554a-73f8-91f4-d488cc502f08/source.tar.gz"
"url": "https://api.flakehub.com/f/pinned/hercules-ci/flake-parts/0.1.480%2Brev-17c9d6cdfc60c64f4ee8d306f9bc0b4ccb51481e/019f2195-dee5-7233-9747-eca0c27f7406/source.tar.gz"
},
"original": {
"type": "tarball",
@@ -354,57 +333,6 @@
"type": "github"
}
},
"flake-utils_10": {
"inputs": {
"systems": "systems_10"
},
"locked": {
"lastModified": 1709126324,
"narHash": "sha256-q6EQdSeUZOG26WelxqkmR7kArjgWCdw5sfJVHPH/7j8=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "d465f4819400de7c8d874d50b982301f28a84605",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"flake-utils_11": {
"inputs": {
"systems": "systems_11"
},
"locked": {
"lastModified": 1705309234,
"narHash": "sha256-uNRRNRKmJyCRC/8y1RqBkqWBLM034y4qN7EprSdmgyA=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "1ef2e671c3b0c19053962c07dbda38332dcebf26",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"flake-utils_12": {
"locked": {
"lastModified": 1667395993,
"narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"flake-utils_2": {
"inputs": {
"systems": "systems_2"
@@ -543,21 +471,18 @@
"git-hooks-nix": {
"inputs": {
"flake-compat": "flake-compat_2",
"gitignore": [
"determinate-nix"
],
"nixpkgs": [
"determinate-nix",
"nixpkgs"
]
},
"locked": {
"lastModified": 1747372754,
"narHash": "sha256-2Y53NGIX2vxfie1rOW0Qb86vjRZ7ngizoo+bnXU9D9k=",
"rev": "80479b6ec16fefd9c1db3ea13aeb038c60530f46",
"revCount": 1026,
"lastModified": 1784288435,
"narHash": "sha256-ReRHaLgr/uVqdD8afFSn+myXIfpHeOhP0yYe0TJqAA8=",
"rev": "43b3c1ab9d40fb1dbb008f451988a91e375825e9",
"revCount": 1231,
"type": "tarball",
"url": "https://api.flakehub.com/f/pinned/cachix/git-hooks.nix/0.1.1026%2Brev-80479b6ec16fefd9c1db3ea13aeb038c60530f46/0196d79a-1b35-7b8e-a021-c894fb62163d/source.tar.gz"
"url": "https://api.flakehub.com/f/pinned/cachix/git-hooks.nix/0.1.1231%2Brev-43b3c1ab9d40fb1dbb008f451988a91e375825e9/019f7135-8fdf-76f0-b1a1-d2c67e91af8d/source.tar.gz"
},
"original": {
"type": "tarball",
@@ -574,11 +499,11 @@
"treefmt-nix": "treefmt-nix"
},
"locked": {
"lastModified": 1781128165,
"narHash": "sha256-97WpKZkaNAL5g7MtASLwqnrJrvrLpQRr6cXWiRNLiXQ=",
"lastModified": 1787502072,
"narHash": "sha256-K5sKCAV3kPbUW0evsqpWrlQRsa2t0jfkduSZ+lRWAA8=",
"owner": "nix-community",
"repo": "harmonia",
"rev": "f0dd1094cdc8d72e038cf9347cacfa9272a8f72d",
"rev": "7c1ef262e324bbf61201fe92a73849eb3d6fd9e2",
"type": "github"
},
"original": {
@@ -595,11 +520,11 @@
]
},
"locked": {
"lastModified": 1781402797,
"narHash": "sha256-pBdDca7xv1nuP0kj+gC5g5AcR/DV+9Zy3CS6uDOMdJ4=",
"lastModified": 1781447016,
"narHash": "sha256-bxZ8XTdUFQRWsh6rZn7fCui/SV4ox7dUAiSg4zYJuDg=",
"owner": "devplayer0",
"repo": "hass-west-wood",
"rev": "3e6ef7a9084e4053c82dea20127a775e7bcf77a5",
"rev": "fd43bede6e1175d9118c42507b737041b8923787",
"type": "github"
},
"original": {
@@ -637,11 +562,11 @@
]
},
"locked": {
"lastModified": 1781319724,
"narHash": "sha256-ZGuxexEMo4Xv28KJ0dX/m/PHN4oZIOnxHZpNTyrvx4M=",
"lastModified": 1787377438,
"narHash": "sha256-Sxu1NLTD/Ern6hFGLlZmtKCSct3YQXZI/lls8RE1XeM=",
"owner": "nix-community",
"repo": "home-manager",
"rev": "8355f0a16b2dbb06a97959a918af5b239bbe05ae",
"rev": "65258d5c65a250189fde2e35f490d15e064c4c62",
"type": "github"
},
"original": {
@@ -657,11 +582,11 @@
]
},
"locked": {
"lastModified": 1781305496,
"narHash": "sha256-g8Vv4Qfc7n+lgov97REu3X6BeJtvYY0hlSUZR1GrGQQ=",
"lastModified": 1787487906,
"narHash": "sha256-zIdM+8teujHm5hc5MIPDnV7k2UeOOT/pFyFtWjOCwsY=",
"owner": "nix-community",
"repo": "home-manager",
"rev": "c87a39aa979acc4848016d2220c6238390d84779",
"rev": "cfba7ad5886b342b8dd63ba74354b3853ea4cfc9",
"type": "github"
},
"original": {
@@ -674,7 +599,7 @@
"home-manager": [
"home-manager-unstable"
],
"nixpkgs": "nixpkgs_4"
"nixpkgs": "nixpkgs_5"
},
"locked": {
"lastModified": 1769548169,
@@ -693,11 +618,11 @@
"libnetRepo": {
"flake": false,
"locked": {
"lastModified": 1776595118,
"narHash": "sha256-6bIEi8q5hXCHU9nApTbQXvpljMWldg3QipCD+jkOGK8=",
"lastModified": 1781446676,
"narHash": "sha256-b3rJDKxzsf7p4wI698iBi2PInDPRH3KwjdqOk/SahKk=",
"owner": "oddlama",
"repo": "nixos-extra-modules",
"rev": "84207afebb794be7b53cfc9768730f37c64f4a13",
"rev": "f097b474fcb5db7dfd52263c055c9e6caeb13d62",
"type": "github"
},
"original": {
@@ -709,11 +634,11 @@
"nix": {
"flake": false,
"locked": {
"lastModified": 1780652321,
"narHash": "sha256-o/6YXRB6AbeL4SYtSHlJ9oEROl6Wmf7yheJNa3fAv2I=",
"lastModified": 1787394889,
"narHash": "sha256-qtDusLx9yn0aME9D9Oe5QhFnmDUaARwMJo/vt4+DtIU=",
"owner": "nixos",
"repo": "nix",
"rev": "d1f04a798cf4276da59567c07a3bf4a628669288",
"rev": "88b09c64fbea076a0376830d98e5331f70ed31a3",
"type": "github"
},
"original": {
@@ -777,11 +702,11 @@
},
"nixpkgs-mine": {
"locked": {
"lastModified": 1781356656,
"narHash": "sha256-Ygkl3ZBJ434/WhwdK1FyvPMeHvNPAopg3KE/1HtcJuk=",
"lastModified": 1787612836,
"narHash": "sha256-25KxhEJHYVZXAwsHQbXpyaG9/WpWGo9EmGe7kzMc25Y=",
"owner": "devplayer0",
"repo": "nixpkgs",
"rev": "a15e20705db295f621cb5bb63613f03a9373323f",
"rev": "c92598bc3fd46ff4d23407045091eea206120979",
"type": "github"
},
"original": {
@@ -793,11 +718,11 @@
},
"nixpkgs-mine-stable": {
"locked": {
"lastModified": 1781356876,
"narHash": "sha256-s8ed+zuk5wrbyhtDQpkxycAcLmhQH9umGRuVRBNKUbU=",
"lastModified": 1787523195,
"narHash": "sha256-NI87OKi5hXSZlIgh5Gwjjca52MAJnwRaU/+Su99fMqg=",
"owner": "devplayer0",
"repo": "nixpkgs",
"rev": "2eb8bacf9f641d4510fc43ba7fc0eea7dfdf5b24",
"rev": "2a058ae98b603146eae51e6a268854ce0ad035a1",
"type": "github"
},
"original": {
@@ -825,11 +750,11 @@
},
"nixpkgs-stable": {
"locked": {
"lastModified": 1780902259,
"narHash": "sha256-q8yYEC5f1mFlQO9RGna4LTc9QrcvWunX6FYp83munkQ=",
"lastModified": 1787414105,
"narHash": "sha256-WncT27+3BOkgTaJZLnCsf3LcYf9RXMuR9ONSN4rzQ7s=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "bd0ff2d3eac24699c3664d5966b9ef36f388e2ca",
"rev": "a9e6d84f9c2f9012f5fe7d964a7851352300e61a",
"type": "github"
},
"original": {
@@ -840,11 +765,11 @@
},
"nixpkgs-unstable": {
"locked": {
"lastModified": 1781074563,
"narHash": "sha256-md8WlXOlfnIeHeOScMTTHFyf2d6iaTwPl2apR5EQ3P4=",
"lastModified": 1787360063,
"narHash": "sha256-dt4WdcvsA8/RCe+VZZwqU0X+XMM3wBbGCWA0/sFWzGo=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "9ae611a455b90cf061d8f332b977e387bda8e1ca",
"rev": "2c423e03bbafcff28bfadc6781a4a8257f205cb5",
"type": "github"
},
"original": {
@@ -884,6 +809,22 @@
}
},
"nixpkgs_4": {
"locked": {
"lastModified": 1784160687,
"narHash": "sha256-iYL/bixrb6FlHFu/gIuBYzq6c6lM5AAXsXNSWXtIgQc=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "4382ed2b7a6839d4280a9b386db49cbc5907414d",
"type": "github"
},
"original": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "4382ed2b7a6839d4280a9b386db49cbc5907414d",
"type": "github"
}
},
"nixpkgs_5": {
"locked": {
"lastModified": 1768564909,
"narHash": "sha256-Kell/SpJYVkHWMvnhqJz/8DqQg2b6PguxVWOuadbHCc=",
@@ -899,38 +840,6 @@
"type": "github"
}
},
"nixpkgs_5": {
"locked": {
"lastModified": 1709309926,
"narHash": "sha256-VZFBtXGVD9LWTecGi6eXrE0hJ/mVB3zGUlHImUs2Qak=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "79baff8812a0d68e24a836df0a364c678089e2c7",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-23.11",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_6": {
"locked": {
"lastModified": 1674990008,
"narHash": "sha256-4zOyp+hFW2Y7imxIpZqZGT8CEqKmDjwgfD6BzRUE0mQ=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "d2bbcbe6c626d339b25a4995711f07625b508214",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"openwrt-feeds": {
"inputs": {
"nixpkgs": [
@@ -941,11 +850,11 @@
]
},
"locked": {
"lastModified": 1787353688,
"narHash": "sha256-YDEm+ev3BpDS9Sq1ByVv0i5QQCE1yezpjWVhcNBBjCE=",
"lastModified": 1787522666,
"narHash": "sha256-Ev4X1HCx6aV4L5GtMQX9DJRgWQ7K914rT11nL7mIkmw=",
"owner": "devplayer0",
"repo": "openwrt-feeds",
"rev": "a30b2b5f83c7d1fffca2146453e8d5866b882da4",
"rev": "53720becf1e89473660e25107ae5d23bf1465621",
"type": "github"
},
"original": {
@@ -963,11 +872,11 @@
"systems": "systems_7"
},
"locked": {
"lastModified": 1787302424,
"narHash": "sha256-fg9pKzO6OeQhe/bY2CpHb6QnHq57P/icwQz35GF/R/8=",
"lastModified": 1787474509,
"narHash": "sha256-jL5RS/TbKk7HxjsGyFWeceHveyRgM8btvfY9Z77P9jM=",
"owner": "astro",
"repo": "nix-openwrt-imagebuilder",
"rev": "276c1dd6346f50231392e97b3a9987c9dd57da28",
"rev": "4371439b1e4e582266fc38345d1a01db1f8db6d6",
"type": "github"
},
"original": {
@@ -1044,8 +953,7 @@
"nixpkgs-unstable": "nixpkgs-unstable",
"openwrt-feeds": "openwrt-feeds",
"openwrt-imagebuilder": "openwrt-imagebuilder",
"ragenix": "ragenix",
"sharry": "sharry"
"ragenix": "ragenix"
}
},
"rust-overlay": {
@@ -1056,11 +964,11 @@
]
},
"locked": {
"lastModified": 1761791894,
"narHash": "sha256-myRIDh+PxaREz+z9LzbqBJF+SnTFJwkthKDX9zMyddY=",
"lastModified": 1787454509,
"narHash": "sha256-r4LDUF+zmJnkftvCVkCrUhSJazsf6EVJF+V2l4/MYbI=",
"owner": "oxalica",
"repo": "rust-overlay",
"rev": "59c45eb69d9222a4362673141e00ff77842cd219",
"rev": "f60c1b57ff805a46b5175c76fc981fb4f81efbcc",
"type": "github"
},
"original": {
@@ -1069,48 +977,6 @@
"type": "github"
}
},
"sbt": {
"inputs": {
"flake-utils": "flake-utils_12",
"nixpkgs": "nixpkgs_6"
},
"locked": {
"lastModified": 1698464090,
"narHash": "sha256-Pnej7WZIPomYWg8f/CZ65sfW85IfIUjYhphMMg7/LT0=",
"owner": "zaninime",
"repo": "sbt-derivation",
"rev": "6762cf2c31de50efd9ff905cbcc87239995a4ef9",
"type": "github"
},
"original": {
"owner": "zaninime",
"repo": "sbt-derivation",
"type": "github"
}
},
"sharry": {
"inputs": {
"devshell-tools": "devshell-tools",
"flake-utils": "flake-utils_11",
"nixpkgs": [
"nixpkgs-unstable"
],
"sbt": "sbt"
},
"locked": {
"lastModified": 1741328331,
"narHash": "sha256-OtsHm9ykxfAOMRcgFDsqFBBy5Wu0ag7eq1qmTIluVcw=",
"owner": "eikek",
"repo": "sharry",
"rev": "6203b90f9a76357d75c108a27ad00f323d45c1d0",
"type": "github"
},
"original": {
"owner": "eikek",
"repo": "sharry",
"type": "github"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
@@ -1126,36 +992,6 @@
"type": "github"
}
},
"systems_10": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
},
"systems_11": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
},
"systems_2": {
"locked": {
"lastModified": 1681028828,
@@ -1284,11 +1120,11 @@
]
},
"locked": {
"lastModified": 1780220602,
"narHash": "sha256-eynAfOmbmxJnkp7YewvCEbShNnnYJ9gLLqkzsYtBPeM=",
"lastModified": 1786901030,
"narHash": "sha256-WSFCsDSE5ffgD2MqzkM2CYjeFiKhRF/dJUN8uedb6YE=",
"owner": "numtide",
"repo": "treefmt-nix",
"rev": "db947814a175b7ca6ded66e21383d938df01c227",
"rev": "27b3b12a8e6375f28ebe122f07d230ca5459bbfa",
"type": "github"
},
"original": {
+4 -5
View File
@@ -35,10 +35,11 @@
home-manager-stable.inputs.nixpkgs.follows = "nixpkgs-stable";
# Determinate Nix, used as the common Nix implementation across systems, homes, the devshell and
# CI (see lib.my.c.nix). We build it ourselves against our pinned nixpkgs (FlakeHub's cache needs
# auth), so it flows through our own Harmonia cache like everything else.
# CI (see lib.my.c.nix). We build it ourselves (FlakeHub's cache needs auth), so it flows through
# our own Harmonia cache like everything else. Keep its tested nixpkgs pin: its packaging carries
# compatibility patches that can conflict with newer nixpkgs patches.
determinate-nix.url = "https://flakehub.com/f/DeterminateSystems/nix-src/*";
determinate-nix.inputs.nixpkgs.follows = "nixpkgs-unstable";
determinate-nix.inputs.nixpkgs.url = "github:NixOS/nixpkgs/4382ed2b7a6839d4280a9b386db49cbc5907414d";
# Stuff used by the flake for build / deployment
# ragenix.url = "github:yaxitech/ragenix";
@@ -68,8 +69,6 @@
openwrt-feeds.inputs.openwrt-imagebuilder.follows = "openwrt-imagebuilder";
# 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";
+4 -4
View File
@@ -423,12 +423,12 @@ in
gtk = {
enable = true;
theme = {
name = "Numix";
package = pkgs.numix-gtk-theme;
name = "Adwaita";
package = pkgs.gnome-themes-extra;
};
gtk4.theme = {
name = "Numix";
package = pkgs.numix-gtk-theme;
name = "Adwaita";
package = pkgs.gnome-themes-extra;
};
iconTheme = {
name = "Numix";
+1 -1
View File
@@ -30,7 +30,7 @@ rec {
kernel = {
lts = pkgs: pkgs.linuxKernel.packages.linux_6_18;
latest = pkgs: pkgs.linuxKernel.packages.linux_7_0;
latest = pkgs: pkgs.linuxKernel.packages.linux_7_2;
};
nginx = rec {
+1 -1
View File
@@ -253,7 +253,7 @@ rec {
in
{
trivial = prev.trivial // {
release = "26.06:u-${prev.trivial.release}";
release = "26.08:u-${prev.trivial.release}";
codeName = "Irritating";
revisionWithDefault = default: self.rev or default;
versionSuffix = ".${date}.${revCode self}:u-${revCode pkgsFlake}";
+73 -13
View File
@@ -1,6 +1,8 @@
{ lib, ... }:
let
inherit (lib.my) net;
inherit (lib.my.c.colony) domain;
home = lib.my.c.home;
in
{
nixos.systems.portcullis = {
@@ -8,7 +10,37 @@ in
nixpkgs = "mine-stable";
home-manager = "mine-stable";
configuration = { lib, pkgs, config, ... }:
assignments = {
# Staging-only: the 10G link lands on the home hi VLAN until portcullis is racked.
hi = {
domain = home.domain;
mtu = home.hiMTU;
ipv4 = {
address = net.cidr.host 41 home.prefixes.hi.v4;
mask = 22;
gateway = home.vips.hi.v4;
};
ipv6 = {
iid = "::6:1";
address = net.cidr.host (65536*6+1) home.prefixes.hi.v6;
};
};
};
configuration = { lib, pkgs, config, assignments, ... }:
let
inherit (lib) mkMerge;
inherit (lib.my) mkVLAN networkdAssignment;
inherit (lib.my.c) networkd;
# Router-sized rings rather than the driver defaults, and GRO kept across
# forwarding so UDP-encapsulated traffic stays batched.
nicTuning = {
RxBufferSize = 4096;
TxBufferSize = 4096;
GenericReceiveOffloadUDPForwarding = true;
};
in
{
hardware = {
enableRedistributableFirmware = true;
@@ -19,7 +51,9 @@ in
boot = {
kernelModules = [ "kvm-intel" ];
kernelParams = [ "intel_iommu=on" ];
# Passthrough mode keeps the IOMMU available without paying DMA translation
# on the forwarding path.
kernelParams = [ "intel_iommu=on" "iommu=pt" ];
initrd = {
availableKernelModules = [ "xhci_pci" "nvme" "usb_storage" "usbhid" "sd_mod" "sr_mod" ];
kernelModules = [ "dm-snapshot" ];
@@ -45,6 +79,13 @@ in
networking = { inherit domain; };
# The I226-V link-drop erratum is driven by EEE as well as ASPM. The driver already
# leaves EEE off, so this pins a default rather than changing one; systemd.link has
# no knob for it.
services.udev.extraRules = ''
ACTION=="add", SUBSYSTEM=="net", DRIVERS=="igc", RUN+="${pkgs.ethtool}/bin/ethtool --set-eee $name eee off"
'';
environment.systemPackages = with pkgs; [
pciutils
usbutils
@@ -54,42 +95,44 @@ in
];
systemd.network = {
# Only one port is patched in while the box is being staged, so don't block
# Only some ports are patched in while the box is being staged, so don't block
# boot on the others coming up.
wait-online.anyInterface = true;
netdevs = mkVLAN "lan-hi" home.vlans.hi;
links = {
"10-et2g5-0" = {
matchConfig.PermanentMACAddress = "00:d0:b4:05:ed:48";
linkConfig.Name = "et2g5-0";
linkConfig = nicTuning // { Name = "et2g5-0"; };
};
"10-et2g5-1" = {
matchConfig.PermanentMACAddress = "00:d0:b4:05:ed:49";
linkConfig.Name = "et2g5-1";
linkConfig = nicTuning // { Name = "et2g5-1"; };
};
"10-et2g5-2" = {
matchConfig.PermanentMACAddress = "00:d0:b4:05:ed:4a";
linkConfig.Name = "et2g5-2";
linkConfig = nicTuning // { Name = "et2g5-2"; };
};
"10-et2g5-3" = {
matchConfig.PermanentMACAddress = "00:d0:b4:05:ed:4b";
linkConfig.Name = "et2g5-3";
linkConfig = nicTuning // { Name = "et2g5-3"; };
};
"11-et10g-0" = {
matchConfig.PermanentMACAddress = "60:be:b4:2e:9b:a2";
linkConfig.Name = "et10g-0";
linkConfig = nicTuning // { Name = "et10g-0"; };
};
"11-et10g-1" = {
matchConfig.PermanentMACAddress = "60:be:b4:2e:9b:a3";
linkConfig.Name = "et10g-1";
linkConfig = nicTuning // { Name = "et10g-1"; };
};
};
networks = {
# TODO: replace with the colony assignments and routing config once portcullis is
# racked at Nikhef. Until then it is staged on the home lo VLAN, so every 2.5G port
# takes DHCP and whichever one is patched in provides connectivity. kea registers
# racked at Nikhef. Until then it is staged at home, so every 2.5G port takes DHCP on
# the lo VLAN and whichever one is patched in provides connectivity. kea registers
# the DHCP hostname, making the box reachable as `portcullis.dyn.h.nul.ie`.
"80-bootstrap" = {
matchConfig.Name = "et2g5-*";
@@ -97,12 +140,29 @@ in
networkConfig.IPv6PrivacyExtensions = "no";
linkConfig.RequiredForOnline = "routable";
};
# 10G up to jim's spare SFP+ port via an intermediary switch. That uplink is
# untagged VLAN 1, so hi has to be tagged on its own interface.
"81-et10g-0" = {
matchConfig.Name = "et10g-0";
vlan = [ "lan-hi" ];
networkConfig = networkd.noL3;
linkConfig = {
# The carrier has to allow hi's jumbo frames before lan-hi can take that MTU
MTUBytes = toString home.hiMTU;
RequiredForOnline = "no";
};
};
"82-lan-hi" = mkMerge [
(networkdAssignment "lan-hi" assignments.hi)
{ networkConfig = home.vlanDns "hi"; }
];
};
};
my = {
# As above: no colony assignment yet, so point deploy at the staging DHCP name.
deploy.node.hostname = "portcullis.dyn.${lib.my.c.home.domain}";
# As above: no colony assignment yet, so deploy over the staging hi address.
deploy.node.hostname = assignments.hi.ipv4.address;
secrets = {
key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAUolR93Byg+Daw8pUYHVpQ34ioxSc2C8vzj9F4KbqMs";
@@ -336,15 +336,6 @@ in
useACMEHost = pubDomain;
};
"share.${pubDomain}" = {
locations."/" = {
proxyPass = "http://object-ctr.${domain}:9090";
proxyWebsockets = true;
extraConfig = proxyHeaders;
};
useACMEHost = pubDomain;
};
"stuff.${pubDomain}" = {
locations."/" = {
proxyPass = "http://jackflix-ctr.${domain}:3923";
@@ -47,10 +47,6 @@ in
key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFdHbZErWLmTPO/aEWB1Fup/aGMf31Un5Wk66FJwTz/8";
files = {
"object/minio.env" = {};
"object/sharry.conf" = {
owner = "sharry";
group = "sharry";
};
"object/minio-client-config.json" = {
owner = config.my.user.config.name;
group = config.my.user.config.group;
@@ -65,7 +61,6 @@ in
firewall = {
tcp.allowed = [
9000 9001
config.services.sharry.config.bind.port
8069
5000
config.services.hedgedoc.settings.port
@@ -122,8 +117,6 @@ in
};
};
sharry = awaitPostgres;
atticd = mkMerge [
awaitPostgres
{
@@ -175,66 +168,6 @@ in
dataDir = [ "/mnt/minio" ];
};
sharry = {
enable = true;
configOverridesFile = config.age.secrets."object/sharry.conf".path;
config = {
base-url = "https://share.${lib.my.c.pubDomain}";
bind.address = "::";
alias-member-enabled = true;
webapp = {
chunk-size = "64M";
};
backend = {
auth = {
fixed = {
enabled = true;
user = "dev";
};
internal = {
enabled = true;
order = 50;
};
};
jdbc = {
url = "jdbc:postgresql://colony-psql:5432/sharry";
user = "sharry";
};
files = {
default-store = "minio";
stores = {
database.enabled = false;
minio = {
enabled = true;
type = "s3";
endpoint = "https://s3.nul.ie";
access-key = "share";
bucket = "share";
};
};
};
compute-checksum.parallel = 4;
signup.mode = "invite";
share = {
max-size = "128G";
max-validity = "3650 days";
};
mail = {
enabled = true;
smtp = {
host = "mail.nul.ie";
port = 587;
user = "sharry@nul.ie";
ssl-type = "starttls";
default-from = "Sharry <sharry@nul.ie>";
timeout = "30 seconds";
};
};
};
};
};
atticd = {
enable = false;
environmentFile = config.age.secrets."object/atticd.env".path;
@@ -308,7 +241,6 @@ in
forwardPorts = [
{ from = "host"; host.port = 9000; guest.port = 9000; }
{ from = "host"; host.port = 9001; guest.port = 9001; }
{ from = "host"; guest.port = config.services.sharry.config.bind.port; }
];
};
})
+8 -5
View File
@@ -2,21 +2,24 @@
let
inherit (lib) concatStringsSep;
inherit (lib.my) dockerNetAssignment;
admin = "76561198049818986"; # /dev/player0
in
{
config = {
virtualisation.oci-containers.containers = {
valheim = {
image = "ghcr.io/lloesche/valheim-server@sha256:d977ccbeff02d2509646fb0157b5e353ebadb3105a3ed351b9c309a09a61701b";
image = "ghcr.io/community-valheim-tools/valheim-server@sha256:f3ccde9a4e292663cf5096d502ff33cc9617015f6d70b6a9ca0968543f165ef2";
environment = {
BACKUPS_IF_IDLE = "false";
SERVER_NAME = "amogus sus";
SERVER_PUBLIC = "true";
WORLD_NAME = "simpland2";
ADMINLIST_IDS = "76561198049818986";
# Previous world: simpland2
WORLD_NAME = "simpland3";
ADMINLIST_IDS = admin;
PERMITTEDLIST_IDS = concatStringsSep " " [
"76561198049818986" # /dev/player0
admin
"76561198044432445" # Nuda
"76561198121606266" # El Pugador
"76561198059894566" # hynge
@@ -27,7 +30,7 @@ in
volumes = [
"valheim_data:/config"
"valhem_server:/opt/valheim"
"valheim_server:/opt/valheim"
];
extraOptions = [
+3
View File
@@ -198,6 +198,9 @@ in
];
"45-lan-lo" = {
matchConfig.Name = "lan-lo";
# The parent carries hi's jumbo frames, but lo runs at the standard MTU;
# without this the VLAN would inherit the parent's larger one.
linkConfig.MTUBytes = "1500";
networkConfig = {
DHCP = "ipv4";
IPv6AcceptRA = true;
@@ -47,8 +47,9 @@ in
inherit (lib) mkMerge mkIf mkForce;
inherit (lib.my) networkdAssignment;
hassPort = 8123;
hassCli = pkgs.writeShellScriptBin "hass-cli" ''
export HASS_SERVER="http://localhost:${toString config.services.home-assistant.config.http.server_port}"
export HASS_SERVER="http://localhost:${toString hassPort}"
export HASS_TOKEN="$(< ${config.age.secrets."hass/cli-token.txt".path})"
exec ${pkgs.home-assistant-cli}/bin/hass-cli "$@"
'';
@@ -69,7 +70,7 @@ in
};
firewall = {
tcp.allowed = [ "http" 1883 ];
tcp.allowed = [ "http" hassPort 1883 ];
};
};
@@ -164,29 +165,7 @@ in
};
};
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
{
home-assistant = {
enable = true;
extraComponents = [
@@ -208,7 +187,6 @@ in
isal
gtts
(pyirishrail python3Packages)
];
customComponents = with pkgs.home-assistant-custom-components; [
alarmo
@@ -217,7 +195,6 @@ in
];
configWritable = false;
openFirewall = true;
config = {
default_config = {};
homeassistant = {
@@ -227,9 +204,10 @@ in
country = "IE";
time_zone = "Europe/Dublin";
external_url = "https://hass.${pubDomain}";
internal_url = "http://hass-ctr.${domain}:${toString cfg.config.http.server_port}";
internal_url = "http://hass-ctr.${domain}:${toString hassPort}";
};
http = {
server_port = hassPort;
use_x_forwarded_for = true;
trusted_proxies = with allAssignments.middleman.internal; [
ipv4.address
+1 -1
View File
@@ -191,7 +191,7 @@ let
# Routes the custom modules into `baseModules` so the NixOS manual documents them. The old
# infinite-recursion is gone, but enabling this makes every system build regenerate the
# manual, and it documents everything the modules transitively import — including third-party
# modules that aren't doc-clean (e.g. `services.sharry`). Prefer the generated
# modules that aren't doc-clean. Prefer the generated
# `nixos.optionsDoc` reference (`docs/reference/nixos-options.md`) instead.
docCustom = mkBoolOpt' false "Whether to document nixfiles' custom NixOS modules.";
+1 -3
View File
@@ -11,7 +11,6 @@ in
imports = [
inputs.impermanence.nixosModules.default
inputs.ragenix.nixosModules.age
inputs.sharry.nixosModules.default
inputs.copyparty.nixosModules.default
inputs.harmonia.nixosModules.harmonia
];
@@ -77,7 +76,6 @@ in
nixpkgs = {
overlays = [
inputs.deploy-rs.overlays.default
inputs.sharry.overlays.default
# TODO: Re-enable when borgthin is updated
# inputs.borgthin.overlays.default
inputs.boardie.overlays.default
@@ -169,7 +167,7 @@ in
services = {
# TODO: Remove if-else when 26.11 releases
kmscon = if (config.system.nixos.release == "26.06:u-26.11") then {
kmscon = if (config.system.nixos.release == "26.08:u-26.11") then {
enable = mkDefault false;
config = {
hwaccel = config.hardware.graphics.enable;
+8 -1
View File
@@ -1,7 +1,7 @@
{ lib, config, vpns, ... }:
let
inherit (builtins) any attrValues;
inherit (lib) optionalString mapAttrsToList concatStringsSep concatMapStringsSep filterAttrs mkIf mkMerge;
inherit (lib) optional optionalString mapAttrsToList concatStringsSep concatMapStringsSep filterAttrs mkIf mkMerge;
inherit (lib.my) isIPv6 mkOpt';
vxlanPort = 4789;
@@ -105,6 +105,11 @@ let
echo "${ownAddr} ${p.addr} : PSK \"$(< "${config.my.vpns.l2.pskFiles.${name}}")\"" >> /run/l2mesh.secrets
'') (attrValues otherPeers);
anySecurity = any (c: c.security.enable) (attrValues memberMeshes);
securedFamily = v6: any (c: c.security.enable && c.ipv6 == v6) (attrValues memberMeshes);
# ESP GSO/GRO batching, which the kernel does not autoload when an SA is created
espOffloadModules =
(optional (securedFamily false) "esp4_offload") ++
(optional (securedFamily true) "esp6_offload");
in
{
options = {
@@ -114,6 +119,8 @@ in
};
config = {
boot.kernelModules = espOffloadModules;
systemd.network = mkMerge (mapAttrsToList mkNetConfig memberMeshes);
environment.etc."ipsec.d/l2mesh.secrets" = mkIf anySecurity {
+6 -2
View File
@@ -23,10 +23,14 @@ let
target = "realtek";
variant = "rtl930x";
profile = "xikestor_sks8300-8x";
packages = [ "luci" "ip-full" "ip-bridge" "ethtool-full" "luci-app-sfp-info" ];
packages = [ "luci" "ip-full" "ip-bridge" "ethtool-full" ];
};
in
{
openwrt-fergal = mkImage (fergal // { release = "snapshot"; });
openwrt-fergal = mkImage (fergal // {
release = "snapshot";
# The release feeds do not provide this LuCI app.
packages = fergal.packages ++ [ "luci-app-sfp-info" ];
});
openwrt-fergal-release = mkImage (fergal // { inherit release; });
}
+46 -11
View File
@@ -36,9 +36,9 @@
// procfs files report a size of zero, so IOUtils reads /proc/meminfo as empty.
// nsIScriptableInputStream also rejects reads larger than that reported size;
// nsIConverterInputStream reads until EOF without relying on it.
function availableMemory() {
function readProcFile(path) {
const file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
file.initWithPath("/proc/meminfo");
file.initWithPath(path);
const fileStream = Cc["@mozilla.org/network/file-input-stream;1"].createInstance(
Ci.nsIFileInputStream
);
@@ -48,17 +48,38 @@
);
input.init(fileStream, "UTF-8", 0, 0);
const chunk = {};
let meminfo = "";
let contents = "";
while (input.readString(4096, chunk)) {
meminfo += chunk.value;
contents += chunk.value;
}
input.close();
return contents;
}
const match = /^MemAvailable:\s+(\d+)\s+kB$/m.exec(meminfo);
function memoryInfo() {
const meminfo = readProcFile("/proc/meminfo");
const readKiB = name => {
const match = new RegExp(`^${name}:\\s+(\\d+)\\s+kB$`, "m").exec(meminfo);
if (!match) {
throw new Error("MemAvailable is absent from /proc/meminfo");
throw new Error(`${name} is absent from /proc/meminfo`);
}
return Number(match[1]) * 1024;
};
return {
available: readKiB("MemAvailable"),
};
}
const availableMemory = () => memoryInfo().available;
function swapOutPages() {
const match = /^pswpout\s+(\d+)$/m.exec(readProcFile("/proc/vmstat"));
if (!match) {
throw new Error("pswpout is absent from /proc/vmstat");
}
return Number(match[1]);
}
async function unloadOne(minInactiveMs) {
@@ -184,6 +205,7 @@
underPressure: false,
timer: null,
paths: null,
previousSwapOutPages: null,
async tick() {
if (this.busy) {
@@ -203,19 +225,32 @@
return;
}
const available = await availableMemory();
const { available } = memoryInfo();
const currentSwapOutPages = swapOutPages();
// Swap usage persists after pressure passes, so react to new swap-outs instead.
const swappedOutPages =
this.previousSwapOutPages === null
? 0
: Math.max(0, currentSwapOutPages - this.previousSwapOutPages);
this.previousSwapOutPages = currentSwapOutPages;
const low = prefInt("lowAvailableMiB") * MiB;
const high = prefInt("highAvailableMiB") * MiB;
if (high <= low) {
throw new Error("highAvailableMiB must be greater than lowAvailableMiB");
}
if (!this.underPressure && available <= low) {
if (!this.underPressure && (available <= low || swappedOutPages > 0)) {
this.underPressure = true;
log(`memory pressure entered at ${Math.round(available / MiB)} MiB available`);
} else if (this.underPressure && available >= high) {
log(
`memory pressure entered at ${Math.round(available / MiB)} MiB available, ` +
`${swappedOutPages} pages swapped out since the previous poll`
);
} else if (this.underPressure && available >= high && swappedOutPages === 0) {
this.underPressure = false;
log(`memory pressure cleared at ${Math.round(available / MiB)} MiB available`);
log(
`memory pressure cleared at ${Math.round(available / MiB)} MiB available, ` +
"no pages swapped out since the previous poll"
);
}
if (this.underPressure) {
-18
View File
@@ -1,18 +0,0 @@
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IGhrYnR2ZyBCeHA4
Y0hkSGZSNkMxNTY0SUVEQ3FXK0V5QUdVK2hXUFloVW1hVERHK2xFCm4xd1JJWXpH
a1J2QTVvUyt4OXdzWWtMMEo4NFZ3ZkY0YXdydXpOVCtya1UKLT4gWDI1NTE5IHhI
TW5FTHdpYjNwclVsajBUS3ZRSXpER0pKaEFiWFU3Q3cyT0RZT1VnQXcKZFNOODJu
d3RiS0p0b3JmRlZ5M0JCRDB0MzNoUkRWamdkNXZQUzB1RHZoQQotPiAvR15OXkZR
eS1ncmVhc2UKVjhxR0dVVHNWWHdxVFkyd1lPMnN5NXp6Ky9MOHlpNnpIeEExVUhO
dEtXNG9DRFY2OWNlWnFIb1c3MjNLS2V6ZAppTEo3RmZHbzRPQVA3b2xkdmZZCi0t
LSA4RmE0OVlPbUhqWDdwVHNvS0JRcm9XQXl6SFVEYXRnWS81SzNxV1NBWjY4CoYX
xS977tMXj6AbcEZvzRgJfLFoFVRGajoa+QwQyLfkZ6wkI/BQQbgSDOR2s6JEB5Fy
RIoJAB7iZoApj+Ctc4W23qif8gdMedp576VRaDSIo8CC+R6FQlf9s+1MHay8Z+ge
TjWV3xO/70eVYjPc2u/NvejZruBQc52X/yWxnZOrOl2QRDe3dzn9PHiawXdun1bl
qZlhaMaR449BPl3eadTrm4l6IybRSRqIgTWgkEOCUqdrVuBtb1HbqTf2FB9/rD41
BblBV0q/UGx9kUxetgPiu8Wa1hjepSeSglJ9SeKAlH0PC3q+F9tYirphrxFrLGiK
e7aV4Ukpqi0T5vpCkkwm7wF3uTZnmPDz7cWvYbIw1T12N3pV/pxrjigTpqB91svC
jlMQCCtdyEojfUb+tlLlNjvkAbvwZHrc8nBCyuvTuzc2vaUnf6VTaJxGG97tUIyY
brkp5b+mDaU=
-----END AGE ENCRYPTED FILE-----