Compare commits
21
Commits
d1b9358069
..
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
73e538ad1f | ||
|
|
9e9027a250 | ||
|
|
b904922356 | ||
|
|
02c965dd96 | ||
|
|
adea4bd9e5 | ||
|
|
e875971237 | ||
|
|
1a9c601c2b | ||
|
|
92855606a4 | ||
|
|
5171a10079 | ||
|
|
e93e9f7a08 | ||
|
|
5e036d17c4 | ||
|
|
57b94b64bb | ||
|
|
bf411e03e2 | ||
|
|
81b7ca9d05 | ||
|
|
dd0b318a44 | ||
|
|
d24d71113f | ||
|
|
87cdfdbd97 | ||
|
|
7bebac194c | ||
|
|
cdc5d9c1db | ||
|
|
e7122b8862 | ||
|
|
0aade09d7e |
@@ -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.
|
||||
@@ -0,0 +1,37 @@
|
||||
---
|
||||
name: flash-openwrt
|
||||
description: >-
|
||||
Flash a flake-built OpenWrt image onto one of the OpenWrt boxes (currently fergal): build the
|
||||
image, pre-flight the box, back up its config, validate and stage the image, run sysupgrade, and
|
||||
verify what came back. Use when the user wants to flash, reflash, upgrade or sysupgrade an OpenWrt
|
||||
box, or after changing its baked-in package list.
|
||||
---
|
||||
|
||||
# Flash an OpenWrt box
|
||||
|
||||
The canonical, agent-agnostic procedure lives in the repo at
|
||||
[`docs/openwrt-flash.md`](../../../docs/openwrt-flash.md). Read it and follow the phases in order.
|
||||
|
||||
Key reminders (see the doc for the full steps):
|
||||
|
||||
- **Stop at the ⏸ before Phase 5.** Flashing reboots the box and cannot be interrupted partway.
|
||||
Confirm with the user, and confirm a serial console is reachable, *before* writing anything.
|
||||
- **Packages are baked into the image**, so a package change means a reflash. Edit the box's list in
|
||||
[`openwrt/default.nix`](../../../openwrt/default.nix), rebuild, and check the built `.manifest` —
|
||||
a package name that doesn't exist is not a build error, it just isn't in the image.
|
||||
- **`scp` does not work** on these boxes (no `sftp-server`). Move files with
|
||||
`ssh <box> 'cat > /dev/…' < file` and `ssh <box> 'cat …' > file`.
|
||||
- **Detach the upgrade with `setsid`**, not `nohup` (absent on busybox). `sysupgrade` kills the SSH
|
||||
session mid-run, and an attached run dies with it — possibly after the firmware is erased.
|
||||
- **Never reach for `sysupgrade -c`.** It needs `/overlay/upper/etc` and aborts *after* erasing the
|
||||
firmware when that is missing, which is exactly the initramfs case. Plain `sysupgrade` already
|
||||
keeps everything in `/lib/upgrade/keep.d/`.
|
||||
- **Poll SSH to detect the reboot, never ping.** Successful pings return in milliseconds, so a
|
||||
"wait for down" loop completes instantly and reports nonsense. Sleep between probes; expect about
|
||||
three minutes.
|
||||
- **Verify after**, don't assume: revision, management address, package count against the manifest,
|
||||
and that the new packages are present and running.
|
||||
|
||||
The images are declared in [`openwrt/default.nix`](../../../openwrt/default.nix); background on the
|
||||
outputs and the pinned package feeds is in
|
||||
[`docs/deployment.md`](../../../docs/deployment.md#openwrt-images).
|
||||
@@ -0,0 +1,40 @@
|
||||
---
|
||||
name: install-box
|
||||
description: >-
|
||||
Install a new NixOS box into this flake, from bare hardware booted into the custom installer
|
||||
through to a deployable system: probe the hardware, partition and format the disks, write the box
|
||||
config and flake entry, run do-install, and document the box. Use when the user wants to install,
|
||||
bootstrap, provision or add a new box/host/machine.
|
||||
---
|
||||
|
||||
# Install a box
|
||||
|
||||
The canonical, agent-agnostic procedure lives in the repo at
|
||||
[`docs/install-box.md`](../../../docs/install-box.md). Read it and follow the phases in order.
|
||||
|
||||
Key reminders (see the doc for the full steps):
|
||||
|
||||
- It is **guided, not automated** — stop at the ⏸ points: settling what the box actually is
|
||||
(Phase 1), wiping and partitioning disks (Phase 3), and running `do-install` (Phase 6). The user
|
||||
often wants to do the install step by hand.
|
||||
- **Phase 1 is not derivable from the hardware.** Name, site, role, channel and whether the box gets
|
||||
assignments now all have to come from the user. Ask before writing files.
|
||||
- **`show-hw-config` is a shell alias**, so it needs `installer-shell bash -lic show-hw-config`.
|
||||
Run it twice: once early for the kernel-module lists, once after mounting for the filesystems.
|
||||
- **`git add` the new box directory before evaluating** — the flake reads through git, and an
|
||||
untracked path fails as "Path … is not tracked by Git" rather than as a Nix error.
|
||||
- **Validate with `check-system <host>`**, not `build-system` — evaluation catches module and option
|
||||
errors cheaply.
|
||||
- **Seed the SSH host key from the installer** (Phase 3) by copying `/etc/ssh/ssh_host_*` onto the
|
||||
persist volume. The installer regenerates them each boot, so they are safe to adopt, and it means
|
||||
`my.secrets.key` can be set and secrets encrypted before the install rather than after first boot.
|
||||
- **Every box declares a secret even when its own config declares none** — `my.user` pulls in
|
||||
`user-passwd.txt` by default — so setting `my.secrets.key` always requires
|
||||
`ragenix --rekey-one secrets/user-passwd.txt.age`. Check with
|
||||
`nix eval .#nixosConfigurations.<host>.config.age.secrets --apply builtins.attrNames` rather than
|
||||
assuming there is nothing to do. Re-encrypt selectively; `ragenix --rekey` rewrites every secret
|
||||
in `secrets/` and drowns the real change in churn.
|
||||
- Take **everything** useful out of `show-hw-config`, not just the modules and filesystems — drop an
|
||||
option only when a nixfiles module already sets it.
|
||||
- Finish with Phase 8: box page, site index row, `networking.md` prose. Don't hand-edit anything
|
||||
between `<!-- ... -->` markers.
|
||||
Symlink
+1
@@ -0,0 +1 @@
|
||||
../../.agents/skills/upgrade-nixpkgs
|
||||
@@ -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).
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -61,9 +61,13 @@ Common ones:
|
||||
`SSH_AUTH_SOCK= ssh-machine …` (or add `-o IdentityAgent=none` to a raw `ssh`).
|
||||
- `ragenix` — edit age secrets using `.keys/dev.key` as identity (see Secrets).
|
||||
- `repl` — `nix repl .#`.
|
||||
- `installer-shell` / `do-install <system>` — drive an install against a booted installer at
|
||||
`$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
|
||||
@@ -177,6 +181,10 @@ recipient key list (always including `.keys/dev.pub`). Edit secrets with the `ra
|
||||
command, which supplies `.keys/dev.key` as the identity. The `.keys/` directory (dev + deploy
|
||||
private keys) is required for editing secrets, deploying, and running dev VMs.
|
||||
|
||||
When a recipient list changes, re-encrypt selectively with `ragenix --rekey-one <file>` for each
|
||||
affected secret. `ragenix --rekey` rewrites **every** secret in `secrets/`, burying the real change
|
||||
in churn.
|
||||
|
||||
## Conventions
|
||||
|
||||
- Format with `nixpkgs-fmt` (`fmt`). 2-space indent, `inherit (...)` blocks at the top of `let` —
|
||||
@@ -204,8 +212,18 @@ private keys) is required for editing secrets, deploying, and running dev VMs.
|
||||
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.
|
||||
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
@@ -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
|
||||
|
||||
+8
-1
@@ -27,6 +27,10 @@ Not every box fits this pattern, but **colony** and **home** are organised this
|
||||
- [`deployment.md`](deployment.md) — deploy-rs, devshell commands, secrets workflow, CI.
|
||||
- [`nixpkgs-upgrade.md`](nixpkgs-upgrade.md) — guided procedure for the periodic upgrade of the four
|
||||
nixpkgs channels and home-manager (fork rebase, stable bumps, input review).
|
||||
- [`install-box.md`](install-box.md) — guided procedure for installing a new box, from the booted
|
||||
installer through partitioning, the box config, `do-install` and documentation.
|
||||
- [`openwrt-flash.md`](openwrt-flash.md) — guided procedure for flashing a flake-built image onto an
|
||||
OpenWrt box, from the build through pre-flight, `sysupgrade` and verification.
|
||||
- [`reference/dns.md`](reference/dns.md) — generated forward and reverse DNS record reference.
|
||||
- [`reference/nixos-options.md`](reference/nixos-options.md) — generated per-option reference for
|
||||
the custom `my.*` NixOS modules.
|
||||
@@ -43,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)
|
||||
@@ -53,6 +57,9 @@ colony (physical VM host, ams1)
|
||||
├── git ────── Gitea + Gitea Actions runner
|
||||
├── mail ───── Debian VM running mailcow (not NixOS)
|
||||
└── 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
|
||||
|
||||
@@ -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. |
|
||||
|
||||
+46
-5
@@ -171,6 +171,46 @@ default `/tmp/xchg/dev.key`), so dev VMs can decrypt the boxes' secrets without
|
||||
keys. Dev VMs also get DHCP on `eth0`, an SSH port forward (host 2222 → guest 22), and are
|
||||
automatically excluded from deploy targets.
|
||||
|
||||
## OpenWrt images
|
||||
|
||||
The OpenWrt boxes are not NixOS and are not deployed by this flake, but their firmware is built
|
||||
here. [`openwrt/default.nix`](../openwrt/default.nix) declares one image per box and packages it
|
||||
through [`astro/nix-openwrt-imagebuilder`](https://github.com/astro/nix-openwrt-imagebuilder),
|
||||
which drives OpenWrt's official ImageBuilder — prebuilt target packages assembled into a sysupgrade
|
||||
image, with no cross-toolchain involved.
|
||||
|
||||
| Output | Box | Release |
|
||||
|---|---|---|
|
||||
| `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
|
||||
one with `nix build .#openwrt-fergal`; the result holds the `-squashfs-sysupgrade.bin` to flash,
|
||||
plus a package manifest and an SBOM. Getting it onto the box is a guided procedure of its own —
|
||||
see [`openwrt-flash.md`](openwrt-flash.md).
|
||||
|
||||
Packages are baked into the image rather than installed on the box. OpenWrt's package server keeps
|
||||
only the current build of each feed, so a box that installs packages at runtime stops being able to
|
||||
do so as soon as the feed moves on from the firmware it is running. Adding a package means editing
|
||||
the image's `packages` list and reflashing.
|
||||
|
||||
### The feed pin
|
||||
|
||||
OpenWrt's download server is never at rest: snapshot is rebuilt daily, and
|
||||
`releases/<version>/packages/` is a symlink to the rolling `packages-<major>` feed shared by every
|
||||
point release. Building straight against it fails on hash mismatches and, worse, resolves the
|
||||
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 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
|
||||
|
||||
GitHub/Gitea Actions workflows live in [`.gitea/workflows/`](../.gitea/workflows).
|
||||
@@ -182,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`
|
||||
|
||||
|
||||
@@ -0,0 +1,213 @@
|
||||
# Installing a box
|
||||
|
||||
Procedure for bringing a new NixOS box into this flake, from bare hardware booted into the custom
|
||||
installer through to a deployable system. Written to be followed by a person or any coding agent; a
|
||||
Claude Code entry point exists at `.claude/skills/install-box/` but the steps below are the
|
||||
canonical source.
|
||||
|
||||
The install is **guided, not automated**: the mechanical steps (probing hardware, partitioning,
|
||||
writing the config, evaluating it) can be done straight through, but stop at the judgment points
|
||||
(marked ⏸) — what the box actually is, wiping disks, and running `do-install` itself. Keep a running
|
||||
summary and present it before any destructive step.
|
||||
|
||||
For the installer image itself — what it contains, how it is built and released — see
|
||||
[`misc/installer.md`](misc/installer.md).
|
||||
|
||||
## Setup facts
|
||||
|
||||
- **Installer access:** the box boots the custom installer (ISO, kexec or netboot) and is reached
|
||||
over SSH as `root` with `.keys/deploy.key`. The devshell sets
|
||||
`INSTALLER_SSH_OPTS = "-i .keys/deploy.key"`; set `INSTALLER` to the address, and
|
||||
`INSTALLER_SSH_PORT` if it is not 22.
|
||||
- **Devshell commands** (from [`devshell/install.nix`](../devshell/install.nix)):
|
||||
`installer-shell [cmd]` and `do-install [--no-bootloader] [--no-substitute] <system>`.
|
||||
- **`INSTALL_ROOT`** is `/mnt` in the installer's environment — everything is mounted under it and
|
||||
`do-install` reads it from the installer rather than assuming.
|
||||
- **`show-hw-config`** is a shell *alias* in the installer (wrapping
|
||||
`nixos-generate-config --show-hardware-config --root $INSTALL_ROOT`), so it needs an interactive
|
||||
shell: `installer-shell bash -lic show-hw-config`. A plain `installer-shell show-hw-config` will
|
||||
not find it.
|
||||
- **Validation:** `check-system <host>` evaluates a system without building it — use it while
|
||||
iterating. Only `build-system` when you need the artifact.
|
||||
- Nix reads the flake through git, so **`git add` new files before evaluating** — an untracked
|
||||
box directory fails with "Path … is not tracked by Git", not a Nix error.
|
||||
|
||||
## Phase 1 — Establish what the box is
|
||||
|
||||
⏸ Settle these before writing anything; they decide where every file goes and they are not
|
||||
recoverable from the hardware:
|
||||
|
||||
1. **Name and site** — the box name doubles as the `nixos.systems.<name>` attribute, the deploy node
|
||||
name and the docs page name. The site decides the directory (`nixos/boxes/<site>/`), the
|
||||
constants block in [`lib/constants.nix`](../lib/constants.nix) it draws prefixes from, and the
|
||||
docs directory (`docs/sites/<site>/`, `docs/remote/`, `docs/mobile/`).
|
||||
2. **Role** — what it does, which decides its modules, networking and firewall config.
|
||||
3. **nixpkgs channel** — `unstable` / `stable` / `mine` / `mine-stable`; match the site's other
|
||||
boxes unless there is a reason not to.
|
||||
4. **Networking** — whether it gets `assignments` now, or bootstraps on DHCP because it is being
|
||||
staged somewhere other than its final home. A box with no assignment still needs a reachable
|
||||
`my.deploy.node.hostname`, since the default (`config.networking.fqdn`) will not resolve.
|
||||
|
||||
## Phase 2 — Reach the installer and inventory the hardware
|
||||
|
||||
With the box booted into the installer and `INSTALLER` set:
|
||||
|
||||
1. Confirm you are talking to the right thing — `installer-shell hostname` reports `installer`, and
|
||||
`/etc/os-release` carries `VARIANT_ID=installer`.
|
||||
2. Collect the inventory you will need for both the config and the docs page: `lscpu`, `free -h`,
|
||||
`lsblk -o NAME,SIZE,TYPE,FSTYPE,MODEL,SERIAL`, `ip -br link`, `ip -br addr`,
|
||||
`lspci -nn | grep -Ei 'ethernet|network|nvme|sata|raid'`, and whether `/sys/firmware/efi` exists.
|
||||
3. Record every NIC's **permanent MAC** against its PCI address — interface naming in Phase 5 pins
|
||||
names to MACs, and the PCI order tells you which physical port is which.
|
||||
4. Run `installer-shell bash -lic show-hw-config` now for the kernel-module lists. Filesystems are
|
||||
not mounted yet, so run it again in Phase 4 for those.
|
||||
|
||||
## Phase 3 — Partition, format and mount
|
||||
|
||||
⏸ Destructive. Check the target disks are the ones you think they are and that nothing on them is
|
||||
wanted, then show the exact command sequence and get confirmation before running it.
|
||||
|
||||
The house layout is a tmpfs root (`my.tmproot`) with three mounts: an ESP at `/boot`, `/nix`, and
|
||||
`/persist` (`neededForBoot = true`). Use **`sgdisk`** for partitioning and put `/nix` and `/persist`
|
||||
on **LVM** so they can be resized later:
|
||||
|
||||
```sh
|
||||
sgdisk -Z /dev/<disk>
|
||||
sgdisk \
|
||||
-n 1:0:+2G -t 1:ef00 -c 1:esp \
|
||||
-n 2:0:0 -t 2:8e00 -c 2:lvm \
|
||||
/dev/<disk>
|
||||
partprobe /dev/<disk>
|
||||
|
||||
pvcreate /dev/<disk>p2
|
||||
vgcreate main /dev/<disk>p2
|
||||
lvcreate -L 48G -n <host>-nix main
|
||||
lvcreate -l 100%FREE -n <host>-persist main
|
||||
|
||||
mkfs.vfat -n ESP /dev/<disk>p1
|
||||
mkfs.ext4 -L nix /dev/main/<host>-nix
|
||||
mkfs.ext4 -L persist /dev/main/<host>-persist
|
||||
```
|
||||
|
||||
Conventions worth keeping: volume group `main`, logical volumes `<host>-nix` / `<host>-persist`,
|
||||
and ext4 filesystem labels `nix` and `persist`. Size the ESP and `/nix` to the box — 2 GiB and
|
||||
48 GiB suit a small single-disk box.
|
||||
|
||||
Then mount everything under `$INSTALL_ROOT`, with a tmpfs standing in for the eventual tmpfs root:
|
||||
|
||||
```sh
|
||||
mount -t tmpfs -o size=2G tmpfs "$INSTALL_ROOT"
|
||||
mkdir -p "$INSTALL_ROOT"/{nix,persist,boot}
|
||||
mount /dev/main/<host>-nix "$INSTALL_ROOT/nix"
|
||||
mount /dev/main/<host>-persist "$INSTALL_ROOT/persist"
|
||||
mount /dev/<disk>p1 "$INSTALL_ROOT/boot"
|
||||
```
|
||||
|
||||
### Seed the SSH host key
|
||||
|
||||
The installer generates fresh host keys on every boot, so adopt them as the box's own rather than
|
||||
letting it generate another set on first boot. Copy them onto the persist volume now:
|
||||
|
||||
```sh
|
||||
install -d -m 0755 "$INSTALL_ROOT/persist/etc/ssh"
|
||||
for t in ed25519 rsa; do
|
||||
install -m 0600 "/etc/ssh/ssh_host_${t}_key" "$INSTALL_ROOT/persist/etc/ssh/ssh_host_${t}_key"
|
||||
install -m 0644 "/etc/ssh/ssh_host_${t}_key.pub" "$INSTALL_ROOT/persist/etc/ssh/ssh_host_${t}_key.pub"
|
||||
done
|
||||
```
|
||||
|
||||
`my.tmproot` persists `services.openssh.hostKeys` at exactly those paths, so the installed system
|
||||
picks them up. This means the box's key is known **before** it first boots, so `my.secrets.key` can
|
||||
be set and its secrets encrypted as part of the same pass — no install, boot, re-encrypt, re-deploy
|
||||
round trip. (For a box already up, the `ssh-get-ed25519 <host>` devshell command prints the same
|
||||
value in the form `my.secrets.key` wants.)
|
||||
|
||||
## Phase 4 — Capture the hardware config
|
||||
|
||||
Re-run `installer-shell bash -lic show-hw-config` with the filesystems mounted.
|
||||
|
||||
Read the whole generated file and carry over **anything** in it that the flake does not already
|
||||
provide — it reflects what was actually detected on this hardware, and the list below is just what
|
||||
usually shows up, not a limit:
|
||||
|
||||
- `boot.initrd.availableKernelModules` and `boot.initrd.kernelModules` (LVM adds `dm-snapshot`)
|
||||
- `boot.kernelModules` (`kvm-intel` / `kvm-amd`) and the microcode attribute
|
||||
- the ESP's `by-uuid` device, and the device paths for `/nix` and `/persist`
|
||||
- anything else it emits — `boot.extraModulePackages`, `hardware.*` attributes, `swapDevices`,
|
||||
additional detected filesystems, `imports` such as `not-detected.nix`
|
||||
|
||||
The test is conflict, not familiarity: drop an option only when a nixfiles module already sets it,
|
||||
and keep it otherwise. The flake's own modules cover the bootloader, `initrd.systemd`,
|
||||
`initrd.services.lvm`, the kernel package and `nixpkgs.hostPlatform` (see
|
||||
[`nixos/modules/common.nix`](../nixos/modules/common.nix) and
|
||||
[`nixos/default.nix`](../nixos/default.nix)), so those are the ones to leave out. Don't paste the
|
||||
file in wholesale either — translate it into the box's own style, and reference LVM volumes as
|
||||
`/dev/main/<host>-nix` rather than the generated `/dev/mapper/main-<host>--nix`.
|
||||
|
||||
## Phase 5 — Write the box config
|
||||
|
||||
Create `nixos/boxes/<site>/<host>/default.nix` (a directory, so per-topic files can be added
|
||||
alongside it later) declaring `nixos.systems.<host>`, and add its path to the `configs` list in
|
||||
[`flake.nix`](../flake.nix). Then `git add` it.
|
||||
|
||||
The minimum is `system`, `nixpkgs`, `home-manager` and a `configuration` with the hardware from
|
||||
Phase 4, the three filesystems, and networking. Beyond that:
|
||||
|
||||
- **Interface naming:** pin names to hardware with `.link` files matching `PermanentMACAddress`,
|
||||
named for speed and index — `et1g0`, `et2g5-0`, `et10g-1`. Never rely on predictable-interface
|
||||
names in the `.network` files.
|
||||
- **Servers** set `my.server.enable = true`.
|
||||
- **Secrets:** set `my.secrets.key` to the ed25519 public key seeded in Phase 3 (the key only, no
|
||||
`root@installer` comment). Note that **every box declares at least one secret** even if its own
|
||||
config declares none: [`nixos/modules/user.nix`](../nixos/modules/user.nix) adds
|
||||
`user-passwd.txt` whenever `my.user.enable` is on, which is the default. So setting
|
||||
`my.secrets.key` always adds the box to that file's recipients, and
|
||||
`ragenix --rekey-one secrets/user-passwd.txt.age` is required — skip it and the box cannot
|
||||
decrypt its user password on first boot. Confirm what the box actually declares with
|
||||
`nix eval .#nixosConfigurations.<host>.config.age.secrets --apply builtins.attrNames`, and
|
||||
re-encrypt each of those files the same way. Create any new secrets with `ragenix -e <path>`.
|
||||
Never use `--rekey`, which rewrites every secret in `secrets/`.
|
||||
- **A box staged away from its final home** gets a bootstrap `.network` taking DHCP, plus
|
||||
`systemd.network.wait-online.anyInterface = true` so boot does not block on unpatched ports, and
|
||||
an explicit `my.deploy.node.hostname`. Comment it as temporary and say what replaces it.
|
||||
|
||||
Validate with `check-system <host>` and fix eval errors before going near the target.
|
||||
|
||||
## Phase 6 — Install
|
||||
|
||||
⏸ The maintainer may want to run this step themselves; ask rather than assume.
|
||||
|
||||
`do-install <host>` builds the system's `toplevel`, `nix copy`s the closure into the installer's
|
||||
`$INSTALL_ROOT` store, points `/nix/var/nix/profiles/system` at it, touches `/etc/NIXOS`, and runs
|
||||
`switch-to-configuration boot` with `NIXOS_INSTALL_BOOTLOADER=1`. It prompts for confirmation and
|
||||
prints the target it resolved.
|
||||
|
||||
- `--no-bootloader` skips the bootloader install (for a box that boots by other means).
|
||||
- `--no-substitute` copies everything from the local store instead of letting the target substitute.
|
||||
|
||||
## Phase 7 — First boot and post-install
|
||||
|
||||
1. Reboot the box off the installer and confirm it comes up: it should get its address, and
|
||||
`hostname` should be the system name. Its SSH host key is the one seeded in Phase 3, so it
|
||||
presents the same fingerprint the installer did.
|
||||
2. **Secrets.** If Phase 5 set `my.secrets.key`, they already decrypt. [`secrets.nix`](../secrets.nix)
|
||||
computes the ragenix recipient list from that key at evaluation time, so nothing needs
|
||||
regenerating — but any secret added to the box later must be re-encrypted for the new recipient
|
||||
list with `ragenix --rekey-one <path>`, one file at a time. Never reach for `ragenix --rekey`:
|
||||
it rewrites every secret in `secrets/` and buries the actual change in churn.
|
||||
3. **Deploy.** `deploy .#<host>` should now work over the `deploy` user. If the box is staged
|
||||
somewhere without its final DNS name, `deploy --hostname <address> .#<host>` overrides the node
|
||||
hostname for one run.
|
||||
|
||||
## Phase 8 — Document it
|
||||
|
||||
Per [`AGENTS.md`](../AGENTS.md), a new box means:
|
||||
|
||||
- a box page under the right docs directory, following the standard layout (H1 + one-line intro;
|
||||
`Source` / `Host` / `nixpkgs` bullets; hardware inventory; `## Role`; `## Network assignments`
|
||||
linking to [`networking.md#box-assignments`](networking.md#box-assignments), or a short
|
||||
explanation if it has none yet; one `##` per topic; `## Notable config files` last);
|
||||
- a row in the site index `README.md` boxes table;
|
||||
- affected prose in [`networking.md`](networking.md) — the assignment tables themselves are
|
||||
CI-generated, so write the prose and leave the tables alone;
|
||||
- the site diagram in [`README.md`](README.md) if the box changes its layout.
|
||||
@@ -34,8 +34,10 @@ The custom NixOS installer image used to bootstrap new boxes.
|
||||
|
||||
## Installing a box
|
||||
|
||||
The devshell's installer commands ([`devshell/install.nix`](../../devshell/install.nix)) drive
|
||||
an install over SSH against a booted installer reachable at `$INSTALLER`:
|
||||
The end-to-end procedure — hardware inventory, partitioning, writing the box config, installing and
|
||||
documenting it — is in [`install-box.md`](../install-box.md). The devshell's installer commands
|
||||
([`devshell/install.nix`](../../devshell/install.nix)) drive an install over SSH against a booted
|
||||
installer reachable at `$INSTALLER`:
|
||||
|
||||
- `installer-shell` — get a shell on the installer.
|
||||
- `do-install <system>` — builds the system's toplevel, `nix copy`s the closure to the
|
||||
|
||||
@@ -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 | |
|
||||
@@ -266,6 +267,11 @@ On top of that: `p2pTunnels` (`10.100.5.0/24`) holds point-to-point tunnel /30s
|
||||
public blocks and the per-customer `mail` / `darts` / `jam` prefixes carry customer-facing
|
||||
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 colony
|
||||
assignments yet (only a home `hi` one, from being staged at home) and the replacement topology is
|
||||
still being designed.
|
||||
|
||||
## home
|
||||
|
||||
The home site prefixes (`lib.my.c.home.prefixes`) come from `192.168.64.0/18` and
|
||||
@@ -383,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
@@ -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.
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
# Flashing an OpenWrt box
|
||||
|
||||
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/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.
|
||||
|
||||
## Phase 1 — Build
|
||||
|
||||
```sh
|
||||
nix build .#openwrt-<box>
|
||||
```
|
||||
|
||||
The result holds the `-squashfs-sysupgrade.bin` to flash, plus a `.manifest` listing every package
|
||||
in the image and an SBOM. Check the manifest for the packages the change was meant to add — an
|
||||
unknown package name is not an error at build time, it just silently isn't there.
|
||||
|
||||
## Phase 2 — Pre-flight
|
||||
|
||||
Confirm on the box:
|
||||
|
||||
```sh
|
||||
grep -E 'RELEASE|REVISION' /etc/openwrt_release # what is running now
|
||||
mount | grep -E ' / | /overlay | /rom ' # flash or RAM? (see below)
|
||||
uci get network.lan.ipaddr # will it come back reachable?
|
||||
cat /lib/upgrade/keep.d/* # what survives the flash
|
||||
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/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.
|
||||
|
||||
`keep.d` normally lists `/etc/config/`, `/etc/dropbear/authorized_keys` and the dropbear host keys,
|
||||
so an ordinary flash preserves both access and identity. Verify rather than assume — losing
|
||||
`authorized_keys` on a box reachable only over SSH means a serial console recovery.
|
||||
|
||||
## Phase 3 — Back up
|
||||
|
||||
```sh
|
||||
sysupgrade -b /tmp/<box>-config-backup.tar.gz
|
||||
```
|
||||
|
||||
Fetch it with `ssh <box> 'cat /tmp/…' > local.tar.gz`. **`scp` does not work** — these boxes have no
|
||||
`/usr/libexec/sftp-server`, so it fails with `Connection closed`. (`scp -O` forces the legacy
|
||||
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/colony/fergal.md#flash-layout).
|
||||
|
||||
## Phase 4 — Stage and validate
|
||||
|
||||
```sh
|
||||
ssh <box> 'cat > /tmp/sysupgrade.bin' < <image>.bin
|
||||
ssh <box> 'sha256sum /tmp/sysupgrade.bin; sysupgrade -T /tmp/sysupgrade.bin'
|
||||
```
|
||||
|
||||
Compare the sha256 against the local file, and require `sysupgrade -T` to exit 0. `-T` validates the
|
||||
image and its device-compatibility metadata without writing anything, which is the last cheap chance
|
||||
to catch a wrong-profile image.
|
||||
|
||||
## Phase 5 — Flash ⏸
|
||||
|
||||
Confirm before this point. It reboots the box and is not interruptible.
|
||||
|
||||
```sh
|
||||
ssh <box> 'setsid sh -c "sleep 2; sysupgrade -v /tmp/sysupgrade.bin" \
|
||||
</dev/null >/tmp/upgrade.log 2>&1 & echo detached'
|
||||
```
|
||||
|
||||
**Detaching matters.** `sysupgrade` kills the SSH session partway through; without `setsid` the
|
||||
upgrade dies with it, potentially after the flash has been erased. `nohup` is not available on these
|
||||
boxes' busybox — use `setsid`.
|
||||
|
||||
Plain `sysupgrade` keeps the config in `keep.d`. Do not reach for `-c` out of caution: it needs
|
||||
`/overlay/upper/etc` and aborts *after* erasing the firmware if that is missing.
|
||||
|
||||
## Phase 6 — Wait and verify
|
||||
|
||||
Poll SSH, not ping. A successful ping returns in milliseconds, so a naive "wait for it to go down"
|
||||
loop finishes before the box has even started rebooting. Sleep between probes and wait on something
|
||||
that only succeeds once userspace is up:
|
||||
|
||||
```sh
|
||||
for i in $(seq 1 40); do
|
||||
sleep 15
|
||||
ssh -o ConnectTimeout=5 -o BatchMode=yes <box> 'grep REVISION /etc/openwrt_release' && break
|
||||
done
|
||||
```
|
||||
|
||||
Expect roughly three minutes. Then confirm the revision changed, the management address returned,
|
||||
the package count matches the manifest, and the new packages are actually present and running.
|
||||
Connecting without host-key overrides also confirms the host keys survived.
|
||||
|
||||
If the box does not return, it needs the serial console — have that confirmed as reachable *before*
|
||||
Phase 5, not after.
|
||||
@@ -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. |
|
||||
|
||||
@@ -24,9 +24,16 @@ prefixes and routing overview are in the [`colony` section of networking.md](../
|
||||
| [`git`](git.md) | Gitea + Gitea Actions runner |
|
||||
| [`mail`](mail.md) | Debian VM running mailcow (not NixOS) |
|
||||
| [`darts`](darts.md) | Third-party/customer VM (not NixOS) |
|
||||
| [`portcullis`](portcullis.md) | Bare-metal edge box for Nikhef; being staged, not yet in service |
|
||||
|
||||
The applications running on `shill` are listed on its own page — see
|
||||
[shill/README.md](shill/README.md#containers).
|
||||
|
||||
`mail` and `darts` are host-defined VMs whose guest operating systems are managed out of band; their
|
||||
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 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).
|
||||
|
||||
@@ -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.
|
||||
@@ -0,0 +1,120 @@
|
||||
# portcullis
|
||||
|
||||
A bare-metal box destined for Nikhef, intended to take over most of the colony edge
|
||||
routing currently done by the [`estuary`](estuary.md) VM.
|
||||
|
||||
- **Source:** [`nixos/boxes/colony/portcullis/`](../../../nixos/boxes/colony/portcullis)
|
||||
- **Host:** bare metal
|
||||
- **nixpkgs:** `mine-stable`
|
||||
|
||||
## Hardware
|
||||
|
||||
| Component | Inventory |
|
||||
|---|---|
|
||||
| Platform | Mini PC (no vendor DMI strings) |
|
||||
| CPU | Intel N150 (4 cores / 4 threads) |
|
||||
| Memory | 8 GiB |
|
||||
| Storage | One 128 GB NVMe SSD (`nvme0n1`), partitioned as a 2 GiB ESP plus an LVM PV holding the `nix` and `persist` volumes |
|
||||
| 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,
|
||||
taking over most of what `estuary` does today — WAN termination, firewalling and NAT, BGP for
|
||||
AS211024 and DNS. Some of that functionality stays on `estuary`, and the surrounding network
|
||||
topology will change with the move, so the split is not settled yet. Until it is, the config in
|
||||
this repository covers only what is needed to boot and reach the box.
|
||||
|
||||
## Network assignments
|
||||
|
||||
`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: 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
|
||||
|
||||
A single NVMe SSD, following the usual tmpfs-root layout: a 2 GiB ESP at `/boot`, then one LVM PV
|
||||
in volume group `main` carrying `portcullis-nix` (48 GiB, `/nix`) and `portcullis-persist` (the
|
||||
remainder, `/persist`).
|
||||
|
||||
## Secrets
|
||||
|
||||
`my.secrets.key` is the SSH host key adopted from the installer session at install time (seeded onto
|
||||
the persist volume before first boot), so secrets could be encrypted for the box without waiting for
|
||||
it to come up. The box declares nothing of its own yet — only the default `user-passwd.txt` that
|
||||
`my.user` brings in.
|
||||
|
||||
## Notable config files
|
||||
|
||||
- [`nixos/boxes/colony/portcullis/default.nix`](../../../nixos/boxes/colony/portcullis/default.nix) — hardware, filesystems and bootstrap networking.
|
||||
@@ -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)) |
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -11,7 +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).
|
||||
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
|
||||
|
||||
@@ -34,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 |
|
||||
+--------------------+ +---------------+
|
||||
| |
|
||||
@@ -146,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
|
||||
|
||||
@@ -200,11 +207,22 @@ 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 (portcullis's switch)
|
||||
|
||||
**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.
|
||||
|
||||
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
|
||||
|
||||
The switches resolve by **short hostname** on the home network — the home routers serve their
|
||||
|
||||
Generated
+145
-226
@@ -8,7 +8,7 @@
|
||||
"ragenix",
|
||||
"nixpkgs"
|
||||
],
|
||||
"systems": "systems_7"
|
||||
"systems": "systems_8"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1761656077,
|
||||
@@ -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,18 +282,39 @@
|
||||
]
|
||||
},
|
||||
"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",
|
||||
"url": "https://flakehub.com/f/hercules-ci/flake-parts/0.1"
|
||||
}
|
||||
},
|
||||
"flake-parts_2": {
|
||||
"inputs": {
|
||||
"nixpkgs-lib": [
|
||||
"openwrt-imagebuilder",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1772408722,
|
||||
"narHash": "sha256-rHuJtdcOjK7rAHpHphUb1iCvgkU3GpfvicLMwwnfMT0=",
|
||||
"owner": "hercules-ci",
|
||||
"repo": "flake-parts",
|
||||
"rev": "f20dc5d9b8027381c474144ecabc9034d6a839a3",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "hercules-ci",
|
||||
"repo": "flake-parts",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-utils": {
|
||||
"inputs": {
|
||||
"systems": "systems"
|
||||
@@ -333,57 +333,6 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-utils_10": {
|
||||
"inputs": {
|
||||
"systems": "systems_9"
|
||||
},
|
||||
"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_10"
|
||||
},
|
||||
"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"
|
||||
@@ -503,7 +452,7 @@
|
||||
},
|
||||
"flake-utils_9": {
|
||||
"inputs": {
|
||||
"systems": "systems_8"
|
||||
"systems": "systems_9"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1731533236,
|
||||
@@ -522,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",
|
||||
@@ -553,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": {
|
||||
@@ -574,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": {
|
||||
@@ -616,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": {
|
||||
@@ -636,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": {
|
||||
@@ -653,7 +599,7 @@
|
||||
"home-manager": [
|
||||
"home-manager-unstable"
|
||||
],
|
||||
"nixpkgs": "nixpkgs_4"
|
||||
"nixpkgs": "nixpkgs_5"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1769548169,
|
||||
@@ -672,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": {
|
||||
@@ -688,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": {
|
||||
@@ -756,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": {
|
||||
@@ -772,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": {
|
||||
@@ -804,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": {
|
||||
@@ -819,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": {
|
||||
@@ -863,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=",
|
||||
@@ -878,35 +840,48 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs_5": {
|
||||
"openwrt-feeds": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"nixpkgs-unstable"
|
||||
],
|
||||
"openwrt-imagebuilder": [
|
||||
"openwrt-imagebuilder"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1709309926,
|
||||
"narHash": "sha256-VZFBtXGVD9LWTecGi6eXrE0hJ/mVB3zGUlHImUs2Qak=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "79baff8812a0d68e24a836df0a364c678089e2c7",
|
||||
"lastModified": 1787522666,
|
||||
"narHash": "sha256-Ev4X1HCx6aV4L5GtMQX9DJRgWQ7K914rT11nL7mIkmw=",
|
||||
"owner": "devplayer0",
|
||||
"repo": "openwrt-feeds",
|
||||
"rev": "53720becf1e89473660e25107ae5d23bf1465621",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-23.11",
|
||||
"repo": "nixpkgs",
|
||||
"owner": "devplayer0",
|
||||
"repo": "openwrt-feeds",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs_6": {
|
||||
"openwrt-imagebuilder": {
|
||||
"inputs": {
|
||||
"flake-parts": "flake-parts_2",
|
||||
"nixpkgs": [
|
||||
"nixpkgs-unstable"
|
||||
],
|
||||
"systems": "systems_7"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1674990008,
|
||||
"narHash": "sha256-4zOyp+hFW2Y7imxIpZqZGT8CEqKmDjwgfD6BzRUE0mQ=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "d2bbcbe6c626d339b25a4995711f07625b508214",
|
||||
"lastModified": 1787474509,
|
||||
"narHash": "sha256-jL5RS/TbKk7HxjsGyFWeceHveyRgM8btvfY9Z77P9jM=",
|
||||
"owner": "astro",
|
||||
"repo": "nix-openwrt-imagebuilder",
|
||||
"rev": "4371439b1e4e582266fc38345d1a01db1f8db6d6",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixpkgs-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"owner": "astro",
|
||||
"repo": "nix-openwrt-imagebuilder",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
@@ -976,8 +951,9 @@
|
||||
"nixpkgs-mine-stable": "nixpkgs-mine-stable",
|
||||
"nixpkgs-stable": "nixpkgs-stable",
|
||||
"nixpkgs-unstable": "nixpkgs-unstable",
|
||||
"ragenix": "ragenix",
|
||||
"sharry": "sharry"
|
||||
"openwrt-feeds": "openwrt-feeds",
|
||||
"openwrt-imagebuilder": "openwrt-imagebuilder",
|
||||
"ragenix": "ragenix"
|
||||
}
|
||||
},
|
||||
"rust-overlay": {
|
||||
@@ -988,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": {
|
||||
@@ -1001,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,
|
||||
@@ -1058,21 +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_2": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
@@ -1150,16 +1069,16 @@
|
||||
},
|
||||
"systems_7": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"lastModified": 1680978846,
|
||||
"narHash": "sha256-Gtqg8b/v49BFDpDetjclCYXm8mAnTrUzR0JnE2nv5aw=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"repo": "x86_64-linux",
|
||||
"rev": "2ecfcac5e15790ba6ce360ceccddb15ad16d08a8",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"repo": "x86_64-linux",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
@@ -1201,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": {
|
||||
|
||||
@@ -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";
|
||||
@@ -58,9 +59,16 @@
|
||||
# harmonia.url = "github:devplayer0/harmonia/cache-config-daemon-store";
|
||||
harmonia.inputs.nixpkgs.follows = "nixpkgs-unstable";
|
||||
|
||||
# Firmware building for the OpenWrt boxes, which aren't managed by this flake otherwise.
|
||||
# `openwrt-feeds` pins the package feeds; without it, evaluation would reach the OpenWrt
|
||||
# download server over import-from-derivation and break on hashes that upstream rotates daily.
|
||||
openwrt-imagebuilder.url = "github:astro/nix-openwrt-imagebuilder";
|
||||
openwrt-imagebuilder.inputs.nixpkgs.follows = "nixpkgs-unstable";
|
||||
openwrt-feeds.url = "github:devplayer0/openwrt-feeds";
|
||||
openwrt-feeds.inputs.nixpkgs.follows = "nixpkgs-unstable";
|
||||
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";
|
||||
@@ -181,6 +189,7 @@
|
||||
# Systems
|
||||
nixos/installer.nix
|
||||
nixos/boxes/colony
|
||||
nixos/boxes/colony/portcullis
|
||||
nixos/boxes/tower
|
||||
nixos/boxes/home/stream.nix
|
||||
nixos/boxes/home/palace
|
||||
@@ -258,7 +267,9 @@
|
||||
deploy = recurseIntoAttrs (pkgs.deploy-rs.lib.deployChecks self.deploy);
|
||||
};
|
||||
|
||||
packages = flattenTree (import ./pkgs { inherit lib pkgs; });
|
||||
packages = flattenTree (
|
||||
(import ./pkgs { inherit lib pkgs; }) //
|
||||
(import ./openwrt { inherit pkgs inputs; }));
|
||||
|
||||
devShells.default = shell;
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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}";
|
||||
|
||||
@@ -0,0 +1,175 @@
|
||||
{ lib, ... }:
|
||||
let
|
||||
inherit (lib.my) net;
|
||||
inherit (lib.my.c.colony) domain;
|
||||
home = lib.my.c.home;
|
||||
in
|
||||
{
|
||||
nixos.systems.portcullis = {
|
||||
system = "x86_64-linux";
|
||||
nixpkgs = "mine-stable";
|
||||
home-manager = "mine-stable";
|
||||
|
||||
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;
|
||||
cpu = {
|
||||
intel.updateMicrocode = true;
|
||||
};
|
||||
};
|
||||
|
||||
boot = {
|
||||
kernelModules = [ "kvm-intel" ];
|
||||
# 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" ];
|
||||
};
|
||||
};
|
||||
|
||||
fileSystems = {
|
||||
"/boot" = {
|
||||
device = "/dev/disk/by-uuid/1A70-EBCB";
|
||||
fsType = "vfat";
|
||||
options = [ "fmask=0022" "dmask=0022" ];
|
||||
};
|
||||
"/nix" = {
|
||||
device = "/dev/main/portcullis-nix";
|
||||
fsType = "ext4";
|
||||
};
|
||||
"/persist" = {
|
||||
device = "/dev/main/portcullis-persist";
|
||||
fsType = "ext4";
|
||||
neededForBoot = true;
|
||||
};
|
||||
};
|
||||
|
||||
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
|
||||
ethtool
|
||||
lm_sensors
|
||||
smartmontools
|
||||
];
|
||||
|
||||
systemd.network = {
|
||||
# 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 = nicTuning // { Name = "et2g5-0"; };
|
||||
};
|
||||
"10-et2g5-1" = {
|
||||
matchConfig.PermanentMACAddress = "00:d0:b4:05:ed:49";
|
||||
linkConfig = nicTuning // { Name = "et2g5-1"; };
|
||||
};
|
||||
"10-et2g5-2" = {
|
||||
matchConfig.PermanentMACAddress = "00:d0:b4:05:ed:4a";
|
||||
linkConfig = nicTuning // { Name = "et2g5-2"; };
|
||||
};
|
||||
"10-et2g5-3" = {
|
||||
matchConfig.PermanentMACAddress = "00:d0:b4:05:ed:4b";
|
||||
linkConfig = nicTuning // { Name = "et2g5-3"; };
|
||||
};
|
||||
|
||||
"11-et10g-0" = {
|
||||
matchConfig.PermanentMACAddress = "60:be:b4:2e:9b:a2";
|
||||
linkConfig = nicTuning // { Name = "et10g-0"; };
|
||||
};
|
||||
"11-et10g-1" = {
|
||||
matchConfig.PermanentMACAddress = "60:be:b4:2e:9b:a3";
|
||||
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 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-*";
|
||||
DHCP = "yes";
|
||||
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 deploy over the staging hi address.
|
||||
deploy.node.hostname = assignments.hi.ipv4.address;
|
||||
|
||||
secrets = {
|
||||
key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAUolR93Byg+Daw8pUYHVpQ34ioxSc2C8vzj9F4KbqMs";
|
||||
};
|
||||
|
||||
server.enable = true;
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -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; }
|
||||
];
|
||||
};
|
||||
})
|
||||
|
||||
@@ -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 = [
|
||||
|
||||
@@ -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
@@ -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.";
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
{ pkgs, inputs }:
|
||||
# Firmware for the OpenWrt boxes. They are not NixOS and are not deployed by this flake - these
|
||||
# outputs only build a sysupgrade image, which is then flashed by hand (see the box's docs page).
|
||||
#
|
||||
# Baking packages into the image is the only reliable way to have them: OpenWrt's package server
|
||||
# keeps just the current build of each feed, so a box installing packages at runtime is broken as
|
||||
# soon as the feed moves on from the firmware it is running.
|
||||
let
|
||||
inherit (inputs) openwrt-imagebuilder openwrt-feeds;
|
||||
|
||||
# Fallback to the release branch. Snapshot tracks OpenWrt main, which is where the rtl930x target
|
||||
# is actually being developed; the release runs a much older kernel. Both are pinned by
|
||||
# `openwrt-feeds`, so neither moves until that input is updated.
|
||||
release = "25.12.5";
|
||||
|
||||
mkImage = args: openwrt-imagebuilder.lib.build (args // {
|
||||
inherit pkgs;
|
||||
cachePath = openwrt-feeds.cachePaths.${args.release};
|
||||
});
|
||||
|
||||
# fergal, the 8-port SFP+ switch (XikeStor SKS8300-8X, board-branded ONTi ONT-S508CL-8S)
|
||||
fergal = {
|
||||
target = "realtek";
|
||||
variant = "rtl930x";
|
||||
profile = "xikestor_sks8300-8x";
|
||||
packages = [ "luci" "ip-full" "ip-bridge" "ethtool-full" ];
|
||||
};
|
||||
in
|
||||
{
|
||||
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; });
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -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-----
|
||||
+78
-74
@@ -1,76 +1,80 @@
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IHNqUFR5ZyBkWHB2
|
||||
MHNUSnRIQkc4OENhN2dVdVlFZFRlWW5oVW9WbENaTGcwK2ZnQVFRCkQyYjdNaEtK
|
||||
SXE5RExTMm9EZC9GSEJGcWNabUgyOERBZmFmV0VqRFVXWTgKLT4gc3NoLWVkMjU1
|
||||
MTkgRExNZUZnIEhJNHN3c0lGL1lrMTA0ZHV2bHdPZ0dveDNBNVlzazEwbU9NcVZl
|
||||
Z3RKelUKaWEwTTZvSTA0T2pKSGVoc3gwL3VPWjNPOFk3dTNjYVo5S2tPWUNtV3Fw
|
||||
dwotPiBzc2gtZWQyNTUxOSAzYkIzWmcgMEN2dDJiRHYrQzRIb1JJVXp1V0ZyMkdu
|
||||
RWNtKy93QVR4SVJkK1VXTlUyQQpNdUU1c1NOdi9ZbmFTNHJBWmNTZFp2NDZORWp1
|
||||
ZzZzMzU3ZWFVYU1Lc1k0Ci0+IHNzaC1lZDI1NTE5IHErMFhjdyBiVS9ZeVE5Vytp
|
||||
a3p2c2xYM28rM0Q1UWMyNkQxYWRScStGRGVhTTYvQWc4ClNvSksydmhid20yTzNC
|
||||
ZHF2b252MWwzRG9Zdi9uRVpqL1BacGRaemo5OEkKLT4gc3NoLWVkMjU1MTkgWkIz
|
||||
ZTZRIFVZUTVjNS9pak9JSnF4N2JOMEZINmtKTzU5OUxHbHRKeng2cldvK2NXU2sK
|
||||
T0M3QTZJU2lqMk9nRGl2c3QwNTlWOEwyYVJUK3pleEtMZ0lYbm9TSmVUZwotPiBz
|
||||
c2gtZWQyNTUxOSBqNjdGWFEgRzRXYTBxQWduN2QvZk84NXVWVHlQN2RiS0pkYXM2
|
||||
U2cvM0JKRXZvTjAxdwpCbWdMZVpMaXBBNHRRZjN4aU5lNmhRNmMzSnBIWUNtbW1w
|
||||
ZStLaXlUL09ZCi0+IHNzaC1lZDI1NTE5IGMwVE5hUSBxK09aSTRaUzZ4VnArMlF4
|
||||
ZFZvNmUrR1hPKzB1SUdRS2Z2dmgyVlROOUVZCllUcDNCSEpVUmVUN2RSYjZ5aDdp
|
||||
SWhCVFlwcWxJMkxodEwrQXNDOTh0TlUKLT4gc3NoLWVkMjU1MTkgbjhDcFV3IHJ4
|
||||
Y1krN2hHTjhOakZSZ3VNNEZnYVVLb1BTZ01iT2dyRjdUdkRodit0QUkKRitpVFJB
|
||||
WDU2eGw2aUhQZFcrMTB6MU5VTURPNE5HbUFYendOMnNDRXRQcwotPiBzc2gtZWQy
|
||||
NTUxOSBWN2xnR3cgcUNRZkp6R0llR0o0RmhvanBoamdoNVBKRWl0Sm1sQVhBRGJw
|
||||
MFFDcmdUbwpENlEvaTh4OUhVQ0VTeXBGMTBoajd1eE42YzYvc0ZvcWlVYU1HWnkr
|
||||
R0FnCi0+IHNzaC1lZDI1NTE5IGpJOFJBZyBFdEpIT3F1MGVtZ0ppN05hVDBLRjZz
|
||||
eTRWdjZkMGM0SWpqeVJpRXRGc2pRClVpTlB6Q3lCZ3FXd1g1eXU3Y3grRVBJRjBC
|
||||
aERTanp4dDhGRFdteThNNTgKLT4gc3NoLWVkMjU1MTkgVCtzYkdBIEpselBValht
|
||||
TTNtY2lTYWg3VHBUTWNIemdiQXpHd01jMS9BeERMclQ0RGcKYU85dnN3ZEJyQmZZ
|
||||
ZE9ZYXl4Ym1BZlBkcm9jMXBhZ1J3aUlxR1dPNm96dwotPiBzc2gtZWQyNTUxOSBo
|
||||
TWE0bncgcXdIK21EVDVMZGhMMEltQ3RNbkx5NDhGVWRVdU4wZlhUNDY0bEZTcEZG
|
||||
QQpUSGZOb0FoSTgxckp0R3dxVjVPZkQ0b3Z5WXpjU1Q1Qy8zaGNVNzh4ZGJvCi0+
|
||||
IHNzaC1lZDI1NTE5IGV5cTNkZyBwaGF3NXVNWUVQUUpuUm9pVHVRK1NoV1FmVFMz
|
||||
dys1bE9WaDdNV0RXZFRVCmVJSUx0VkQvbDRjOWdvNlhCNm9RSnhnUHFpYnp0ZjVM
|
||||
UG82UElhM3R6MFkKLT4gc3NoLWVkMjU1MTkgN1dROVBBIERKYnFGQm9pVlIzYWxu
|
||||
M3Fza0RucE9SczQzRk5ialU2R215L1NwcVU2bW8Ka2hCL2JSU1c1bEhFS0t3VnEy
|
||||
YWtaMG5mZHBxYjNkK0JQUjlmVHJYSmNUMAotPiBzc2gtZWQyNTUxOSBnU3hQMFEg
|
||||
VHhRN0VEV0xGL0hJUHd1V0drWVJzaWtucXJ2c2xMUVpYMFc2TklLUGdYVQp0VWZj
|
||||
UEZGeGhaRDh4SmFsek93ejBUM1A3OVorTWFmcWt0QkVCZmV0QU5ZCi0+IHNzaC1l
|
||||
ZDI1NTE5IFZGY3c1ZyBmOHgraG50b2NiREZIcjRacTUwZ1Z3R2h0QXlHMTl6SFNJ
|
||||
Qlc4MTFtT2k4CkVSdzY3cEVpR3J3L0szMXBVdmd2OUFsWGJwanRtSGl3QmRyREhJ
|
||||
WjRwS2sKLT4gc3NoLWVkMjU1MTkgaGtidHZnIGVVNDZzenBDRlRmeW4ybUJqamRD
|
||||
UFFpWDY1ZjJuK1VQNWJJSGIyaFJnbmcKcmg3b284YmZQUWt0clBjVDZVZk5CUUlo
|
||||
aFRWWUwzVmVPcFBDaW1xKzRqSQotPiBzc2gtZWQyNTUxOSBldDJ6cFEgWEZpOXdx
|
||||
bWo3NnZYSjFTdldoSDBBMVVobHRXYWJjZEd2RVBIanRrQUZROApkZG82RUZHSkRH
|
||||
TGkyTG1tRUlRMEg5MVRwRHNjTE9UL1BMRUpDdXVLZ2tVCi0+IHNzaC1lZDI1NTE5
|
||||
IFpiTEpXQSB0ZWtnRFVWKzRkSU45enFadkx6N3FpRmE1MnByZEljd3FyWGFyd2Ja
|
||||
QVNvClpiZkxsQ04zR2pzOXBtODdnbjdSaHBtSVFVT3V2aFdyZ2FoaytISmNzR0UK
|
||||
LT4gc3NoLWVkMjU1MTkgWk5xSW9nIHMvWElOMTgvR0ZveHZLSlVqNW9PSmNvdm92
|
||||
ZW92dHhvbG8vRzl2NFdqMFEKUktLWEJuQ25LM1J6OXBvRlRlMEFEeXlhV3M1Yk85
|
||||
Wk0rZWJMQ3U2SVYrcwotPiBzc2gtZWQyNTUxOSBxTGpxeVEgODlpYkhNQmFkNjlp
|
||||
ZUYyZ0VDdzBsTmk5TGVPU0VTRnZlSUdMazN3cXB4awpGbEhWMVR0N0NzTGljeEpw
|
||||
dWtrTXR0QW5CVHE5enA2dXZZNm55ajVSa3NVCi0+IHNzaC1lZDI1NTE5IEJhUWxS
|
||||
ZyBJc2xSZUJkaEd5U1EyV0J6T2pUU1ZWMnRPSzYwNFRERndsdThYeFFQSUVJCmlT
|
||||
OWh1dnpNRDU5WlNSaW5FTUZ5QVVHSmw0NUpQMklOb3JYU3FSM3ZTWEUKLT4gc3No
|
||||
LWVkMjU1MTkgcytxUmZnIERvYU1PNXJkWEs0VkI5YWNuY3ZGK2xpVkpxN01zbHA2
|
||||
cm9DUzk1WHIrRWsKZVRGWUwrdmVnOTh4clFHdm1yL0JuSVRpVldjWkMwUkdFTk5J
|
||||
LzNlT0dZUQotPiBzc2gtZWQyNTUxOSA2MkpjY0EgTXI5QVZySXpsQlVoTE51c29j
|
||||
MWltaUZHZUlPUEo1WXZNdUUzSWdWRU94MApMVUYzYlhNZDc5RTRzc0NxNW5PblU0
|
||||
WndIV1FZTm51ZlhQdWFQa2NNS25FCi0+IHNzaC1lZDI1NTE5IC9oeC9kQSBWUkJS
|
||||
ZmR2a3BDdHRrVUhqejFjZFI4cWw1MEkrRWUwdHZHZEloemtUT2pBCmFYU21lRllo
|
||||
MTlic204cU41Vi95dFBMSSs2eWtVSzJndG1keWdVeUgxNFEKLT4gc3NoLWVkMjU1
|
||||
MTkgSEovSjdBIGhhck53d1ZzYVY5ZjF5VHpXYVBDMGZ5SGdTL3B4NHQ2a3lENGti
|
||||
NXhZVGMKV3B0d0IwNm5qM0xDUWdOTEZ3Q3ErWGdNRmtIeTBMSjV1eDYzMUVYVFpY
|
||||
cwotPiBzc2gtZWQyNTUxOSBPRXFNc2cgcm9ZOS81emFxd2toNTRFUW1LRi9jU3FF
|
||||
VUdYRzRWTm5uV0ZjclJPZmsyQQoxZnRtTzZ5YzRJTVRGalU4NFZhQmZlMjhtY2Nv
|
||||
Q1oyZURhQUpjR2dvRVNRCi0+IHNzaC1lZDI1NTE5IC9FSlh2ZyA5aUNiQk1FZUtU
|
||||
S0hta1lOMVlWL1RwdjUvQnl2MTg5VWQrMnNCVERkVkZrCkFraTR4UmFBbXpOR1lq
|
||||
TTFCSmZmV0R1VjhWb2V0RXdiYkpaek0rdGVsRE0KLT4gWDI1NTE5IEpZSlNOZk5D
|
||||
WlZ3VFFpVUx2RHlwblVEZHZyVVNGbHNrZ3hUY1FYQUJNMWMKYlo4dFVnS2hhYk1m
|
||||
THZXMktudExKdEE1enlGWUgyM3FiMGpFbmR6RkNyawotPiBGImFlO2V5Ky1ncmVh
|
||||
c2UKdTZXVjRtcTR2TDFITzB1d3J3RG1hejk1am15SEswR05PMFdoTXR2UVpoRE9H
|
||||
dkMvQldlc1FPWlRFSURXN3ppSQoyU0pSRHFIS2I3d1dtTE5OTFFXSTYyR2tTbTZB
|
||||
RDVROStRCi0tLSAvWEcyR09pQnRhSzN4d2kzSzduOUVtTXd2U25CWmZJdWt5NnNl
|
||||
RHVieGNBCi411IjgmUKttjX6ljaZGWivstOajx2pkTVLV/zFiEj3jv+KDGy1psZQ
|
||||
no+eatGMO8LeJhGJ6H7TBKOmJhFMfoQp1XKJA8OGY+FGZ98bit04djo3jqbVSOms
|
||||
JSPRTvTxxQx+40yO+ETV+2qkRU1OdJTobz9YvuqGlHrJS8UNN30QMPT0ienu3QTY
|
||||
Tbo=
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IHNqUFR5ZyArcmVy
|
||||
aWRwblUvTDlpdzVjTy9GdmsxUSswVlBoa01WU0J0WjVPNTlaazJZCnpjYW14dkU5
|
||||
RFBZS1B2V0J3U2ZkUzJCZlN3WUZ0QzUvRGc3QkUyL3VXRWsKLT4gc3NoLWVkMjU1
|
||||
MTkgRExNZUZnIGFFanNQbkFRQzJxcU5heE8xRlM2clZTaldSR3M2SzVyMHJDakFt
|
||||
eWNHU1EKcWFka3hQajV5d1NiaENUNFhOUlpoSFlJUm8xSWNXVE5HaGM3blp0OVAy
|
||||
TQotPiBzc2gtZWQyNTUxOSAzYkIzWmcgdFNqcnJoWjh1SDN5Vml5UC8rZ1NXSi82
|
||||
Sm11QXJKUXI5Ulc5Mi9rL3UxawpzSFVXQzBQbUZFM3l2aHlIU1dzREMvRXdrMWFL
|
||||
cEptWW4yVWF6aTJTVUdBCi0+IHNzaC1lZDI1NTE5IHErMFhjdyBCTnhBMG5RcTdt
|
||||
MHg5aVN2ZmZQR3VTcFo1K3lBMTh3c2dBRjlzRWdjM2pvCkpwZTFZVExJc25aLzFy
|
||||
OWZsNjVzMmNRREJ4ME56TVRneEZIdUdqODVZZnMKLT4gc3NoLWVkMjU1MTkgWkIz
|
||||
ZTZRIDlvYURqSFRVNUdEM1lIV3oxQ00zMG5CNXIyNnhrVXpFd3VhS0IwTDhqRlkK
|
||||
UzhsN3hLSUpQZ2lrNHNVd0s1aXBIY3ZaVm0rZjlXU0RSbU1Bb2h2NlBNawotPiBz
|
||||
c2gtZWQyNTUxOSBqNjdGWFEgaUsxSzNGS09nMGo0eXlsUVdDL2R5UjVXYm9PZ3R1
|
||||
R0cra1JWbldnREJ3UQozeVYveGNUTExDUEJjT254NGlzTGxLSkl2akpqRnVmWU0y
|
||||
Z09oZFQ0YnFzCi0+IHNzaC1lZDI1NTE5IGMwVE5hUSBIQlZSU3VOYythUHo2NERV
|
||||
cE1rbDNlazMzZk1CMlJaM295K09POUZUc1dJCkZ1Ui9DZ2JsUm9FWithQmMwcHpm
|
||||
SkNTcEtpSWVJdjJoZG1KY29hSEIvdDQKLT4gc3NoLWVkMjU1MTkgbjhDcFV3ICtP
|
||||
dUUwMGx4WEtkQ21ySEFMREVXMHJaSitPalkxdXpPZTJJczZsUEI1MWMKVzRaOE10
|
||||
MEhjSHFVZW1RKzVDNnBYV2EyQXFTc2ozcVZlb0g4Z20zV3hROAotPiBzc2gtZWQy
|
||||
NTUxOSBWN2xnR3cgSmY1VEE1TjVROTU2U3lvMFNuaVhKNXlrNm1GV3NPSGtIZzhJ
|
||||
YkVNV1hXSQpZVzhlMnd4OTViN1ZmZlI2TkRMZ29sSU8rY0ZxcUhxY1U3UlM5MHRi
|
||||
N0VBCi0+IHNzaC1lZDI1NTE5IGpJOFJBZyAybmhuVGIzd1V3cCtoS2UxdVJ5S1p6
|
||||
UmowTEpvSDA2Mjl3Q0dBeENhUXlJCmFCWkZ5VVBKUnRtTGlvcUtMblJWNlVPTHRa
|
||||
RWdJY3kyL2dyUTV4Y09CWTgKLT4gc3NoLWVkMjU1MTkgVCtzYkdBIFE0R1hTaVAr
|
||||
aVBObnNVdkx2YVJ4TTJKYk9QUVhEbXlFQW1ESXVmWTBSbDAKVkJwRlZNVlBwNVJx
|
||||
WE9vVjl2OXFQcnZUTCtSS0NVQ1dFUmJXaDlhcVYwSQotPiBzc2gtZWQyNTUxOSBo
|
||||
TWE0bncgc0o4b2VsUDZsRDFlaFlJWEpkNU5jRERnWGJ0blJMbXhkR3RWQ1gxNkpB
|
||||
dwp4YU1WdGpJNUtQc09lRDdFVXNZdThWUEVnZDFvdVhUaW1JZUdQaGlId0NRCi0+
|
||||
IHNzaC1lZDI1NTE5IGV5cTNkZyBJTzdrSFdVTERDVk5hNHZsL1N6U010Y1ltZkpY
|
||||
VS9vUXl6N1FDV3pvWUhNCm1UUHZNTUlrMjdybkVEaWV1NmVlY2hFeklJZndFQUpP
|
||||
VGRNbzZ2SXVPVncKLT4gc3NoLWVkMjU1MTkgN1dROVBBIDdzKzBGUGNXZTYramx2
|
||||
VnAzYlcvOTZ6MTRtSGZTRFY0SFluQWMwTVdsMncKTG83dWdTbldMbnRBMVkwSnBu
|
||||
QkxDTUhhUERXeUl2UXVEaldvRjZtQXZsbwotPiBzc2gtZWQyNTUxOSBnU3hQMFEg
|
||||
SnBrUFhPa3dmMC84V1E0VVlyUC9VME1HWThPaDNxKzZLZC9Ybnd5aHdsYwpxQk1m
|
||||
SW9TY2NOSExZeVJGY3NUbVcvMm5OUytjUnhJR1ZFV1NZUDRqdnRrCi0+IHNzaC1l
|
||||
ZDI1NTE5IFZGY3c1ZyBac29qNmN5aGhpNDVVRHNsc1NIYzViZUdZK2tnSzFTc3pr
|
||||
SWxFcXFONzNrCmxRK1haWkJ6aXU3amNPZ2hlMnovUDllTGMvSGZiTnNJWjhZN2k3
|
||||
VnFmMzgKLT4gc3NoLWVkMjU1MTkgaGtidHZnIG41VkQyQW9rcmY2N2E2Qmc2bUds
|
||||
bWpUSWsxVHMvS2ZGUXhvSjNnanA5d0EKRE1IQ3UzNWE4VFdaQXZGakNscEE3RkQ3
|
||||
V2FESTdIWGRFOWV2QmpuaWZOYwotPiBzc2gtZWQyNTUxOSBldDJ6cFEgM1NwL3Jt
|
||||
WDBHQmErN2JzNUVTelVqb1dRMzlha3NYVEVvRGRrMW9jN0ZoWQpkZ3N1TUExRVhz
|
||||
aEE1QXFEbytySkdlcmpyV0JvRzFpRUZJc0VlenhBNDg0Ci0+IHNzaC1lZDI1NTE5
|
||||
IENrT1RXUSBCSU8xbTBNaXRqK0kzZVZOUGo3ZmZYd09sMTd6UHJvU2t5SUJBams1
|
||||
R0FjCjEzSEN0ZXd2NmI1M2xvWG1CRTNtcUZZZHhjOVpqemNXS3ByQWtmSy9MdmcK
|
||||
LT4gc3NoLWVkMjU1MTkgWmJMSldBIEx6TUdIT1I4VEIzbXFBdWh0eFZKSnN5R2pZ
|
||||
VjhYejVtczZSTko5Ty93M2cKNUlBUHFKd0JwNFFHYk9pcnpTcVYvWmZrL3BIWDUx
|
||||
cW9vOEpkM1J1emorSQotPiBzc2gtZWQyNTUxOSBaTnFJb2cgY1VUU3BJeFBRYkN4
|
||||
aWFsWEVWL2NHenYxODcvcnJHZXhRbERFU3YzQlZ4UQo4Z000SGJia01MdHpQU1kw
|
||||
VjRLWXhGczJNWWNMdVhWV09TUXB2UE1PejZZCi0+IHNzaC1lZDI1NTE5IHFManF5
|
||||
USAzOWRKUTZDVTVpVlFuWGgrdlNYN2RBQXArbFNoN1k4OWxQR0VIZHRUWXlvCi9Y
|
||||
blkrellUVVROYUMyWHdBK1NHdjQ5YWs2blpvbS9JZVkybUVPbFRxb2sKLT4gc3No
|
||||
LWVkMjU1MTkgQmFRbFJnIDhoR0lXTlNLQ3Z2WTZXQUFlQ25odmJPeFI1TWIyWUlv
|
||||
SG02SjFYR29HQ1EKKzlqaTdMWmpwVCtQWDFDZlk3aXQ0L2t3YkZudHAzSC9WK2Vr
|
||||
L3RXbW44SQotPiBzc2gtZWQyNTUxOSBzK3FSZmcgYzFia3NxQWJvOFFqa3g2ZkFL
|
||||
YVlXOTRzdTFUZWIvV1piM2RDSDh0Z21nUQptQ1dSSC83Wnd4cnJQcDNPRlhtV24v
|
||||
THZ6bUd6Q25SU0Q4b3R6Y0d3dkQ0Ci0+IHNzaC1lZDI1NTE5IDYySmNjQSBCcFhE
|
||||
SUVnRzZCQitpNjd1S0h4VmxWSzBDMlkrbURLVlZ5ZzIzWE1TUlZrClF0bVh3UU5k
|
||||
TFRvWlc2Z2pXMzhiY3ZyN2g3akhQa25zY3NhSEhWV05DUFEKLT4gc3NoLWVkMjU1
|
||||
MTkgL2h4L2RBIHc0UWp1WEdNWnJsaUpVTmU5Q2tITGNNYkRFcEllUENTMDgyOCtG
|
||||
NCtIM1kKVmV2MjB1WlAva0xKMDdrWE43NUV1YXNOc0FTV0NNbnZuaXpVTWhvc2ZL
|
||||
bwotPiBzc2gtZWQyNTUxOSBXekxHSEEgdmJRZU5SVCsxelMxOXIwZ3FLeEhlYURX
|
||||
cmptKzQvZkZUZVJzM1h0UENYbwpTempycGIrU3J0b0FvaTJOek5VZ2RzeTA5NGRC
|
||||
N1JYY3hMYVVLM1diZE44Ci0+IHNzaC1lZDI1NTE5IEhKL0o3QSBSRkZyTXJ1djhJ
|
||||
aWt4Mk9iaitSSTdtWk9Eeklvb1VLU2oxVmQ1L0NDYUZzCkdIcmJBQ0RqbFlDdHh4
|
||||
a0o3Ujd2ZEZPbFJKOFozS3UxR3lldERYaVY5S0EKLT4gc3NoLWVkMjU1MTkgT0Vx
|
||||
TXNnIHgwbTFxV3ZrR0h2SHBocEluZTZmSnBmUStKMkNtRVhTZTJNeVM3akZjVUEK
|
||||
V3FKZGlxMmw3QzB3VnAxUXBnK2RzdUNUU3MwSHVIcXg3UHFVSyt1QklhSQotPiBz
|
||||
c2gtZWQyNTUxOSAvRUpYdmcgSVdJOVdaNW9PdVY5TE9iNjFlY0ZQN001dnNENWR6
|
||||
cU5DSTVpcEMvRHpSUQo5SnZmMmRCdldKQ2VCSC8zaG1yTVMra2p3QkZHNVF3WmNR
|
||||
a2VMNlJpRXNJCi0+IFgyNTUxOSBYU3VVMkttYUc5NVpBU0FSZk5MNXIvRVIxd0w5
|
||||
RHZxcmdLb2dmaVlTWGxZCm9lNUloSGJxcmYvcFVkc3dDNXNmUkZNbXB0elpwMVBB
|
||||
dWdpSTJZQlgwUk0KLT4gPipeLWdyZWFzZSA8ZCBNcmxSP1BrCjZpcVoxcFNmV2Zi
|
||||
K0pxeHlKSmJ0ZWNIdm5mYk1Wano5VHZBb2wxTExxT0dKajFNMnZGU1JYODhXT3Jy
|
||||
MElGNG4KUEh2V0MvZkg5UmN3SVJEajQ3NVhXUk5YWFZPVnFyZkpsRjhnCi0tLSBS
|
||||
Ulh4WkhVY3BuYXFicjhoN29DbFJwemlrczFQcUxpMTNaRXFLOWc2YTJFCvATVcNx
|
||||
h0TZBbnm6QBWZVNDRiU8yHFAgaS/25pRvcaixnji3NkeKYYuEEnVSw6oUthhVSSg
|
||||
g222QeHfXortX7m+/zTD0uIhdVm7e+emA8LBxsQEOgeDy33XA3Hi9yX2BFGV7l82
|
||||
NTBlLbCiH0mlFZ6ZO8rtA/nMcriCQUb2QZ+TaSGAop4RHObEeFw=
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
|
||||
Reference in New Issue
Block a user