Compare commits
18 Commits
c354998e11
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 135d52d3de | |||
| f8a89b9c87 | |||
| 5e3b196ee0 | |||
| b8f31e23f8 | |||
| 7fe3c8186c | |||
| 1ca978baf4 | |||
| b9bcb1eaeb | |||
| f9f61e19ad | |||
| 14af217880 | |||
| 26e6870337 | |||
| 000c03d3c1 | |||
| 4f6ea35ee4 | |||
| de681f33da | |||
| ab2308e765 | |||
| 539a76a94f | |||
| 88d0d19239 | |||
| 8f9ca5e1c4 | |||
| 08605ab422 |
@@ -0,0 +1,25 @@
|
||||
---
|
||||
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).
|
||||
@@ -1,2 +1,4 @@
|
||||
watch_file devshell/{default,commands,install,vm-tasks}.nix
|
||||
use flake
|
||||
# --accept-flake-config trusts the flake's nixConfig (our Harmonia cache) non-interactively, so
|
||||
# direnv doesn't stall on the trust prompt.
|
||||
use flake . --accept-flake-config
|
||||
|
||||
@@ -10,17 +10,22 @@ jobs:
|
||||
runs-on: ubuntu-26.04
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: cachix/install-nix-action@v31
|
||||
- uses: DeterminateSystems/determinate-nix-action@v3
|
||||
with:
|
||||
# Gitea will supply a token in GITHUB_TOKEN, which this action will
|
||||
# try to pass to Nix when downloading from GitHub
|
||||
github_access_token: ${{ secrets.GH_PULL_TOKEN }}
|
||||
extra_nix_config: |
|
||||
# Gitea will supply a token in GITHUB_TOKEN, which this action passes to
|
||||
# Nix (as access-tokens) when downloading from GitHub
|
||||
github-token: ${{ secrets.GH_PULL_TOKEN }}
|
||||
extra-conf: |
|
||||
# Make sure we're using sandbox
|
||||
sandbox-fallback = false
|
||||
# Big C++ projects fill up memory...
|
||||
cores = 6
|
||||
|
||||
# Determinate performance features
|
||||
lazy-trees = true
|
||||
eval-cores = 0
|
||||
|
||||
accept-flake-config = true
|
||||
extra-substituters = https://nix-cache.nul.ie
|
||||
extra-trusted-public-keys = nix-cache.nul.ie-1:BzH5yMfF4HbzY1C977XzOxoPhEc9Zbu39ftPkUbH+m4=
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ on:
|
||||
|
||||
jobs:
|
||||
update:
|
||||
if: "!contains(github.event.head_commit.message, 'docs: update generated tables')"
|
||||
if: "!contains(github.event.head_commit.message, 'docs: Update generated references')"
|
||||
runs-on: ubuntu-26.04
|
||||
permissions:
|
||||
contents: write
|
||||
@@ -26,6 +26,16 @@ jobs:
|
||||
- name: Update option reference
|
||||
run: nix run .#update-docs-options
|
||||
|
||||
- name: Update DNS reference
|
||||
run: >
|
||||
nix run .#update-docs-dns --
|
||||
ams1.int.nul.ie
|
||||
100.10.in-addr.arpa
|
||||
2.d.4.0.0.c.7.9.e.0.a.2.ip6.arpa
|
||||
h.nul.ie
|
||||
168.192.in-addr.arpa
|
||||
0.d.4.0.0.c.7.9.e.0.a.2.ip6.arpa
|
||||
|
||||
- name: Commit and push if changed
|
||||
env:
|
||||
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
|
||||
@@ -36,6 +46,6 @@ jobs:
|
||||
git remote set-url origin "${REPO_URL/https:\/\//https:\/\/oauth2:${GITEA_TOKEN}@}"
|
||||
git add docs/
|
||||
if ! git diff --cached --quiet; then
|
||||
git commit -m "docs: update generated tables"
|
||||
git commit -m "docs: Update generated references"
|
||||
git push
|
||||
fi
|
||||
|
||||
@@ -46,7 +46,10 @@ Common ones:
|
||||
Pass the flake-qualified node, e.g. `deploy .#git`. The deploy node name is **always** the system
|
||||
name (`deploy-rs.nix` keys nodes directly off `nixos.systems` / `home-manager.homes`); a system is
|
||||
only a deploy target when `config.my.deploy.enable` is true (defaults true; auto-disabled for dev
|
||||
VMs and containers). Pass `--boot` to stage a config as the boot default **without** live-switching
|
||||
VMs and containers). A container is **not** its own deploy node — it is generated as a
|
||||
`container-<name>` profile on its **host** node. So `deploy .#<host>` deploys the host's `system`
|
||||
profile and every one of its containers, whereas `deploy .#<host>.container-<name>` targets a
|
||||
single container (e.g. `deploy .#shill.container-middleman`). Pass `--boot` to stage a config as the boot default **without** live-switching
|
||||
(`deploy --boot .#<host>`) — the box keeps running its current generation until it reboots. Use this
|
||||
when a live `switch` would break connectivity mid-change (e.g. a router's WAN VLAN rework), then
|
||||
reboot to cut over.
|
||||
@@ -58,7 +61,9 @@ 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 .#`.
|
||||
- `update-nixpkgs` / `update-home-manager` — bump pinned inputs.
|
||||
- `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).
|
||||
|
||||
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
|
||||
@@ -66,8 +71,8 @@ Use the narrowest relevant evaluation while iterating: `check-system <host>` for
|
||||
`nix flake check --no-build` for final broad validation or reproducing CI.
|
||||
CI builds each attr of `.#ci.x86_64-linux` (systems, homes, packages, shell) and pushes to the
|
||||
Harmonia binary cache; see `.gitea/workflows/ci.yaml` and `ci/push-to-cache.sh`. A separate
|
||||
workflow (`.gitea/workflows/update-docs.yaml`) regenerates the network-assignment tables and NixOS
|
||||
option reference via `nix run .#update-docs-{assignments,options}`.
|
||||
workflow (`.gitea/workflows/update-docs.yaml`) regenerates the network assignments, NixOS option
|
||||
reference and live DNS reference under `docs/`.
|
||||
|
||||
For DNS lookups use **`drill`** (ldns) — `dig` isn't installed in this environment (it fails with
|
||||
exit 127, which is easy to miss if stderr is redirected). E.g. `drill -Q @<resolver> <name> A`.
|
||||
@@ -212,10 +217,12 @@ physical box share a name, the site keeps the `README.md` and the box page stays
|
||||
example `sites/colony/README.md` and `sites/colony/colony.md`).
|
||||
|
||||
**Box page layout** (match the existing pages): H1 + a one-line intro; a short bullet list of
|
||||
`Source` / `Host` / `nixpkgs`; `## Role`; `## Network assignments` that **links** to
|
||||
`Source` / `Host` / `nixpkgs`; an optional hardware inventory or VPS resource-allocation section;
|
||||
`## Role`; `## Network assignments` that **links** to
|
||||
[`networking.md#box-assignments`](docs/networking.md#box-assignments) (never inline the table); one
|
||||
`##` section per topic; `## Notable config files` last. A box without static assignments still gets
|
||||
the section with a short explanation instead of a generated-table link.
|
||||
`##` section per topic; `## Notable config files` last. Keep non-hardware platform details in their
|
||||
topical sections rather than moving them with the inventory. A box without static assignments still
|
||||
gets the section with a short explanation instead of a generated-table link.
|
||||
|
||||
**Structure and layout:**
|
||||
- Use **tables** for lists of structured items (BGP peers, forwarded ports, vhosts, containers,
|
||||
@@ -247,10 +254,12 @@ the section with a short explanation instead of a generated-table link.
|
||||
Addresses outside that data model (such as external peers or service endpoints) stay with the
|
||||
topic that owns them.
|
||||
|
||||
**Generated content:** the network-assignment tables in `networking.md` and the option reference
|
||||
(`docs/reference/nixos-options.md`) are CI-generated (`nix run .#update-docs-{assignments,options}`)
|
||||
— don't hand-edit between the `<!-- ... -->` markers; write the prose and let the updater refresh
|
||||
the tables.
|
||||
**Generated content:** the network-assignment tables in `networking.md`, the option reference
|
||||
(`docs/reference/nixos-options.md`) and the live DNS tables (`docs/reference/dns.md`) are
|
||||
CI-generated by the corresponding `update-docs-*` packages; the workflow supplies the DNS zones.
|
||||
Don't hand-edit content between `<!-- ... -->` markers; write the surrounding prose or source
|
||||
configuration and let the updater refresh the tables. The option-reference file is generated in
|
||||
full.
|
||||
|
||||
**Keep docs current:** when you add, remove or repurpose a box or service, update its box page, the
|
||||
relevant site-index `README.md`, and any affected prose in `networking.md` (the assignment/option
|
||||
|
||||
@@ -0,0 +1,362 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Render docs/reference/dns.md from live authoritative DNS zone transfers.
|
||||
|
||||
The authoritative servers are queried directly over AXFR. Records owned by Kea are
|
||||
identified by DHCID records and omitted together with their forward and reverse data.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import ipaddress
|
||||
import re
|
||||
import socket
|
||||
import sys
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
|
||||
import dns.exception
|
||||
import dns.name
|
||||
import dns.query
|
||||
import dns.resolver
|
||||
import dns.rdatatype
|
||||
|
||||
|
||||
OUT = Path("docs/reference/dns.md")
|
||||
DEFAULT_PORT = 53
|
||||
IGNORED_TYPES = {"DHCID", "SOA"}
|
||||
POWERDNS_ALIAS = 65401
|
||||
POWERDNS_LUA = 65402
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Record:
|
||||
zone: str
|
||||
owner: str
|
||||
type: str
|
||||
value: str
|
||||
|
||||
|
||||
def normalize_name(name: str) -> str:
|
||||
return name.rstrip(".").lower()
|
||||
|
||||
|
||||
def record_data(rdtype: int, rdata) -> tuple[str, str]:
|
||||
if rdtype == POWERDNS_ALIAS:
|
||||
target, _used = dns.name.from_wire(rdata.data, 0)
|
||||
return "ALIAS", target.to_text()
|
||||
if rdtype == POWERDNS_LUA:
|
||||
logical_type = int.from_bytes(rdata.data[:2], byteorder="big")
|
||||
return "LUA", dns.rdatatype.to_text(logical_type)
|
||||
return dns.rdatatype.to_text(rdtype), rdata.to_text()
|
||||
|
||||
|
||||
def server_addresses(server: str, port: int) -> list[str]:
|
||||
addresses = []
|
||||
try:
|
||||
for result in socket.getaddrinfo(server, port, type=socket.SOCK_STREAM):
|
||||
address = result[4][0]
|
||||
if address not in addresses:
|
||||
addresses.append(address)
|
||||
except socket.gaierror as error:
|
||||
raise RuntimeError(f"cannot resolve nameserver {server}: {error}") from error
|
||||
return addresses
|
||||
|
||||
|
||||
def transfer(server: str, port: int, zone: str) -> list[Record]:
|
||||
addresses = server_addresses(server, port)
|
||||
|
||||
errors = []
|
||||
for address in addresses:
|
||||
try:
|
||||
records = []
|
||||
for message in dns.query.xfr(
|
||||
address, zone, port=port, lifetime=60, relativize=False
|
||||
):
|
||||
for rrset in message.answer:
|
||||
for rdata in rrset:
|
||||
record_type, value = record_data(rrset.rdtype, rdata)
|
||||
records.append(
|
||||
Record(
|
||||
zone=normalize_name(zone),
|
||||
owner=normalize_name(rrset.name.to_text()),
|
||||
type=record_type,
|
||||
value=value,
|
||||
)
|
||||
)
|
||||
if not any(record.type == "SOA" for record in records):
|
||||
raise RuntimeError("transfer returned no SOA record")
|
||||
return records
|
||||
except (dns.exception.DNSException, OSError, RuntimeError) as error:
|
||||
errors.append(f"{address}: {error}")
|
||||
raise RuntimeError(f"AXFR of {zone} from {server} failed ({'; '.join(errors)})")
|
||||
|
||||
|
||||
def system_nameservers(domain: str) -> list[str]:
|
||||
try:
|
||||
answer = dns.resolver.resolve(domain, "NS", lifetime=30)
|
||||
except dns.exception.DNSException as error:
|
||||
message = f"cannot discover authoritative servers for {domain}: {error}"
|
||||
raise RuntimeError(message) from error
|
||||
return [rdata.target.to_text() for rdata in answer]
|
||||
|
||||
|
||||
def nameservers_via(server: str, port: int, domain: str) -> list[str]:
|
||||
errors = []
|
||||
for address in server_addresses(server, port):
|
||||
resolver = dns.resolver.Resolver(configure=False)
|
||||
resolver.nameservers = [address]
|
||||
resolver.port = port
|
||||
try:
|
||||
answer = resolver.resolve(domain, "NS", lifetime=15, search=False)
|
||||
return [rdata.target.to_text() for rdata in answer]
|
||||
except dns.exception.DNSException as error:
|
||||
errors.append(f"{address}: {error}")
|
||||
raise RuntimeError(f"NS query for {domain} via {server} failed ({'; '.join(errors)})")
|
||||
|
||||
|
||||
def discover_nameservers(domains: list[str], port: int) -> dict[str, list[str]]:
|
||||
discovered = {}
|
||||
unresolved = {}
|
||||
candidates = []
|
||||
for domain in domains:
|
||||
try:
|
||||
servers = system_nameservers(domain)
|
||||
discovered[domain] = servers
|
||||
for server in servers:
|
||||
if server not in candidates:
|
||||
candidates.append(server)
|
||||
except RuntimeError as error:
|
||||
unresolved[domain] = [str(error)]
|
||||
|
||||
for domain, errors in list(unresolved.items()):
|
||||
for server in candidates:
|
||||
try:
|
||||
discovered[domain] = nameservers_via(server, port, domain)
|
||||
del unresolved[domain]
|
||||
break
|
||||
except RuntimeError as error:
|
||||
errors.append(str(error))
|
||||
|
||||
if unresolved:
|
||||
details = "; ".join(
|
||||
f"{domain}: {'; '.join(errors)}" for domain, errors in unresolved.items()
|
||||
)
|
||||
raise RuntimeError(details)
|
||||
return discovered
|
||||
|
||||
|
||||
def transfer_domain(
|
||||
port: int, domain: str, servers: list[str], fallback: list[str] = ()
|
||||
) -> list[Record]:
|
||||
# A zone may be delegated publicly to servers that refuse AXFR (e.g. HE serving
|
||||
# reverse DNS) while our own authoritative servers, discovered for other zones,
|
||||
# will transfer it. Try the delegated servers first, then fall back to those.
|
||||
ordered = list(servers)
|
||||
for server in fallback:
|
||||
if server not in ordered:
|
||||
ordered.append(server)
|
||||
|
||||
errors = []
|
||||
for server in ordered:
|
||||
try:
|
||||
return transfer(server, port, domain)
|
||||
except RuntimeError as error:
|
||||
errors.append(str(error))
|
||||
raise RuntimeError(f"no authoritative server allowed AXFR for {domain} ({'; '.join(errors)})")
|
||||
|
||||
|
||||
def dynamic_names(records: list[Record]) -> set[str]:
|
||||
return {record.owner for record in records if record.type == "DHCID"}
|
||||
|
||||
|
||||
def dynamic_addresses(records: list[Record], names: set[str]) -> set[str]:
|
||||
return {
|
||||
record.value.rstrip(".").lower()
|
||||
for record in records
|
||||
if record.owner in names and record.type in {"A", "AAAA"}
|
||||
}
|
||||
|
||||
|
||||
def reverse_address(owner: str) -> str | None:
|
||||
if owner.endswith(".in-addr.arpa"):
|
||||
labels = owner.removesuffix(".in-addr.arpa").split(".")
|
||||
if len(labels) != 4:
|
||||
return None
|
||||
try:
|
||||
return str(ipaddress.IPv4Address(".".join(reversed(labels))))
|
||||
except ValueError:
|
||||
return None
|
||||
|
||||
if owner.endswith(".ip6.arpa"):
|
||||
labels = owner.removesuffix(".ip6.arpa").split(".")
|
||||
if len(labels) != 32:
|
||||
return None
|
||||
try:
|
||||
value = int("".join(reversed(labels)), 16)
|
||||
return str(ipaddress.IPv6Address(value))
|
||||
except ValueError:
|
||||
return None
|
||||
return None
|
||||
|
||||
|
||||
def static_records(records: list[Record]) -> list[Record]:
|
||||
names = dynamic_names(records)
|
||||
addresses = dynamic_addresses(records, names)
|
||||
static = []
|
||||
for record in records:
|
||||
if record.type in IGNORED_TYPES or record.owner in names:
|
||||
continue
|
||||
if record.type == "PTR":
|
||||
target = normalize_name(record.value.split()[0])
|
||||
address = reverse_address(record.owner)
|
||||
if target in names or address in addresses:
|
||||
continue
|
||||
static.append(record)
|
||||
return static
|
||||
|
||||
|
||||
def relative_name(owner: str, zone: str) -> str:
|
||||
if owner == zone:
|
||||
return "@"
|
||||
suffix = f".{zone}"
|
||||
return owner[: -len(suffix)] if owner.endswith(suffix) else owner
|
||||
|
||||
|
||||
def markdown_code(value: str) -> str:
|
||||
escaped = value.replace("|", "\\|")
|
||||
return f"`{escaped}`"
|
||||
|
||||
|
||||
def display_record(record: Record) -> tuple[str, str]:
|
||||
if record.type != "LUA":
|
||||
return record.type, record.value
|
||||
return f"{record.value} (LUA)", "generated at query time"
|
||||
|
||||
|
||||
def render_forward(domain: str, records: list[Record]) -> list[str]:
|
||||
domain = normalize_name(domain)
|
||||
rows = []
|
||||
for record in records:
|
||||
if record.zone != domain or record.type == "PTR":
|
||||
continue
|
||||
record_type, value = display_record(record)
|
||||
rows.append((relative_name(record.owner, record.zone), record_type, value))
|
||||
rows.sort(key=lambda row: (row[0] != "@", row[0], row[1], row[2]))
|
||||
|
||||
lines = ["| Name | Type | Value |", "|---|---|---|"]
|
||||
lines.extend(
|
||||
f"| {markdown_code(name)} | {markdown_code(record_type)} | {markdown_code(value)} |"
|
||||
for name, record_type, value in rows
|
||||
)
|
||||
return lines
|
||||
|
||||
|
||||
def render_reverse(domain: str, records: list[Record]) -> list[str]:
|
||||
domain = normalize_name(domain)
|
||||
rows = []
|
||||
for record in records:
|
||||
if record.zone != domain or record.type != "PTR":
|
||||
continue
|
||||
rows.append((reverse_address(record.owner) or record.owner, record.value))
|
||||
rows.sort(key=lambda row: ipaddress.ip_address(row[0]))
|
||||
|
||||
lines = ["| Address | Name |", "|---|---|"]
|
||||
lines.extend(
|
||||
f"| {markdown_code(address)} | {markdown_code(name)} |" for address, name in rows
|
||||
)
|
||||
return lines
|
||||
|
||||
|
||||
def is_reverse(domain: str) -> bool:
|
||||
domain = normalize_name(domain)
|
||||
return domain.endswith(".in-addr.arpa") or domain.endswith(".ip6.arpa")
|
||||
|
||||
|
||||
def rendered_zones(transferred: list[tuple[str, list[Record]]]) -> dict[str, list[str]]:
|
||||
records = static_records([record for _domain, zone in transferred for record in zone])
|
||||
rendered = {}
|
||||
for domain, _raw_records in transferred:
|
||||
if is_reverse(domain):
|
||||
rendered[normalize_name(domain)] = render_reverse(domain, records)
|
||||
else:
|
||||
rendered[normalize_name(domain)] = render_forward(domain, records)
|
||||
return rendered
|
||||
|
||||
|
||||
def update_target(transferred: list[tuple[str, list[Record]]], target: Path) -> bool:
|
||||
rendered = rendered_zones(transferred)
|
||||
text = target.read_text()
|
||||
lines = text.splitlines()
|
||||
marker_re = re.compile(r"^<!--\s*dns:\s*(\S+)\s*-->$")
|
||||
found = set()
|
||||
output = []
|
||||
i = 0
|
||||
while i < len(lines):
|
||||
match = marker_re.match(lines[i].strip())
|
||||
if not match or normalize_name(match.group(1)) not in rendered:
|
||||
output.append(lines[i])
|
||||
i += 1
|
||||
continue
|
||||
|
||||
domain = normalize_name(match.group(1))
|
||||
end = i + 1
|
||||
while end < len(lines) and lines[end].strip() != "<!-- dns-end -->":
|
||||
end += 1
|
||||
if end >= len(lines):
|
||||
raise RuntimeError(f"missing <!-- dns-end --> for {domain}")
|
||||
|
||||
output.extend(
|
||||
[
|
||||
lines[i],
|
||||
"<!-- dns-start -->",
|
||||
*rendered[domain],
|
||||
"<!-- dns-end -->",
|
||||
]
|
||||
)
|
||||
found.add(domain)
|
||||
i = end + 1
|
||||
|
||||
missing = rendered.keys() - found
|
||||
if missing:
|
||||
raise RuntimeError(f"missing DNS markers for: {', '.join(sorted(missing))}")
|
||||
|
||||
new = "\n".join(output) + "\n"
|
||||
if new == text:
|
||||
return False
|
||||
target.write_text(new)
|
||||
return True
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(prog="update-docs-dns", description=__doc__)
|
||||
parser.add_argument("domain", nargs="+", help="DNS zone to transfer")
|
||||
parser.add_argument("--port", type=int, default=DEFAULT_PORT)
|
||||
parser.add_argument("--output", type=Path, default=OUT)
|
||||
args = parser.parse_args()
|
||||
|
||||
try:
|
||||
nameservers = discover_nameservers(args.domain, args.port)
|
||||
fallback = []
|
||||
for servers in nameservers.values():
|
||||
for server in servers:
|
||||
if server not in fallback:
|
||||
fallback.append(server)
|
||||
transferred = [
|
||||
(domain, transfer_domain(args.port, domain, nameservers[domain], fallback))
|
||||
for domain in args.domain
|
||||
]
|
||||
except RuntimeError as error:
|
||||
print(f"update-docs-dns: {error}", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
try:
|
||||
changed = update_target(transferred, args.output)
|
||||
except (OSError, RuntimeError) as error:
|
||||
print(f"update-docs-dns: {error}", file=sys.stderr)
|
||||
return 1
|
||||
if changed:
|
||||
print(f"updated {args.output}")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
@@ -12,6 +12,9 @@ in
|
||||
NIX_USER_CONF_FILES = toString (pkgs.writeText "nix.conf"
|
||||
''
|
||||
experimental-features = nix-command flakes ca-derivations
|
||||
lazy-trees = true
|
||||
eval-cores = 0
|
||||
accept-flake-config = true
|
||||
connect-timeout = 5
|
||||
fallback = true
|
||||
${lib.my.c.nix.cache.conf}
|
||||
@@ -22,7 +25,7 @@ in
|
||||
|
||||
packages = with pkgs; [
|
||||
coreutils
|
||||
nixVersions.stable
|
||||
determinate-nix
|
||||
rage
|
||||
wireguard-tools
|
||||
(pkgs.writeShellScriptBin "deploy" ''
|
||||
|
||||
@@ -29,7 +29,7 @@ let
|
||||
coreutils
|
||||
gnugrep
|
||||
openssh
|
||||
nixVersions.stable
|
||||
determinate-nix
|
||||
jq
|
||||
];
|
||||
text =
|
||||
|
||||
+4
-1
@@ -25,6 +25,9 @@ Not every box fits this pattern, but **colony** and **home** are organised this
|
||||
- [`networking.md`](networking.md) — network assignments, domains, site topologies, router HA,
|
||||
the AS211024 L2 mesh, BGP, WireGuard, Tailscale.
|
||||
- [`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).
|
||||
- [`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.
|
||||
|
||||
@@ -63,7 +66,7 @@ h.nul.ie
|
||||
├── palace (physical VM host — AMD, 100G, SR-IOV)
|
||||
│ ├── river ── primary router VM (PPPoE / Digiweb WAN)
|
||||
│ ├── cellar ─ NVMe-oF / SPDK storage target VM
|
||||
│ └── sfh ──── container host VM ("services for home")
|
||||
│ └── sfh ──── container host VM ("shill from home")
|
||||
│ ├── hass ── Home Assistant + Frigate + MQTT (container)
|
||||
│ └── unifi ─ UniFi controller (container)
|
||||
├── stream (physical secondary router — Virgin Media WAN)
|
||||
|
||||
+41
-7
@@ -110,6 +110,31 @@ VM's unix sockets from `/run/vms/<vm>/` on `<host>` over SSH):
|
||||
| `vm-monitor <host> <vm>` | QEMU monitor socket in `minicom`. |
|
||||
| `vm-viewer <host> <vm>` | SPICE display in `virt-viewer` (not on Darwin). |
|
||||
|
||||
## Nix implementation
|
||||
|
||||
Every context uses **Determinate Nix** as its `nix.package`, for its performance features
|
||||
(parallel evaluation and lazy trees) — not `determinate-nixd`; the daemon and `nix.conf` model
|
||||
are unchanged, and the Determinate NixOS module is deliberately not imported.
|
||||
|
||||
- **Input and package.** The [`determinate-nix`](../flake.nix) input is the `nix-src` flake
|
||||
(`flakehub.com/f/DeterminateSystems/nix-src`), with `nixpkgs.follows = "nixpkgs-unstable"`. We
|
||||
build it ourselves against our pinned nixpkgs — FlakeHub's own cache needs authentication, so
|
||||
there is nothing to gain from leaving it unpinned — and it then flows through the Harmonia cache
|
||||
like everything else. `determinateOverlay` exposes it under the stable attr `determinate-nix`,
|
||||
added to both the devshell `pkgs'` and the config `configPkgs'` overlay lists, so systems, homes
|
||||
and the devshell all resolve the same package (`pkgs'.mine.determinate-nix`).
|
||||
- **Settings.** `lib.my.c.nix.determinateSettings` (`lazy-trees`, `eval-cores = 0`) is merged into
|
||||
`nix.settings` for systems and homes and into the devshell's `nix.conf`. These keys are only
|
||||
understood by the Determinate binary.
|
||||
- **Consumers follow automatically.** Everything that shells out to Nix references
|
||||
`config.nix.package` (deploy-rs, containers, `build`, netboot, Harmonia), so they inherit
|
||||
Determinate without further change.
|
||||
- **`accept-flake-config`.** Set true only in the devshell `nix.conf`, `.envrc` (as
|
||||
`--accept-flake-config`, for direnv) and CI — the contexts that build this flake — so its
|
||||
`nixConfig` (the Harmonia cache) is trusted without an interactive prompt. It is deliberately not
|
||||
set system-wide: boxes already trust that cache via `nix.settings`, so a global setting would only
|
||||
blanket-trust every flake's `nixConfig` for no gain.
|
||||
|
||||
## Secrets
|
||||
|
||||
Secrets are age-encrypted files in [`secrets/`](../secrets), managed with **ragenix** (a fork
|
||||
@@ -152,8 +177,10 @@ GitHub/Gitea Actions workflows live in [`.gitea/workflows/`](../.gitea/workflows
|
||||
|
||||
### `ci.yaml`
|
||||
|
||||
On pushes to `master`, this 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
|
||||
On pushes to `master`, this installs Determinate Nix on the runner (via
|
||||
`DeterminateSystems/determinate-nix-action`, configured with the same performance settings and
|
||||
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).
|
||||
|
||||
@@ -170,14 +197,13 @@ Pushing the `installer` tag (refreshed by `update-installer`) builds `my.buildAs
|
||||
|
||||
### `update-docs.yaml`
|
||||
|
||||
On pushes to the docs branch, excluding its own commits, this runs
|
||||
`nix run .#update-docs-assignments` and `nix run .#update-docs-options` and commits changed outputs
|
||||
as `docs: update generated tables`.
|
||||
On pushes to `master`, excluding its own commits, this runs the assignment, option and DNS
|
||||
reference generators and commits changed outputs as `docs: Update generated references`.
|
||||
|
||||
### The docs generators
|
||||
|
||||
Both are registered in [`pkgs/default.nix`](../pkgs/default.nix) (`writeShellScriptBin`s wrapping
|
||||
Python scripts under [`ci/`](../ci)). They leave the worktree unchanged when their output is current;
|
||||
The generators are registered in [`pkgs/default.nix`](../pkgs/default.nix) as wrappers around
|
||||
Python scripts under [`ci/`](../ci). They leave the worktree unchanged when their output is current;
|
||||
the workflow stages `docs/` and uses `git diff --cached --quiet` to decide whether to commit.
|
||||
|
||||
`update-docs-assignments` ([`ci/update-docs-assignments.py`](../ci/update-docs-assignments.py))
|
||||
@@ -197,3 +223,11 @@ apply to every box, so defaults don't pick up a real host's values. The renderer
|
||||
[`docs/reference/nixos-options.md`](reference/nixos-options.md), one table per module file. The
|
||||
whole file is generated; edit the option descriptions in the modules, not the reference. The
|
||||
internal `asX` build-target options are marked `internal = true` so they're excluded.
|
||||
|
||||
`update-docs-dns` ([`ci/update-docs-dns.py`](../ci/update-docs-dns.py)) accepts forward and reverse
|
||||
zone names, discovers their authoritative nameservers through NS queries, and transfers each zone
|
||||
over AXFR. If a private reverse zone is not visible through the configured recursive resolver, it
|
||||
asks the authoritative servers discovered for the other requested zones. It updates only the
|
||||
matching `<!-- dns: <zone> -->` blocks in the [`DNS records`](reference/dns.md) reference; the page's
|
||||
headings and prose remain handwritten. Kea-managed owners are identified by `DHCID` records and
|
||||
removed together with their A, AAAA and PTR records; SOA records and TTLs are also omitted.
|
||||
|
||||
+11
-11
@@ -6,17 +6,6 @@ Portable workstation — a Framework Laptop 13 (Intel), running the full GUI env
|
||||
- **Host:** physical (laptop)
|
||||
- **nixpkgs:** `mine`
|
||||
|
||||
## Role
|
||||
|
||||
- Personal portable workstation: `my.gui.enable`, with Sway managed by home-manager.
|
||||
- Joins the tailnet through the headscale on [`britway`](../remote/britway.md) (fish abbr
|
||||
`tsup` = `doas tailscale up --login-server=https://hs.nul.ie --accept-routes`).
|
||||
|
||||
## Network assignments
|
||||
|
||||
`tower` has no static assignment; it uses DHCP through NetworkManager and reaches the other boxes
|
||||
over Tailscale.
|
||||
|
||||
## Hardware / platform
|
||||
|
||||
| Component | Inventory |
|
||||
@@ -32,6 +21,17 @@ The configuration enables Intel microcode updates, `kvm-intel`, `intel_iommu=on`
|
||||
`intel-media-driver` and the latest kernel (`lib.my.c.kernel.latest`). Thunderbolt security
|
||||
(`bolt`), the fingerprint reader (`fprintd`) and `tlp` power management are also enabled.
|
||||
|
||||
## Role
|
||||
|
||||
- Personal portable workstation: `my.gui.enable`, with Sway managed by home-manager.
|
||||
- Joins the tailnet through the headscale on [`britway`](../remote/britway.md) (fish abbr
|
||||
`tsup` = `doas tailscale up --login-server=https://hs.nul.ie --accept-routes`).
|
||||
|
||||
## Network assignments
|
||||
|
||||
`tower` has no static assignment; it uses DHCP through NetworkManager and reaches the other boxes
|
||||
over Tailscale.
|
||||
|
||||
## Storage
|
||||
|
||||
- Two LUKS-encrypted partitions, `persist` and `home` (both `allowDiscards`); `/nix` is a
|
||||
|
||||
+9
-1
@@ -321,13 +321,21 @@ family move together.
|
||||
serving disjoint pool halves. `radvd` advertises the v6 VIP as RDNSS (`untrusted` gets Cloudflare)
|
||||
and keepalived's `notify_master`/`notify_backup` hooks ensure that only the master sends RAs.
|
||||
|
||||
Statically-addressed boxes (the servers on `hi`) don't run DHCP, so they'd otherwise learn a
|
||||
resolver only from the v6 RA RDNSS — which vanishes when v6 is disabled, taking DNS with it. They
|
||||
instead anchor DNS on the VIPs via the shared `lib.my.c.home.vlanDns "<vlan>"` fragment, which sets
|
||||
`DNS` to `vips.<vlan>.{v4,v6}` and `Domains` to the advertised search list; the always-present
|
||||
static v4 VIP keeps resolution working even with v6 down.
|
||||
|
||||
#### DNS binding
|
||||
|
||||
`pdns-recursor` binds the VIPs directly; see
|
||||
[`routing-common/dns.nix`](../nixos/boxes/home/routing-common/dns.nix). The
|
||||
`net.ipv4.ip_nonlocal_bind` / `net.ipv6.ip_nonlocal_bind` settings let the backup listen before it
|
||||
owns the addresses, so failover does not depend on client resolver timeouts. The recursor forwards
|
||||
the site's zones to authoritative PowerDNS on `127.0.0.1:5353`.
|
||||
the site's zones to authoritative PowerDNS on `127.0.0.1:5353`. The generated
|
||||
[DNS reference](reference/dns.md) lists the live forward and reverse records; the authoritative
|
||||
servers allow its AXFRs from the shared internal prefixes and the colony site's egress address.
|
||||
|
||||
#### `wan-online.target`
|
||||
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
# Upgrading nixpkgs
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
Work the phases in order; skip one only if explicitly scoped to a subset.
|
||||
|
||||
## Setup facts
|
||||
|
||||
- **Fork checkout:** `~/documents/projects/nixpkgs` — remotes `origin` (`devplayer0/nixpkgs`) and
|
||||
`upstream` (`NixOS/nixpkgs`). Confirm the path exists; if not, ask.
|
||||
- **Fork branches:** `devplayer0` (tracks `nixos-unstable`) and `devplayer0-stable` (tracks the
|
||||
current NixOS stable). Each is a small stack of local patches rebased onto upstream.
|
||||
- **Flake pins** in `flake.nix` (`inputs`):
|
||||
- `nixpkgs-unstable.url = "nixpkgs/nixos-unstable"`
|
||||
- `nixpkgs-stable.url = "nixpkgs/nixos-<STABLE>"` (e.g. `nixos-26.05`)
|
||||
- `nixpkgs-mine.url = "github:devplayer0/nixpkgs/devplayer0"`
|
||||
- `nixpkgs-mine-stable.url = "github:devplayer0/nixpkgs/devplayer0-stable"`
|
||||
- `home-manager-unstable.url = "home-manager"`
|
||||
- `home-manager-stable.url = "home-manager/release-<STABLE>"`
|
||||
- **Devshell commands:** `update-nixpkgs` = `nix flake update nixpkgs-{unstable,stable,mine,mine-stable}`;
|
||||
`update-home-manager` = `nix flake update home-manager-{unstable,stable}`.
|
||||
- **Validation:** prefer `check-system <host>` and `nix flake check --no-build` over full builds.
|
||||
|
||||
## Phase 1 — Determine the current NixOS stable
|
||||
|
||||
Do this first: everything downstream (the fork's `devplayer0-stable` rebase target, the `flake.nix`
|
||||
stable pins) has to agree on one NixOS stable release, so establish it up front.
|
||||
|
||||
1. Find the latest NixOS stable release branch — check `git branch -r` on `upstream` for the newest
|
||||
`release-YY.NN`, or the NixOS release schedule.
|
||||
2. Compare it to `<STABLE>` in the `flake.nix` `nixpkgs-stable` / `home-manager-stable` URLs.
|
||||
3. **If they already match** (no new stable): note "stable is current" and carry `<STABLE>` into the
|
||||
later phases.
|
||||
4. ⏸ **If a newer stable has cut:** stop and report the coordinated change set before proceeding —
|
||||
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.
|
||||
- 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.
|
||||
|
||||
## Phase 2 — Rebase the nixpkgs fork
|
||||
|
||||
For **both** branches — `devplayer0` onto `upstream/nixos-unstable`, and `devplayer0-stable` onto
|
||||
`upstream/release-<STABLE>` (the release established in Phase 1):
|
||||
|
||||
1. In `~/documents/projects/nixpkgs`, confirm a clean working tree (`git status`). If dirty, stop
|
||||
and report — don't stash silently.
|
||||
2. `git fetch upstream --prune` and `git fetch origin --prune`. If the checkout has been idle a
|
||||
long time this fetch can be large and slow; let it finish.
|
||||
3. Enumerate the patch stack before rebasing:
|
||||
`git log --oneline upstream/nixos-unstable..origin/devplayer0` (and the stable equivalent
|
||||
against `upstream/release-<STABLE>`). For each commit, check whether it has landed upstream or
|
||||
been superseded — e.g. `git log --oneline upstream/nixos-unstable -- <path>` or grep the
|
||||
upstream tree for the package/option. Note any patch that now looks redundant.
|
||||
4. Rebase: `git switch devplayer0 && git rebase upstream/nixos-unstable` (and the stable branch
|
||||
onto `upstream/release-<STABLE>`).
|
||||
- ⏸ **Conflicts:** stop. Report which patch conflicts and against what upstream change; let the
|
||||
maintainer resolve, or drop the patch if it has been upstreamed.
|
||||
- Clean rebase: continue.
|
||||
5. Summarize: which patches still apply, which are now redundant (candidate to drop), which
|
||||
conflicted.
|
||||
6. ⏸ **Push:** only after confirmation. `git push --force-with-lease origin devplayer0
|
||||
devplayer0-stable` (force needed — rebase rewrites history).
|
||||
|
||||
## Phase 3 — Update the pinned inputs
|
||||
|
||||
Run together (they move as a set):
|
||||
|
||||
```
|
||||
update-nixpkgs
|
||||
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
|
||||
|
||||
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:
|
||||
|
||||
```
|
||||
grep -rn "versionAtLeast\|versionOlder\|when 2[0-9]\.[0-9][0-9]\|TODO.*2[0-9]\.[0-9][0-9]" \
|
||||
--include=*.nix nixos home-manager lib pkgs flake.nix
|
||||
```
|
||||
|
||||
Known example: `nixos/modules/common.nix` carries a `# TODO: Remove if-else when 26.11 releases`
|
||||
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
|
||||
|
||||
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`, …),
|
||||
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
|
||||
|
||||
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
|
||||
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.
|
||||
@@ -0,0 +1,319 @@
|
||||
# DNS records
|
||||
|
||||
The tables on this page are generated from live authoritative zone transfers by
|
||||
`nix run .#update-docs-dns -- <zones...>`; CI keeps them current. The Nix DNS configuration is
|
||||
the source of truth. Edit the prose and headings here, but not content between the DNS markers.
|
||||
|
||||
DHCP-managed records are excluded. The generator identifies them by `DHCID` and removes the
|
||||
corresponding forward and reverse records. SOA records and TTLs are also omitted because they are
|
||||
operational metadata rather than useful inventory.
|
||||
|
||||
## Colony
|
||||
|
||||
These zones are served by [`estuary`](../sites/colony/estuary.md); their source configuration is
|
||||
[`estuary/dns.nix`](../../nixos/boxes/colony/vms/estuary/dns.nix).
|
||||
|
||||
### Forward zone: `ams1.int.nul.ie`
|
||||
|
||||
<!-- dns: ams1.int.nul.ie -->
|
||||
<!-- dns-start -->
|
||||
| Name | Type | Value |
|
||||
|---|---|---|
|
||||
| `@` | `ALIAS` | `estuary-vm.ams1.int.nul.ie.` |
|
||||
| `@` | `NS` | `ns.ams1.int.nul.ie.` |
|
||||
| `_acme-challenge` | `TXT (LUA)` | `generated at query time` |
|
||||
| `andrey-cust` | `A` | `94.142.242.254` |
|
||||
| `chatterbox-ctr` | `A` | `10.100.2.5` |
|
||||
| `chatterbox-ctr` | `AAAA` | `2a0e:97c0:4d2:12::5` |
|
||||
| `colony` | `A` | `94.142.241.224` |
|
||||
| `colony` | `AAAA` | `2a0e:97c0:4d2:10::2` |
|
||||
| `colony-psql` | `CNAME` | `colony-psql-ctr.ams1.int.nul.ie.` |
|
||||
| `colony-psql-ctr` | `A` | `10.100.2.4` |
|
||||
| `colony-psql-ctr` | `AAAA` | `2a0e:97c0:4d2:12::4` |
|
||||
| `colony-routing` | `A` | `10.100.0.2` |
|
||||
| `colony-vms` | `A` | `10.100.1.1` |
|
||||
| `colony-vms` | `AAAA` | `2a0e:97c0:4d2:11::1` |
|
||||
| `ctr` | `CNAME` | `shill-vm.ams1.int.nul.ie.` |
|
||||
| `darts-cust` | `A` | `94.142.242.255` |
|
||||
| `darts-cust` | `AAAA` | `2a0e:97c0:4d2:2001::1` |
|
||||
| `enshrouded` | `A` | `94.142.240.44` |
|
||||
| `enshrouded-oci` | `A` | `10.100.3.5` |
|
||||
| `enshrouded-oci` | `AAAA` | `2a0e:97c0:4d2:13::5` |
|
||||
| `estuary-vm` | `A` | `94.142.240.44` |
|
||||
| `estuary-vm` | `AAAA` | `2a02:898:0:20::329:1` |
|
||||
| `estuary-vm-base` | `A` | `10.100.0.1` |
|
||||
| `estuary-vm-base` | `AAAA` | `2a0e:97c0:4d2:10::1` |
|
||||
| `fw` | `CNAME` | `estuary-vm.ams1.int.nul.ie.` |
|
||||
| `gam-ctr` | `A` | `10.100.2.11` |
|
||||
| `gam-ctr` | `AAAA` | `2a0e:97c0:4d2:12::b` |
|
||||
| `git-vm` | `A` | `94.142.241.117` |
|
||||
| `git-vm` | `AAAA` | `2a0e:97c0:4d2:11::4` |
|
||||
| `git-vm-routing` | `A` | `10.100.1.4` |
|
||||
| `graeme` | `A` | `94.142.240.44` |
|
||||
| `graeme` | `AAAA` | `2a0e:97c0:4d2:13::8` |
|
||||
| `graeme-oci` | `A` | `10.100.3.8` |
|
||||
| `graeme-oci` | `AAAA` | `2a0e:97c0:4d2:13::8` |
|
||||
| `hillcrest-tun` | `A` | `10.100.5.2` |
|
||||
| `http` | `A` | `94.142.240.44` |
|
||||
| `http` | `AAAA` | `2a0e:97c0:4d2:12::2` |
|
||||
| `jackflix-ctr` | `A` | `10.100.2.6` |
|
||||
| `jackflix-ctr` | `AAAA` | `2a0e:97c0:4d2:12::6` |
|
||||
| `jam-cust` | `A` | `10.100.100.4` |
|
||||
| `jam-cust` | `AAAA` | `2a0e:97c0:4d2:2002::1` |
|
||||
| `jam-fwd` | `A` | `94.142.241.225` |
|
||||
| `john-valorant-tun` | `A` | `10.100.5.6` |
|
||||
| `kevcraft` | `A` | `94.142.240.44` |
|
||||
| `kevcraft` | `AAAA` | `2a0e:97c0:4d2:13::6` |
|
||||
| `kevcraft-oci` | `A` | `10.100.3.6` |
|
||||
| `kevcraft-oci` | `AAAA` | `2a0e:97c0:4d2:13::6` |
|
||||
| `kinkcraft` | `A` | `94.142.240.44` |
|
||||
| `kinkcraft` | `AAAA` | `2a0e:97c0:4d2:13::7` |
|
||||
| `kinkcraft-oci` | `A` | `10.100.3.7` |
|
||||
| `kinkcraft-oci` | `AAAA` | `2a0e:97c0:4d2:13::7` |
|
||||
| `librespeed` | `CNAME` | `http.ams1.int.nul.ie.` |
|
||||
| `mail-vm` | `A` | `94.142.241.227` |
|
||||
| `mail-vm` | `AAAA` | `2a0e:97c0:4d2:2000::1` |
|
||||
| `middleman-ctr` | `A` | `10.100.2.2` |
|
||||
| `middleman-ctr` | `AAAA` | `2a0e:97c0:4d2:12::2` |
|
||||
| `ns` | `ALIAS` | `estuary-vm.ams1.int.nul.ie.` |
|
||||
| `object-ctr` | `A` | `10.100.2.7` |
|
||||
| `object-ctr` | `AAAA` | `2a0e:97c0:4d2:12::7` |
|
||||
| `oci` | `CNAME` | `whale-vm.ams1.int.nul.ie.` |
|
||||
| `qclk-ctr` | `A` | `10.100.2.10` |
|
||||
| `qclk-ctr` | `AAAA` | `2a0e:97c0:4d2:12::a` |
|
||||
| `shill-vm` | `A` | `94.142.241.225` |
|
||||
| `shill-vm` | `AAAA` | `2a0e:97c0:4d2:11::2` |
|
||||
| `shill-vm-ctrs` | `A` | `10.100.2.1` |
|
||||
| `shill-vm-ctrs` | `AAAA` | `2a0e:97c0:4d2:12::1` |
|
||||
| `shill-vm-routing` | `A` | `10.100.1.2` |
|
||||
| `simpcraft` | `A` | `94.142.240.44` |
|
||||
| `simpcraft` | `AAAA` | `2a0e:97c0:4d2:13::3` |
|
||||
| `simpcraft-oci` | `A` | `10.100.3.3` |
|
||||
| `simpcraft-oci` | `AAAA` | `2a0e:97c0:4d2:13::3` |
|
||||
| `simpcraft-staging` | `A` | `94.142.240.44` |
|
||||
| `simpcraft-staging` | `AAAA` | `2a0e:97c0:4d2:13::4` |
|
||||
| `simpcraft-staging-oci` | `A` | `10.100.3.4` |
|
||||
| `simpcraft-staging-oci` | `AAAA` | `2a0e:97c0:4d2:13::4` |
|
||||
| `terraria` | `A` | `94.142.240.44` |
|
||||
| `terraria` | `AAAA` | `2a0e:97c0:4d2:12::b` |
|
||||
| `toot-ctr` | `A` | `10.100.2.8` |
|
||||
| `toot-ctr` | `AAAA` | `2a0e:97c0:4d2:12::8` |
|
||||
| `valheim` | `A` | `94.142.240.44` |
|
||||
| `valheim` | `AAAA` | `2a0e:97c0:4d2:13::2` |
|
||||
| `valheim-oci` | `A` | `10.100.3.2` |
|
||||
| `valheim-oci` | `AAAA` | `2a0e:97c0:4d2:13::2` |
|
||||
| `vaultwarden-ctr` | `A` | `10.100.2.3` |
|
||||
| `vaultwarden-ctr` | `AAAA` | `2a0e:97c0:4d2:12::3` |
|
||||
| `vm` | `CNAME` | `colony.ams1.int.nul.ie.` |
|
||||
| `waffletail-ctr` | `A` | `10.100.2.9` |
|
||||
| `waffletail-ctr` | `AAAA` | `2a0e:97c0:4d2:12::9` |
|
||||
| `whale-vm` | `A` | `94.142.241.226` |
|
||||
| `whale-vm` | `AAAA` | `2a0e:97c0:4d2:11::3` |
|
||||
| `whale-vm-routing` | `A` | `10.100.1.3` |
|
||||
<!-- dns-end -->
|
||||
|
||||
### IPv4 reverse zone: `100.10.in-addr.arpa`
|
||||
|
||||
<!-- dns: 100.10.in-addr.arpa -->
|
||||
<!-- dns-start -->
|
||||
| Address | Name |
|
||||
|---|---|
|
||||
| `10.100.0.1` | `estuary-vm-base.ams1.int.nul.ie.` |
|
||||
| `10.100.0.2` | `colony-routing.ams1.int.nul.ie.` |
|
||||
| `10.100.1.1` | `colony-vms.ams1.int.nul.ie.` |
|
||||
| `10.100.1.2` | `shill-vm-routing.ams1.int.nul.ie.` |
|
||||
| `10.100.1.3` | `whale-vm-routing.ams1.int.nul.ie.` |
|
||||
| `10.100.1.4` | `git-vm-routing.ams1.int.nul.ie.` |
|
||||
| `10.100.2.1` | `shill-vm-ctrs.ams1.int.nul.ie.` |
|
||||
| `10.100.2.2` | `middleman-ctr.ams1.int.nul.ie.` |
|
||||
| `10.100.2.3` | `vaultwarden-ctr.ams1.int.nul.ie.` |
|
||||
| `10.100.2.4` | `colony-psql-ctr.ams1.int.nul.ie.` |
|
||||
| `10.100.2.5` | `chatterbox-ctr.ams1.int.nul.ie.` |
|
||||
| `10.100.2.6` | `jackflix-ctr.ams1.int.nul.ie.` |
|
||||
| `10.100.2.7` | `object-ctr.ams1.int.nul.ie.` |
|
||||
| `10.100.2.8` | `toot-ctr.ams1.int.nul.ie.` |
|
||||
| `10.100.2.9` | `waffletail-ctr.ams1.int.nul.ie.` |
|
||||
| `10.100.2.10` | `qclk-ctr.ams1.int.nul.ie.` |
|
||||
| `10.100.2.11` | `gam-ctr.ams1.int.nul.ie.` |
|
||||
| `10.100.3.2` | `valheim-oci.ams1.int.nul.ie.` |
|
||||
| `10.100.3.3` | `simpcraft-oci.ams1.int.nul.ie.` |
|
||||
| `10.100.3.4` | `simpcraft-staging-oci.ams1.int.nul.ie.` |
|
||||
| `10.100.3.5` | `enshrouded-oci.ams1.int.nul.ie.` |
|
||||
| `10.100.3.6` | `kevcraft-oci.ams1.int.nul.ie.` |
|
||||
| `10.100.3.7` | `kinkcraft-oci.ams1.int.nul.ie.` |
|
||||
| `10.100.3.8` | `graeme-oci.ams1.int.nul.ie.` |
|
||||
<!-- dns-end -->
|
||||
|
||||
### IPv6 reverse zone: `2.d.4.0.0.c.7.9.e.0.a.2.ip6.arpa`
|
||||
|
||||
<!-- dns: 2.d.4.0.0.c.7.9.e.0.a.2.ip6.arpa -->
|
||||
<!-- dns-start -->
|
||||
| Address | Name |
|
||||
|---|---|
|
||||
| `2a0e:97c0:4d2:10::1` | `estuary-vm-base.ams1.int.nul.ie.` |
|
||||
| `2a0e:97c0:4d2:10::2` | `colony.ams1.int.nul.ie.` |
|
||||
| `2a0e:97c0:4d2:11::1` | `colony-vms.ams1.int.nul.ie.` |
|
||||
| `2a0e:97c0:4d2:11::2` | `shill-vm.ams1.int.nul.ie.` |
|
||||
| `2a0e:97c0:4d2:11::3` | `whale-vm.ams1.int.nul.ie.` |
|
||||
| `2a0e:97c0:4d2:11::4` | `git-vm.ams1.int.nul.ie.` |
|
||||
| `2a0e:97c0:4d2:12::1` | `shill-vm-ctrs.ams1.int.nul.ie.` |
|
||||
| `2a0e:97c0:4d2:12::2` | `middleman-ctr.ams1.int.nul.ie.` |
|
||||
| `2a0e:97c0:4d2:12::3` | `vaultwarden-ctr.ams1.int.nul.ie.` |
|
||||
| `2a0e:97c0:4d2:12::4` | `colony-psql-ctr.ams1.int.nul.ie.` |
|
||||
| `2a0e:97c0:4d2:12::5` | `chatterbox-ctr.ams1.int.nul.ie.` |
|
||||
| `2a0e:97c0:4d2:12::6` | `jackflix-ctr.ams1.int.nul.ie.` |
|
||||
| `2a0e:97c0:4d2:12::7` | `object-ctr.ams1.int.nul.ie.` |
|
||||
| `2a0e:97c0:4d2:12::8` | `toot-ctr.ams1.int.nul.ie.` |
|
||||
| `2a0e:97c0:4d2:12::9` | `waffletail-ctr.ams1.int.nul.ie.` |
|
||||
| `2a0e:97c0:4d2:12::a` | `qclk-ctr.ams1.int.nul.ie.` |
|
||||
| `2a0e:97c0:4d2:12::b` | `gam-ctr.ams1.int.nul.ie.` |
|
||||
| `2a0e:97c0:4d2:13::2` | `valheim-oci.ams1.int.nul.ie.` |
|
||||
| `2a0e:97c0:4d2:13::3` | `simpcraft-oci.ams1.int.nul.ie.` |
|
||||
| `2a0e:97c0:4d2:13::4` | `simpcraft-staging-oci.ams1.int.nul.ie.` |
|
||||
| `2a0e:97c0:4d2:13::5` | `enshrouded-oci.ams1.int.nul.ie.` |
|
||||
| `2a0e:97c0:4d2:13::6` | `kevcraft-oci.ams1.int.nul.ie.` |
|
||||
| `2a0e:97c0:4d2:13::7` | `kinkcraft-oci.ams1.int.nul.ie.` |
|
||||
| `2a0e:97c0:4d2:13::8` | `graeme-oci.ams1.int.nul.ie.` |
|
||||
| `2a0e:97c0:4d2:2000::1` | `mail.nul.ie.` |
|
||||
| `2a0e:97c0:4d2:2001::1` | `darts-cust.ams1.int.nul.ie.` |
|
||||
<!-- dns-end -->
|
||||
|
||||
## Home
|
||||
|
||||
These zones are served by [`river`](../sites/home/river.md) and
|
||||
[`stream`](../sites/home/stream.md); their shared source configuration is
|
||||
[`routing-common/dns.nix`](../../nixos/boxes/home/routing-common/dns.nix).
|
||||
|
||||
### Forward zone: `h.nul.ie`
|
||||
|
||||
<!-- dns: h.nul.ie -->
|
||||
<!-- dns-start -->
|
||||
| Name | Type | Value |
|
||||
|---|---|---|
|
||||
| `@` | `NS` | `ns1.h.nul.ie.` |
|
||||
| `@` | `NS` | `ns2.h.nul.ie.` |
|
||||
| `boot` | `CNAME` | `river-hi.h.nul.ie.` |
|
||||
| `brian` | `A` | `192.168.64.13` |
|
||||
| `castle` | `A` | `192.168.68.40` |
|
||||
| `castle` | `AAAA` | `2a0e:97c0:4d0:1::3:1` |
|
||||
| `cellar` | `A` | `192.168.68.80` |
|
||||
| `cellar` | `AAAA` | `2a0e:97c0:4d0:1::4:1` |
|
||||
| `dave` | `A` | `192.168.68.11` |
|
||||
| `dave` | `AAAA` | `2a0e:97c0:4d0:1::1:2` |
|
||||
| `dave-core` | `A` | `192.168.64.11` |
|
||||
| `dave-lo` | `A` | `192.168.72.11` |
|
||||
| `dave-lo` | `AAAA` | `2a0e:97c0:4d0:2::1:2` |
|
||||
| `dyn` | `NS` | `ns1.dyn.h.nul.ie.` |
|
||||
| `dyn` | `NS` | `ns2.dyn.h.nul.ie.` |
|
||||
| `frigate` | `CNAME` | `hass-ctr.h.nul.ie.` |
|
||||
| `hass-ctr` | `A` | `192.168.68.103` |
|
||||
| `hass-ctr` | `AAAA` | `2a0e:97c0:4d0:1::5:3` |
|
||||
| `hass-ctr-lo` | `A` | `192.168.72.103` |
|
||||
| `hass-ctr-lo` | `AAAA` | `2a0e:97c0:4d0:2::5:3` |
|
||||
| `jim` | `A` | `192.168.68.10` |
|
||||
| `jim` | `AAAA` | `2a0e:97c0:4d0:1::1:1` |
|
||||
| `jim-core` | `A` | `192.168.64.10` |
|
||||
| `jim-lo` | `A` | `192.168.72.10` |
|
||||
| `jim-lo` | `AAAA` | `2a0e:97c0:4d0:2::1:1` |
|
||||
| `nixlight` | `A` | `192.168.72.46` |
|
||||
| `ns1` | `ALIAS` | `river.h.nul.ie.` |
|
||||
| `ns1.dyn` | `ALIAS` | `river.h.nul.ie.` |
|
||||
| `ns2` | `ALIAS` | `stream.h.nul.ie.` |
|
||||
| `ns2.dyn` | `ALIAS` | `stream.h.nul.ie.` |
|
||||
| `palace` | `A` | `192.168.68.22` |
|
||||
| `palace` | `AAAA` | `2a0e:97c0:4d0:1::2:1` |
|
||||
| `palace-core` | `A` | `192.168.64.20` |
|
||||
| `palace-kvm` | `A` | `192.168.72.21` |
|
||||
| `reolink-living-room` | `A` | `192.168.72.45` |
|
||||
| `river` | `A (LUA)` | `generated at query time` |
|
||||
| `river` | `AAAA` | `2a0e:97c0:4df:0:1::1` |
|
||||
| `river-core` | `A` | `192.168.64.1` |
|
||||
| `river-hi` | `A` | `192.168.68.1` |
|
||||
| `river-hi` | `AAAA` | `2a0e:97c0:4d0:1::1` |
|
||||
| `river-lo` | `A` | `192.168.72.1` |
|
||||
| `river-lo` | `AAAA` | `2a0e:97c0:4d0:2::1` |
|
||||
| `river-ut` | `A` | `192.168.80.1` |
|
||||
| `river-ut` | `AAAA` | `2a0e:97c0:4d0:3::1` |
|
||||
| `router-hi` | `A` | `192.168.71.254` |
|
||||
| `router-hi` | `AAAA` | `2a0e:97c0:4d0:1::ffff` |
|
||||
| `router-lo` | `A` | `192.168.79.254` |
|
||||
| `router-lo` | `AAAA` | `2a0e:97c0:4d0:2::ffff` |
|
||||
| `router-ut` | `A` | `192.168.80.254` |
|
||||
| `router-ut` | `AAAA` | `2a0e:97c0:4d0:3::ffff` |
|
||||
| `sfh` | `A` | `192.168.68.81` |
|
||||
| `sfh` | `AAAA` | `2a0e:97c0:4d0:1::4:2` |
|
||||
| `shytzel` | `A` | `192.168.64.12` |
|
||||
| `stream` | `A (LUA)` | `generated at query time` |
|
||||
| `stream` | `AAAA` | `2a0e:97c0:4df:0:1::2` |
|
||||
| `stream-core` | `A` | `192.168.64.2` |
|
||||
| `stream-hi` | `A` | `192.168.68.2` |
|
||||
| `stream-hi` | `AAAA` | `2a0e:97c0:4d0:1::2` |
|
||||
| `stream-lo` | `A` | `192.168.72.2` |
|
||||
| `stream-lo` | `AAAA` | `2a0e:97c0:4d0:2::2` |
|
||||
| `stream-ut` | `A` | `192.168.80.2` |
|
||||
| `stream-ut` | `AAAA` | `2a0e:97c0:4d0:3::2` |
|
||||
| `unifi-ctr` | `A` | `192.168.68.100` |
|
||||
| `unifi-ctr` | `AAAA` | `2a0e:97c0:4d0:1::5:1` |
|
||||
| `unifi-ctr-core` | `A` | `192.168.64.21` |
|
||||
| `ups` | `A` | `192.168.72.20` |
|
||||
| `vibe` | `A` | `192.168.68.15` |
|
||||
| `vibe` | `AAAA` | `2a0e:97c0:4d0:1::1:6` |
|
||||
| `vibe-core` | `A` | `192.168.64.15` |
|
||||
| `vibe-lo` | `A` | `192.168.72.15` |
|
||||
| `vibe-lo` | `AAAA` | `2a0e:97c0:4d0:2::1:6` |
|
||||
| `wave` | `A` | `192.168.72.14` |
|
||||
| `wave` | `AAAA` | `2a0e:97c0:4d0:2::1:5` |
|
||||
| `wave-core` | `A` | `192.168.64.14` |
|
||||
<!-- dns-end -->
|
||||
|
||||
### IPv4 reverse zone: `168.192.in-addr.arpa`
|
||||
|
||||
<!-- dns: 168.192.in-addr.arpa -->
|
||||
<!-- dns-start -->
|
||||
| Address | Name |
|
||||
|---|---|
|
||||
| `192.168.64.1` | `river-core.h.nul.ie.` |
|
||||
| `192.168.64.2` | `stream-core.h.nul.ie.` |
|
||||
| `192.168.64.20` | `palace-core.h.nul.ie.` |
|
||||
| `192.168.64.21` | `unifi-ctr-core.h.nul.ie.` |
|
||||
| `192.168.68.1` | `river-hi.h.nul.ie.` |
|
||||
| `192.168.68.2` | `stream-hi.h.nul.ie.` |
|
||||
| `192.168.68.22` | `palace.h.nul.ie.` |
|
||||
| `192.168.68.40` | `castle.h.nul.ie.` |
|
||||
| `192.168.68.80` | `cellar.h.nul.ie.` |
|
||||
| `192.168.68.81` | `sfh.h.nul.ie.` |
|
||||
| `192.168.68.100` | `unifi-ctr.h.nul.ie.` |
|
||||
| `192.168.68.103` | `hass-ctr.h.nul.ie.` |
|
||||
| `192.168.71.254` | `router-hi.h.nul.ie.` |
|
||||
| `192.168.72.1` | `river-lo.h.nul.ie.` |
|
||||
| `192.168.72.2` | `stream-lo.h.nul.ie.` |
|
||||
| `192.168.72.103` | `hass-ctr-lo.h.nul.ie.` |
|
||||
| `192.168.79.254` | `router-lo.h.nul.ie.` |
|
||||
| `192.168.80.1` | `river-ut.h.nul.ie.` |
|
||||
| `192.168.80.2` | `stream-ut.h.nul.ie.` |
|
||||
| `192.168.80.254` | `router-ut.h.nul.ie.` |
|
||||
<!-- dns-end -->
|
||||
|
||||
### IPv6 reverse zone: `0.d.4.0.0.c.7.9.e.0.a.2.ip6.arpa`
|
||||
|
||||
<!-- dns: 0.d.4.0.0.c.7.9.e.0.a.2.ip6.arpa -->
|
||||
<!-- dns-start -->
|
||||
| Address | Name |
|
||||
|---|---|
|
||||
| `2a0e:97c0:4d0:1::1` | `river-hi.h.nul.ie.` |
|
||||
| `2a0e:97c0:4d0:1::2` | `stream-hi.h.nul.ie.` |
|
||||
| `2a0e:97c0:4d0:1::ffff` | `router-hi.h.nul.ie.` |
|
||||
| `2a0e:97c0:4d0:1::2:1` | `palace.h.nul.ie.` |
|
||||
| `2a0e:97c0:4d0:1::3:1` | `castle.h.nul.ie.` |
|
||||
| `2a0e:97c0:4d0:1::4:1` | `cellar.h.nul.ie.` |
|
||||
| `2a0e:97c0:4d0:1::4:2` | `sfh.h.nul.ie.` |
|
||||
| `2a0e:97c0:4d0:1::5:1` | `unifi-ctr.h.nul.ie.` |
|
||||
| `2a0e:97c0:4d0:1::5:3` | `hass-ctr.h.nul.ie.` |
|
||||
| `2a0e:97c0:4d0:2::1` | `river-lo.h.nul.ie.` |
|
||||
| `2a0e:97c0:4d0:2::2` | `stream-lo.h.nul.ie.` |
|
||||
| `2a0e:97c0:4d0:2::ffff` | `router-lo.h.nul.ie.` |
|
||||
| `2a0e:97c0:4d0:2::5:3` | `hass-ctr-lo.h.nul.ie.` |
|
||||
| `2a0e:97c0:4d0:3::1` | `river-ut.h.nul.ie.` |
|
||||
| `2a0e:97c0:4d0:3::2` | `stream-ut.h.nul.ie.` |
|
||||
| `2a0e:97c0:4d0:3::ffff` | `router-ut.h.nul.ie.` |
|
||||
<!-- dns-end -->
|
||||
@@ -7,6 +7,14 @@ narrower gateway role than [`britway`](britway.md) (no control plane, no BGP).
|
||||
- **Host:** VPS (Birmingham, `bhx1`; provider uplink assignment `allhost`)
|
||||
- **nixpkgs:** `mine`
|
||||
|
||||
## Platform
|
||||
|
||||
| Component | Allocation |
|
||||
|---|---|
|
||||
| Virtualisation | KVM/QEMU guest |
|
||||
| Compute | 2 vCPUs and 2 GiB RAM |
|
||||
| Storage | 32 GiB virtio disk with separate ext4 filesystems for `/boot`, `/nix` and `/persist`; root is tmpfs |
|
||||
|
||||
## Role
|
||||
|
||||
- **Tailscale exit node** — logs into the headscale on [`britway`](britway.md)
|
||||
@@ -20,14 +28,6 @@ narrower gateway role than [`britway`](britway.md) (no control plane, no BGP).
|
||||
|
||||
See the consolidated [network assignments](../networking.md#box-assignments) table (this box: `britnet`).
|
||||
|
||||
## Platform
|
||||
|
||||
| Component | Allocation |
|
||||
|---|---|
|
||||
| Virtualisation | KVM/QEMU guest |
|
||||
| Compute | 2 vCPUs and 2 GiB RAM |
|
||||
| Storage | 32 GiB virtio disk with separate ext4 filesystems for `/boot`, `/nix` and `/persist`; root is tmpfs |
|
||||
|
||||
## Networking
|
||||
|
||||
- The provider interface is renamed to `veth0` by MAC. Its IPv6 default gateway sits off-subnet, so
|
||||
|
||||
@@ -7,6 +7,14 @@ control plane, a tailnet exit node, and the BGP speaker for AS211024.
|
||||
- **Host:** VPS at Vultr (London, `lon1`)
|
||||
- **nixpkgs:** `mine`
|
||||
|
||||
## Platform
|
||||
|
||||
| Component | Allocation |
|
||||
|---|---|
|
||||
| Virtualisation | Vultr VC2 virtual guest on a QEMU-compatible platform |
|
||||
| Compute | 2 vCPUs and 2 GiB RAM |
|
||||
| Storage | 65 GiB virtio disk with separate ext4 filesystems for `/boot`, `/nix` and `/persist`; root is tmpfs |
|
||||
|
||||
## Role
|
||||
|
||||
- **Headscale** — the self-hosted Tailscale control plane at `hs.nul.ie`; every other box's
|
||||
@@ -27,14 +35,6 @@ control plane, a tailnet exit node, and the BGP speaker for AS211024.
|
||||
|
||||
See the consolidated [network assignments](../networking.md#box-assignments) table (this box: `britway`).
|
||||
|
||||
## Platform
|
||||
|
||||
| Component | Allocation |
|
||||
|---|---|
|
||||
| Virtualisation | Vultr VC2 virtual guest on a QEMU-compatible platform |
|
||||
| Compute | 2 vCPUs and 2 GiB RAM |
|
||||
| Storage | 65 GiB virtio disk with separate ext4 filesystems for `/boot`, `/nix` and `/persist`; root is tmpfs |
|
||||
|
||||
## Networking
|
||||
|
||||
- Two assignments: `vultr` on the provider interface `veth0` (renamed by MAC), and `as211024`
|
||||
|
||||
+12
-12
@@ -8,6 +8,18 @@ everything at the colony site.
|
||||
- **Host:** bare metal (this *is* the physical box)
|
||||
- **nixpkgs:** `mine-stable`
|
||||
|
||||
## Hardware
|
||||
|
||||
| Component | Inventory |
|
||||
|---|---|
|
||||
| Platform | ASRock Rack X570D4U server board |
|
||||
| CPU | AMD Ryzen 9 5950X (16 cores / 32 threads) |
|
||||
| Memory | 128 GiB |
|
||||
| NVMe storage | Three 2 TB Samsung SSD 980 PRO devices providing the NVMe-backed LVM thin pool and data LVs |
|
||||
| Bulk storage | Three 12 TB WD120EDBZ disks and one 18 TB WD180EDGZ disk for the bulk LVM volumes |
|
||||
| Boot | SanDisk USB device holding the EFI system partition |
|
||||
| Network / management | Two Intel I210 Gigabit Ethernet controllers, one passed through to `estuary`; ASPEED BMC graphics and console |
|
||||
|
||||
## Role
|
||||
|
||||
Bare-metal AMD host. It does little application work itself — its job is to run
|
||||
@@ -55,18 +67,6 @@ Netdata uses FreeIPMI while ignoring the VCCM sensor. The box also runs `smartd`
|
||||
|
||||
See the consolidated [network assignments](../../networking.md#box-assignments) table (this box: `colony`).
|
||||
|
||||
## Hardware
|
||||
|
||||
| Component | Inventory |
|
||||
|---|---|
|
||||
| Platform | ASRock Rack X570D4U server board |
|
||||
| CPU | AMD Ryzen 9 5950X (16 cores / 32 threads) |
|
||||
| Memory | 128 GiB |
|
||||
| NVMe storage | Three 2 TB Samsung SSD 980 PRO devices providing the NVMe-backed LVM thin pool and data LVs |
|
||||
| Bulk storage | Three 12 TB WD120EDBZ disks and one 18 TB WD180EDGZ disk for the bulk LVM volumes |
|
||||
| Boot | SanDisk USB device holding the EFI system partition |
|
||||
| Network / management | Two Intel I210 Gigabit Ethernet controllers, one passed through to `estuary`; ASPEED BMC graphics and console |
|
||||
|
||||
## Networking
|
||||
|
||||
- Two bridges: `base` (the colony base network, shared with `estuary`) and
|
||||
|
||||
@@ -75,7 +75,8 @@ Besides the forwards, `extraRules` defines:
|
||||
|
||||
## DNS
|
||||
|
||||
Both halves are PowerDNS ([`dns.nix`](../../../nixos/boxes/colony/vms/estuary/dns.nix)).
|
||||
Both halves are PowerDNS ([`dns.nix`](../../../nixos/boxes/colony/vms/estuary/dns.nix)). The live
|
||||
forward and reverse records are listed in the generated [DNS reference](../../reference/dns.md).
|
||||
|
||||
### Authoritative
|
||||
|
||||
@@ -86,7 +87,8 @@ zone.
|
||||
- Zone contents are largely generated from `allAssignments`
|
||||
(`lib.my.dns.fwdRecords` / `ptrRecords` / `ptr6Records`); `ALIAS` records
|
||||
(with `expand-alias`) point the zone apex at estuary itself.
|
||||
- AXFR is allowed to HE.net's secondary (`216.218.133.2` / `2001:470:600::2`).
|
||||
- AXFR is allowed to HE.net's secondary and the trusted internal/site-egress sources used by the
|
||||
generated DNS reference.
|
||||
- `_acme-challenge` is a LUA `TXT` record answered from a file (DNS-01 issuance).
|
||||
- Reached publicly via the NAT redirect of port 53 → 5353; the `base` side also
|
||||
accepts DNS directly.
|
||||
|
||||
@@ -77,6 +77,12 @@ Management uses host `.15`: `192.168.64.15` on native/core as a backup,
|
||||
route through its VIP; `untrusted` has no address. With `l2mtu 9214`, `vibe` can use the jumbo
|
||||
`hi` network unlike `wave`.
|
||||
|
||||
Like the switches, `vibe` is **pure L2 and must not route** — `ip-forward=no`, IPv6 `forward=no`,
|
||||
`accept-router-advertisements=no`, `ra-lifetime=0`. RouterOS ships these forwarding-on and then
|
||||
advertises itself as an IPv6 router; re-check after any upgrade. See
|
||||
[switches.md#switches-must-not-route](switches.md#switches-must-not-route) for the rationale and
|
||||
commands.
|
||||
|
||||
## wave (Cudy AX3000, OpenWrt)
|
||||
|
||||
Single-port AP, so the port is a VLAN **trunk** carrying management + both SSIDs.
|
||||
|
||||
+11
-11
@@ -7,6 +7,17 @@ root storage on NVMe-oF volumes from `cellar`.
|
||||
- **Host:** physical
|
||||
- **nixpkgs:** `mine`
|
||||
|
||||
## Hardware
|
||||
|
||||
| Component | Inventory |
|
||||
|---|---|
|
||||
| Platform | ASUS ProArt X670E-CREATOR WIFI |
|
||||
| CPU | AMD Ryzen 9 7950X (16 cores / 32 threads) |
|
||||
| Memory | 64 GiB |
|
||||
| Graphics | Integrated AMD Radeon graphics |
|
||||
| Network | Mellanox ConnectX-4 100G, Aquantia AQC113CS 10G, Intel I225-V 2.5G and MediaTek MT7922 Wi-Fi 6E controllers |
|
||||
| System storage | No local root disk; the box netboots and uses the SPDK NVMe-oF namespace exported by `cellar` |
|
||||
|
||||
## Role
|
||||
|
||||
### Desktop
|
||||
@@ -38,17 +49,6 @@ a `drm-amd-display` flicker patch remains commented out.
|
||||
|
||||
See the consolidated [network assignments](../../networking.md#box-assignments) table (this box: `castle`).
|
||||
|
||||
## Hardware
|
||||
|
||||
| Component | Inventory |
|
||||
|---|---|
|
||||
| Platform | ASUS ProArt X670E-CREATOR WIFI |
|
||||
| CPU | AMD Ryzen 9 7950X (16 cores / 32 threads) |
|
||||
| Memory | 64 GiB |
|
||||
| Graphics | Integrated AMD Radeon graphics |
|
||||
| Network | Mellanox ConnectX-4 100G, Aquantia AQC113CS 10G, Intel I225-V 2.5G and MediaTek MT7922 Wi-Fi 6E controllers |
|
||||
| System storage | No local root disk; the box netboots and uses the SPDK NVMe-oF namespace exported by `cellar` |
|
||||
|
||||
## Networking
|
||||
|
||||
- `et100g` (100G, MTU 9000) carries `lan-hi` (the `hi` assignment, also pinned by a kea
|
||||
|
||||
+12
-12
@@ -8,18 +8,6 @@ SR-IOV VFs, PCI NVMe drives and LVM disks.
|
||||
- **Host:** physical
|
||||
- **nixpkgs:** `mine-stable`
|
||||
|
||||
## Role
|
||||
|
||||
- Home hypervisor: VMs are declared in `my.vms.instances`
|
||||
([`palace/vms/default.nix`](../../../nixos/boxes/home/palace/vms/default.nix)); disks are LVs in
|
||||
the `main` thin pool (`services.lvm.boot.thin.enable`).
|
||||
- AMD box (`kvm-amd`, `amd_iommu=on`, microcode updates); the kernel is built with
|
||||
`ACPI_APEI_PCIEAER`/`PCIEAER` for the PCIe passthrough work below.
|
||||
|
||||
## Network assignments
|
||||
|
||||
See the consolidated [network assignments](../../networking.md#box-assignments) table (this box: `palace`).
|
||||
|
||||
## Hardware
|
||||
|
||||
| Component | Inventory |
|
||||
@@ -32,6 +20,18 @@ See the consolidated [network assignments](../../networking.md#box-assignments)
|
||||
| NVMe storage | Three 2 TB Samsung NVMe devices passed through to `cellar`; SPDK combines them as the `NVMeRaid` RAID 0 device |
|
||||
| Network / graphics | Mellanox ConnectX-4 100G adapter with four SR-IOV VFs, two Intel I211 Gigabit Ethernet controllers, and an AMD Radeon RX 550/560-family GPU |
|
||||
|
||||
## Role
|
||||
|
||||
- Home hypervisor: VMs are declared in `my.vms.instances`
|
||||
([`palace/vms/default.nix`](../../../nixos/boxes/home/palace/vms/default.nix)); disks are LVs in
|
||||
the `main` thin pool (`services.lvm.boot.thin.enable`).
|
||||
- AMD box (`kvm-amd`, `amd_iommu=on`, microcode updates); the kernel is built with
|
||||
`ACPI_APEI_PCIEAER`/`PCIEAER` for the PCIe passthrough work below.
|
||||
|
||||
## Network assignments
|
||||
|
||||
See the consolidated [network assignments](../../networking.md#box-assignments) table (this box: `palace`).
|
||||
|
||||
## Networking
|
||||
|
||||
100G `et100g` (mlx5, MTU 9000) uplinks to the `dave` switch and carries `lan-hi` (VLAN 100, the
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# sfh
|
||||
|
||||
"Services for home" — the NixOS container host for the home site. A VM on `palace` that netboots
|
||||
"Shill from home" — the NixOS container host for the home site. A VM on `palace` that netboots
|
||||
from `river` and runs its root off NVMe-oF from `cellar`.
|
||||
|
||||
- **Source:** [`nixos/boxes/home/palace/vms/sfh/`](../../../../nixos/boxes/home/palace/vms/sfh)
|
||||
|
||||
+17
-18
@@ -8,6 +8,18 @@ redundant router pair with [`river`](river.md) and is dual-homed to both switche
|
||||
- **Host:** physical
|
||||
- **nixpkgs:** `mine`
|
||||
|
||||
## Hardware
|
||||
|
||||
| Component | Inventory |
|
||||
|---|---|
|
||||
| Platform | BROUNION R86S |
|
||||
| CPU | Intel Celeron N5105 (4 cores / 4 threads) |
|
||||
| Memory | 16 GiB |
|
||||
| Storage | 512 GB Samsung SSD 970 PRO NVMe containing `/boot`, `/nix` and `/persist`; integrated 128 GB eMMC is present but is not used by the declared filesystems |
|
||||
| Network | Three Intel `igc` interfaces and a dual-port Mellanox `mlx4_en` adapter; `wan`, `lan-jim` and `lan-dave` use three of these ports |
|
||||
|
||||
The platform configuration enables `kvm-intel`, `intel_iommu=on` and Intel microcode updates.
|
||||
|
||||
## Role
|
||||
|
||||
At `routing-common` index 1, `stream` normally holds the secondary position in the router pair.
|
||||
@@ -61,28 +73,15 @@ box sets:
|
||||
reaching the modem subnet (needed only because it shares `wan`; WAN egress is otherwise
|
||||
accepted).
|
||||
|
||||
## Platform
|
||||
|
||||
### Hardware
|
||||
|
||||
| Component | Inventory |
|
||||
|---|---|
|
||||
| Platform | BROUNION R86S |
|
||||
| CPU | Intel Celeron N5105 (4 cores / 4 threads) |
|
||||
| Memory | 16 GiB |
|
||||
| Storage | 512 GB Samsung SSD 970 PRO NVMe containing `/boot`, `/nix` and `/persist`; integrated 128 GB eMMC is present but is not used by the declared filesystems |
|
||||
| Network | Three Intel `igc` interfaces and a dual-port Mellanox `mlx4_en` adapter; `wan`, `lan-jim` and `lan-dave` use three of these ports |
|
||||
|
||||
The platform configuration enables `kvm-intel`, `intel_iommu=on` and Intel microcode updates.
|
||||
|
||||
### Switching (RSTP)
|
||||
## Switching (RSTP)
|
||||
|
||||
`stream` is dual-homed to both switches: `lan-jim` (igc) and `lan-dave` (mlx4_en), both MTU 9000,
|
||||
are enslaved to the `lan` bridge with `STP=true`. [`routing-common/mstpd.nix`](../../../nixos/boxes/home/routing-common/mstpd.nix)
|
||||
runs a patched `mstpd` and forces RSTP on `lan` once it's routable, so exactly one uplink carries
|
||||
are enslaved to the `lan` bridge with `STP=true`. The explicit bridge-port costs prefer
|
||||
`lan-dave` at 10 over `lan-jim` at 100. [`routing-common/mstpd.nix`](../../../nixos/boxes/home/routing-common/mstpd.nix)
|
||||
runs a patched `mstpd` and forces RSTP on `lan` once it is configured, so exactly one uplink carries
|
||||
traffic at a time. (The remaining NICs are renamed `et2`/`et5` and left unconfigured.)
|
||||
|
||||
### Deployment
|
||||
## Deployment
|
||||
|
||||
`my.deploy.node.hostname` is currently commented out.
|
||||
|
||||
|
||||
@@ -86,7 +86,9 @@ from a box that does not depend on it, or power `castle` off cleanly first.
|
||||
|
||||
Switch L3 presence (`/interface vlan` on `main`) exists **only** for VLANs the switch is managed
|
||||
from — `hi` (100) and `lo` (110), plus native core. WAN and guest VLANs deliberately have no switch
|
||||
L3 interface.
|
||||
L3 interface. jim and dave carry a static IPv4 and **global IPv6** address on `hi`/`lo` (plus a
|
||||
static default route on each stack) purely for management — they are **pure L2, never routers**. See
|
||||
[Switches must not route](#switches-must-not-route).
|
||||
|
||||
## The Digiweb WAN path (trunked VLAN 10 + PVID 140)
|
||||
|
||||
@@ -147,6 +149,33 @@ this is plain tagged bridging.
|
||||
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.
|
||||
|
||||
## Switches must not route
|
||||
|
||||
jim and dave (and the `vibe` AP) are **pure L2** — river/stream do all routing. Their per-stack
|
||||
management addresses and static default routes exist only so the boxes themselves can be reached and
|
||||
reach out; they must **never** forward traffic or advertise themselves as routers. RouterOS defaults
|
||||
work against this: `ip-forward` and IPv6 `forward` ship **on**, and with IPv6 forwarding on RouterOS
|
||||
also emits Router Advertisements (`ra-lifetime=30m`) on every L3 interface — so a switch silently
|
||||
becomes a competing IPv6 default router. This surfaced after the 7.18 → 7.23 upgrade, when clients
|
||||
picked up dave/jim as default routers alongside river.
|
||||
|
||||
The required config on each RouterOS box:
|
||||
```
|
||||
/ip settings set ip-forward=no
|
||||
/ipv6 settings set forward=no accept-router-advertisements=no
|
||||
/ipv6 nd set [find] ra-lifetime=0
|
||||
```
|
||||
- `ip-forward=no` / `forward=no` — no L3 forwarding on either stack; IPv6 `forward=no` also stops RA
|
||||
emission at the source.
|
||||
- `accept-router-advertisements=no` — with forwarding off RouterOS would otherwise start *accepting*
|
||||
RAs; this keeps the box on its deterministic **static** default route.
|
||||
- `ra-lifetime=0` — belt-and-suspenders: even if forwarding is ever re-enabled the box advertises
|
||||
router-lifetime 0 (i.e. "not a default router"). Setting it also emits a withdrawal RA that
|
||||
actively clears the rogue default from clients (they otherwise cache it for up to ~30 min).
|
||||
|
||||
**After any RouterOS upgrade, re-check `/ip settings` and `/ipv6 settings`** — an upgrade can reset
|
||||
these to the forwarding-on defaults. brian (UniFi) is not a RouterOS box and was not affected.
|
||||
|
||||
## Future: multiple ONTs (per-port VLAN translation)
|
||||
|
||||
If a second ONT arrives (e.g. a Digiweb line for stream, or a second river), trunking breaks: both
|
||||
|
||||
Generated
+116
@@ -163,6 +163,29 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"determinate-nix": {
|
||||
"inputs": {
|
||||
"flake-parts": "flake-parts",
|
||||
"git-hooks-nix": "git-hooks-nix",
|
||||
"nixpkgs": [
|
||||
"nixpkgs-unstable"
|
||||
],
|
||||
"nixpkgs-23-11": "nixpkgs-23-11",
|
||||
"nixpkgs-regression": "nixpkgs-regression"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1785428605,
|
||||
"narHash": "sha256-wfaiSRLM1wDb4MV+NEzbyheK9Y03/oe56NR2I84UF7E=",
|
||||
"rev": "0ff46631f69584c9f76792cae595ea253bd482c3",
|
||||
"revCount": 26288,
|
||||
"type": "tarball",
|
||||
"url": "https://api.flakehub.com/f/pinned/DeterminateSystems/nix-src/3.21.9/019fb409-4d6e-7243-8a88-23ceee2520e9/source.tar.gz"
|
||||
},
|
||||
"original": {
|
||||
"type": "tarball",
|
||||
"url": "https://flakehub.com/f/DeterminateSystems/nix-src/%2A"
|
||||
}
|
||||
},
|
||||
"devshell": {
|
||||
"inputs": {
|
||||
"flake-utils": "flake-utils",
|
||||
@@ -256,6 +279,42 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-compat_2": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1696426674,
|
||||
"narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
|
||||
"owner": "edolstra",
|
||||
"repo": "flake-compat",
|
||||
"rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "edolstra",
|
||||
"repo": "flake-compat",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-parts": {
|
||||
"inputs": {
|
||||
"nixpkgs-lib": [
|
||||
"determinate-nix",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1748821116,
|
||||
"narHash": "sha256-F82+gS044J1APL0n4hH50GYdPRv/5JWm34oCJYmVKdE=",
|
||||
"rev": "49f0870db23e8c1ca0b5259734a02cd9e1e371a1",
|
||||
"revCount": 377,
|
||||
"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"
|
||||
},
|
||||
"original": {
|
||||
"type": "tarball",
|
||||
"url": "https://flakehub.com/f/hercules-ci/flake-parts/0.1"
|
||||
}
|
||||
},
|
||||
"flake-utils": {
|
||||
"inputs": {
|
||||
"systems": "systems"
|
||||
@@ -460,6 +519,30 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"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,
|
||||
"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"
|
||||
},
|
||||
"original": {
|
||||
"type": "tarball",
|
||||
"url": "https://flakehub.com/f/cachix/git-hooks.nix/0.1.941"
|
||||
}
|
||||
},
|
||||
"harmonia": {
|
||||
"inputs": {
|
||||
"crane": "crane",
|
||||
@@ -655,6 +738,22 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs-23-11": {
|
||||
"locked": {
|
||||
"lastModified": 1717159533,
|
||||
"narHash": "sha256-oamiKNfr2MS6yH64rUn99mIZjc45nGJlj9eGth/3Xuw=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "a62e6edd6d5e1fa0329b8653c801147986f8d446",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "a62e6edd6d5e1fa0329b8653c801147986f8d446",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs-mine": {
|
||||
"locked": {
|
||||
"lastModified": 1781356656,
|
||||
@@ -687,6 +786,22 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs-regression": {
|
||||
"locked": {
|
||||
"lastModified": 1643052045,
|
||||
"narHash": "sha256-uGJ0VXIhWKGXxkeNnq4TvV3CIOkUJ3PAoLZ3HMzNVMw=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs-stable": {
|
||||
"locked": {
|
||||
"lastModified": 1780902259,
|
||||
@@ -847,6 +962,7 @@
|
||||
"borgthin": "borgthin",
|
||||
"copyparty": "copyparty",
|
||||
"deploy-rs": "deploy-rs",
|
||||
"determinate-nix": "determinate-nix",
|
||||
"devshell": "devshell_3",
|
||||
"flake-utils": "flake-utils_6",
|
||||
"harmonia": "harmonia",
|
||||
|
||||
@@ -1,6 +1,19 @@
|
||||
{
|
||||
description = "System configs";
|
||||
|
||||
# Offer our Harmonia cache when building the flake itself, so `nix develop` / `nix build` don't
|
||||
# rebuild from source. Nix reads `nixConfig` before the flake evaluates and rejects any computed
|
||||
# value (imports/thunks), so these must stay literal — keep them in sync with `lib.my.c.nix.cache`.
|
||||
# Consumers must trust these (accept-flake-config / a trusted user) for them to take effect.
|
||||
nixConfig = {
|
||||
extra-substituters = [
|
||||
"https://nix-cache.nul.ie"
|
||||
];
|
||||
extra-trusted-public-keys = [
|
||||
"nix-cache.nul.ie-1:BzH5yMfF4HbzY1C977XzOxoPhEc9Zbu39ftPkUbH+m4="
|
||||
];
|
||||
};
|
||||
|
||||
inputs = {
|
||||
flake-utils.url = "github:numtide/flake-utils";
|
||||
# libnet.url = "github:reo101/nix-lib-net";
|
||||
@@ -21,6 +34,12 @@
|
||||
home-manager-stable.url = "home-manager/release-26.05";
|
||||
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.
|
||||
determinate-nix.url = "https://flakehub.com/f/DeterminateSystems/nix-src/*";
|
||||
determinate-nix.inputs.nixpkgs.follows = "nixpkgs-unstable";
|
||||
|
||||
# Stuff used by the flake for build / deployment
|
||||
# ragenix.url = "github:yaxitech/ragenix";
|
||||
ragenix.url = "github:devplayer0/ragenix/add-rekey-one-flag";
|
||||
@@ -83,6 +102,22 @@
|
||||
};
|
||||
pkgsLibOverlay = final: prev: { lib = prev.lib.extend libOverlay; };
|
||||
myPkgsOverlay = final: prev: import ./pkgs { lib = final.lib; pkgs = prev; };
|
||||
# Exposes Determinate Nix under a stable attr name so systems, homes and the devshell all
|
||||
# resolve the exact same package (referenced as `pkgs'.mine.determinate-nix` in configs).
|
||||
# `nix-util`'s `readLinkAt.works` unit test creates PATH_MAX-length symlinks, which our CI
|
||||
# runner's XFS-backed build filesystem rejects (XFS hard-caps symlink targets at 1024 bytes).
|
||||
# Skip just that test via gtest's GTEST_FILTER so the rest of the suite still gates the build.
|
||||
determinateOverlay = final: prev: {
|
||||
determinate-nix =
|
||||
(inputs.determinate-nix.packages.${prev.stdenv.hostPlatform.system}.default).overrideAttrs (o: {
|
||||
checkInputs = map
|
||||
(drv:
|
||||
if (drv.name or "") == "nix-util-tests-run"
|
||||
then drv.overrideAttrs (_: { GTEST_FILTER = "-readLinkAt.*"; })
|
||||
else drv)
|
||||
o.checkInputs;
|
||||
});
|
||||
};
|
||||
|
||||
# Override the flake-level lib since we're going to use it for non-config specific stuff
|
||||
pkgsFlakes = mapAttrs (_: pkgsFlake: pkgsFlake // { lib = pkgsFlake.lib.extend libOverlay; }) {
|
||||
@@ -111,6 +146,7 @@
|
||||
pkgsLibOverlay
|
||||
|
||||
myPkgsOverlay
|
||||
determinateOverlay
|
||||
inputs.devshell.overlays.default
|
||||
inputs.ragenix.overlays.default
|
||||
inputs.deploy-rs.overlays.default
|
||||
@@ -126,6 +162,7 @@
|
||||
pkgsLibOverlay
|
||||
|
||||
myPkgsOverlay
|
||||
determinateOverlay
|
||||
];
|
||||
|
||||
config = {
|
||||
@@ -187,7 +224,13 @@
|
||||
nixosModules = nixfiles.config.nixos.modules;
|
||||
homeModules = nixfiles.config.home-manager.modules;
|
||||
|
||||
nixosConfigurations = mapAttrs (_: s: s.rendered) nixfiles.config.nixos.systems;
|
||||
# Containers and the installer override `rendered` with a bare `extendModules` config
|
||||
# (`my.asContainer` / `my.asISO`) that lacks the `pkgs`/`lib` attrs `eval-config` exposes on a
|
||||
# normal system. Determinate Nix's flake schemas read `machine.pkgs.stdenv.system` for every
|
||||
# `nixosConfigurations` entry, so re-attach them from the full system eval (`configuration`).
|
||||
nixosConfigurations = mapAttrs
|
||||
(_: s: s.rendered // { inherit (s.configuration) pkgs lib; })
|
||||
nixfiles.config.nixos.systems;
|
||||
homeConfigurations = mapAttrs (_: s: s.configuration) nixfiles.config.home-manager.homes;
|
||||
|
||||
deploy = nixfiles.config.deploy-rs.rendered;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
let
|
||||
inherit (builtins) listToAttrs mapAttrs readFile;
|
||||
inherit (lib)
|
||||
optionalString nameValuePair concatMapStrings concatStringsSep optionalAttrs versionAtLeast
|
||||
optionalString nameValuePair concatMapStrings concatStringsSep optionalAttrs
|
||||
mapAttrsToList mkMerge mkIf mkDefault mkOption;
|
||||
inherit (lib.hm) dag;
|
||||
inherit (lib.my) mkOpt' dummyOption;
|
||||
@@ -50,8 +50,7 @@ in
|
||||
};
|
||||
|
||||
nix = {
|
||||
package = mkIf (!(versionAtLeast config.home.stateVersion "22.11")) pkgs.nix;
|
||||
settings = with lib.my.c.nix; {
|
||||
settings = with lib.my.c.nix; determinateSettings // {
|
||||
experimental-features = [ "nix-command" "flakes" "ca-derivations" ];
|
||||
max-jobs = mkDefault "auto";
|
||||
|
||||
@@ -257,13 +256,13 @@ in
|
||||
ssh.authKeys.files = [ lib.my.c.sshKeyFiles.me ];
|
||||
};
|
||||
|
||||
nix.package = mkIf (versionAtLeast config.home.stateVersion "22.05") pkgs.nix;
|
||||
nix.package = pkgs'.mine.determinate-nix;
|
||||
|
||||
fonts.fontconfig.enable = true;
|
||||
|
||||
home = {
|
||||
packages = with pkgs; [
|
||||
pkgs'.mine.nix
|
||||
pkgs'.mine.determinate-nix
|
||||
];
|
||||
|
||||
# Without this, we are at the mercy of whatever version of nix is in $PATH...
|
||||
|
||||
@@ -111,6 +111,13 @@ rec {
|
||||
extra-trusted-public-keys = ${concatStringsSep " " keys}
|
||||
'';
|
||||
};
|
||||
|
||||
# Determinate-specific settings enabling its performance features. Only understood by the
|
||||
# Determinate Nix binary, so they must not be emitted for a base-Nix package.
|
||||
determinateSettings = {
|
||||
lazy-trees = true;
|
||||
eval-cores = 0;
|
||||
};
|
||||
};
|
||||
|
||||
pubDomain = "nul.ie";
|
||||
@@ -307,6 +314,8 @@ rec {
|
||||
|
||||
home = rec {
|
||||
domain = "h.${pubDomain}";
|
||||
# Search domains advertised to clients (radvd DNSSL / kea domain-search)
|
||||
searchDomains = [ domain "dyn.${domain}" colony.domain britway.domain ];
|
||||
vlans = {
|
||||
hi = 100;
|
||||
lo = 110;
|
||||
@@ -383,6 +392,16 @@ rec {
|
||||
};
|
||||
};
|
||||
|
||||
# networkConfig fragment anchoring a VLAN client's DNS on the router pair's
|
||||
# VRRP VIPs rather than the RA RDNSS. v6 addresses here are RA/token-derived,
|
||||
# so when RA is absent (e.g. v6 disabled) there is no v6 and no RDNSS at all;
|
||||
# the always-present static v4 VIP keeps name resolution working, with the v6
|
||||
# VIP as a bonus when v6 is up. Merge into the VLAN network's networkConfig.
|
||||
vlanDns = vlan: {
|
||||
DNS = [ vips.${vlan}.v4 vips.${vlan}.v6 ];
|
||||
Domains = searchDomains;
|
||||
};
|
||||
|
||||
roceBootModules = [ "ib_core" "ib_uverbs" "mlx5_core" "mlx5_ib" ];
|
||||
};
|
||||
|
||||
|
||||
+5
-1
@@ -107,7 +107,7 @@ rec {
|
||||
then throw "\nFailed assertions:\n${concatStringsSep "\n" (map (x: "- ${x}") failedAssertions)}"
|
||||
else showWarnings config.warnings res;
|
||||
|
||||
homeStateVersion' = hmBranch: (if (hmBranch == "stable" || hmBranch == "mine-stable") then "22.11" else "23.05");
|
||||
homeStateVersion' = hmBranch: "23.05";
|
||||
homeStateVersion = hmBranch: {
|
||||
# The flake passes a default setting, but we don't care about that
|
||||
home.stateVersion = mkForce (homeStateVersion' hmBranch);
|
||||
@@ -149,6 +149,10 @@ rec {
|
||||
(optional (a.ipv6.gateway != null) a.ipv6.gateway);
|
||||
networkConfig = {
|
||||
IPv6AcceptRA = a.ipv6.gateway == null || a.ipv6.iid != null;
|
||||
# These are servers: temporary (privacy) addresses only rotate our stable source
|
||||
# address out from under long-lived connected sockets (e.g. nginx's resolver, which
|
||||
# wedges permanently when the address it bound to expires).
|
||||
IPv6PrivacyExtensions = "no";
|
||||
# NOTE: LLDP emission / reception is ignored on bridge interfaces
|
||||
LLDP = true;
|
||||
EmitLLDP = "customer-bridge";
|
||||
|
||||
@@ -114,8 +114,12 @@ in
|
||||
];
|
||||
also-notify = [ "127.0.0.1" ];
|
||||
allow-axfr-ips = [
|
||||
"127.0.0.0/8" "::1/128"
|
||||
"216.218.133.2" "2001:470:600::2"
|
||||
];
|
||||
]
|
||||
++ lib.my.c.home.routersPubV4
|
||||
++ lib.my.c.as211024.trusted.v4
|
||||
++ lib.my.c.as211024.trusted.v6;
|
||||
enable-lua-records = true;
|
||||
#loglevel = 7;
|
||||
#log-dns-queries = true;
|
||||
|
||||
@@ -38,6 +38,8 @@ in
|
||||
let
|
||||
inherit (lib) mkMerge;
|
||||
inherit (lib.my) networkdAssignment;
|
||||
|
||||
podmanSubnet = "10.88.0.0/16";
|
||||
in
|
||||
{
|
||||
imports = [
|
||||
@@ -158,7 +160,7 @@ in
|
||||
oci-containers = {
|
||||
backend = "podman";
|
||||
};
|
||||
containers.containersConf.settings.network.default_subnet = "10.88.0.0/16";
|
||||
containers.containersConf.settings.network.default_subnet = podmanSubnet;
|
||||
};
|
||||
|
||||
systemd.network = {
|
||||
@@ -195,7 +197,7 @@ in
|
||||
extraRules = ''
|
||||
table inet filter {
|
||||
chain forward {
|
||||
ip saddr 10.88.0.0/16 accept
|
||||
ip saddr ${podmanSubnet} accept
|
||||
}
|
||||
}
|
||||
'';
|
||||
|
||||
@@ -1,7 +1,14 @@
|
||||
{ lib, pkgs, config, ... }:
|
||||
let
|
||||
inherit (lib) mkForce;
|
||||
inherit (lib.my) net;
|
||||
inherit (lib.my.c) pubDomain;
|
||||
|
||||
# The podman bridge gateway (first host of the default subnet); job
|
||||
# containers reach the runner's artifact cache server here, through a single
|
||||
# fixed port opened in the firewall below.
|
||||
podmanGateway = net.cidr.host 1 config.virtualisation.containers.containersConf.settings.network.default_subnet;
|
||||
cachePort = 34567;
|
||||
in
|
||||
{
|
||||
config = {
|
||||
@@ -34,6 +41,11 @@ in
|
||||
cache = {
|
||||
enabled = true;
|
||||
dir = "/var/cache/gitea-runner";
|
||||
# Announce the podman bridge gateway rather than let act_runner
|
||||
# autodetect the box's outbound address, which containers can't
|
||||
# route back to.
|
||||
host = podmanGateway;
|
||||
port = cachePort;
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -73,6 +85,15 @@ in
|
||||
group = "gitea-runner";
|
||||
};
|
||||
};
|
||||
|
||||
# Let job containers reach the runner's artifact cache server on the host.
|
||||
firewall.extraRules = ''
|
||||
table inet filter {
|
||||
chain input {
|
||||
iifname "podman0" tcp dport ${toString cachePort} accept
|
||||
}
|
||||
}
|
||||
'';
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -48,8 +48,9 @@ in
|
||||
"s3-secret-key.txt"
|
||||
])
|
||||
(_: with config.services.mastodon; {
|
||||
owner = user;
|
||||
inherit group;
|
||||
# user doesn't exist any more, so this breaks on deploy
|
||||
# owner = user;
|
||||
# inherit group;
|
||||
})) // {
|
||||
"toot/pds.env" = {
|
||||
owner = "pds";
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
let
|
||||
inherit (lib.my) net;
|
||||
inherit (lib.my.c) networkd;
|
||||
inherit (lib.my.c.home) domain vlans prefixes vips roceBootModules;
|
||||
inherit (lib.my.c.home) domain vlans prefixes vips vlanDns roceBootModules;
|
||||
in
|
||||
{
|
||||
nixos.systems.castle = {
|
||||
@@ -189,8 +189,12 @@ in
|
||||
};
|
||||
"40-lan-hi" = mkMerge [
|
||||
(networkdAssignment "lan-hi" assignments.hi)
|
||||
{
|
||||
networkConfig = vlanDns "hi" // {
|
||||
# So we don't drop the IP we use to connect to NVMe-oF!
|
||||
{ networkConfig.KeepConfiguration = "static"; }
|
||||
KeepConfiguration = "static";
|
||||
};
|
||||
}
|
||||
];
|
||||
"45-lan-lo" = {
|
||||
matchConfig.Name = "lan-lo";
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
let
|
||||
inherit (lib.my) net mkVLAN;
|
||||
inherit (lib.my.c) pubDomain;
|
||||
inherit (lib.my.c.home) domain vlans prefixes vips hiMTU;
|
||||
inherit (lib.my.c.home) domain vlans prefixes vips vlanDns hiMTU;
|
||||
in
|
||||
{
|
||||
imports = [ ./vms ];
|
||||
@@ -203,7 +203,10 @@ in
|
||||
MACAddress=52:54:00:90:34:95
|
||||
'';
|
||||
};
|
||||
"60-lan-hi" = networkdAssignment "lan-hi" assignments.hi;
|
||||
"60-lan-hi" = mkMerge [
|
||||
(networkdAssignment "lan-hi" assignments.hi)
|
||||
{ networkConfig = vlanDns "hi"; }
|
||||
];
|
||||
|
||||
"50-lan-core-phy" = {
|
||||
matchConfig.Name = "lan-core-phy";
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
let
|
||||
inherit (lib.my) net;
|
||||
inherit (lib.my.c) pubDomain;
|
||||
inherit (lib.my.c.home) domain prefixes vips hiMTU;
|
||||
inherit (lib.my.c.home) domain prefixes vips vlanDns hiMTU;
|
||||
in
|
||||
{
|
||||
nixos.systems.cellar = {
|
||||
@@ -79,7 +79,10 @@ in
|
||||
};
|
||||
|
||||
networks = {
|
||||
"80-lan-hi" = networkdAssignment "lan-hi" assignments.hi;
|
||||
"80-lan-hi" = mkMerge [
|
||||
(networkdAssignment "lan-hi" assignments.hi)
|
||||
{ networkConfig = vlanDns "hi"; }
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
let
|
||||
inherit (lib.my) net;
|
||||
inherit (lib.my.c) pubDomain;
|
||||
inherit (lib.my.c.home) domain prefixes vips hiMTU;
|
||||
inherit (lib.my.c.home) domain prefixes vips vlanDns hiMTU;
|
||||
in
|
||||
{
|
||||
nixos.systems.hass = { config, ... }: {
|
||||
@@ -82,7 +82,10 @@ in
|
||||
|
||||
systemd = {
|
||||
network.networks = {
|
||||
"80-container-host0" = networkdAssignment "host0" assignments.hi;
|
||||
"80-container-host0" = mkMerge [
|
||||
(networkdAssignment "host0" assignments.hi)
|
||||
{ networkConfig = vlanDns "hi"; }
|
||||
];
|
||||
"80-container-lan-lo" = networkdAssignment "lan-lo" assignments.lo;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{ lib, ... }:
|
||||
let
|
||||
inherit (lib.my) net;
|
||||
inherit (lib.my.c.home) domain prefixes vips hiMTU;
|
||||
inherit (lib.my.c.home) domain prefixes vips vlanDns hiMTU;
|
||||
in
|
||||
{
|
||||
nixos.systems.unifi = { config, ... }: {
|
||||
@@ -58,7 +58,10 @@ in
|
||||
|
||||
systemd = {
|
||||
network.networks = {
|
||||
"80-container-host0" = networkdAssignment "host0" assignments.hi;
|
||||
"80-container-host0" = mkMerge [
|
||||
(networkdAssignment "host0" assignments.hi)
|
||||
{ networkConfig = vlanDns "hi"; }
|
||||
];
|
||||
"80-lan-core" = networkdAssignment "lan-core" assignments.core;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{ lib, ... }:
|
||||
let
|
||||
inherit (lib.my) net;
|
||||
inherit (lib.my.c.home) domain prefixes vips hiMTU roceBootModules;
|
||||
inherit (lib.my.c.home) domain prefixes vips vlanDns hiMTU roceBootModules;
|
||||
in
|
||||
{
|
||||
imports = [ ./containers ];
|
||||
@@ -134,8 +134,10 @@ in
|
||||
networks = {
|
||||
"30-lan-hi" = mkMerge [
|
||||
(networkdAssignment "lan-hi" assignments.hi)
|
||||
{
|
||||
# So we don't drop the IP we use to connect to NVMe-oF!
|
||||
{ networkConfig.KeepConfiguration = "static"; }
|
||||
networkConfig = vlanDns "hi" // { KeepConfiguration = "static"; };
|
||||
}
|
||||
];
|
||||
"30-lan-hi-ctrs" = {
|
||||
matchConfig.Name = "lan-hi-ctrs";
|
||||
|
||||
@@ -170,6 +170,12 @@ in
|
||||
"0.0.0.0:5353" "[::]:5353"
|
||||
];
|
||||
also-notify = [ "127.0.0.1" ];
|
||||
allow-axfr-ips = [
|
||||
"127.0.0.0/8" "::1/128"
|
||||
allAssignments.estuary.internal.ipv4.address
|
||||
]
|
||||
++ lib.my.c.as211024.trusted.v4
|
||||
++ lib.my.c.as211024.trusted.v6;
|
||||
enable-lua-records = true;
|
||||
# loglevel = 7;
|
||||
# log-dns-queries = true;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
index: { lib, pkgs, config, assignments, allAssignments, ... }:
|
||||
let
|
||||
inherit (lib) mkForce;
|
||||
inherit (lib) mkForce concatStringsSep;
|
||||
inherit (lib.my) net netbootKeaClientClasses;
|
||||
inherit (lib.my.c.home) domain prefixes vips hiMTU;
|
||||
inherit (lib.my.c.home) domain searchDomains prefixes vips hiMTU;
|
||||
|
||||
dns-servers = [
|
||||
{
|
||||
@@ -59,7 +59,7 @@ in
|
||||
}
|
||||
{
|
||||
name = "domain-search";
|
||||
data = "${domain}, dyn.${domain}, ${lib.my.c.colony.domain}, ${lib.my.c.britway.domain}";
|
||||
data = concatStringsSep ", " searchDomains;
|
||||
always-send = true;
|
||||
}
|
||||
];
|
||||
|
||||
@@ -24,7 +24,7 @@ in
|
||||
services = {
|
||||
networkd-dispatcher.rules = {
|
||||
configure-mstpd = {
|
||||
onState = [ "routable" ];
|
||||
onState = [ "configured" ];
|
||||
script = ''
|
||||
#!${pkgs.runtimeShell}
|
||||
if [ "$IFACE" = "lan" ]; then
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
index: { lib, pkgs, ... }:
|
||||
let
|
||||
inherit (lib) mkForce concatMapStringsSep;
|
||||
inherit (lib) mkForce concatMapStringsSep concatStringsSep;
|
||||
inherit (lib.my) net;
|
||||
inherit (lib.my.c.home) domain prefixes vips;
|
||||
inherit (lib.my.c.home) domain searchDomains prefixes vips;
|
||||
|
||||
# untrusted uses external (Cloudflare) resolvers, matching the v4 kea config;
|
||||
# trusted VLANs use the internal recursor via its floating VRRP VIP
|
||||
@@ -18,7 +18,7 @@ let
|
||||
AdvLinkMTU ${toString prefixes."${name}".mtu};
|
||||
prefix ${prefixes."${name}".v6} {};
|
||||
RDNSS ${rdnss name} {};
|
||||
DNSSL ${domain} dyn.${domain} ${lib.my.c.colony.domain} ${lib.my.c.britway.domain} {};
|
||||
DNSSL ${concatStringsSep " " searchDomains} {};
|
||||
};
|
||||
'';
|
||||
in
|
||||
|
||||
@@ -115,10 +115,7 @@
|
||||
Name = "lan";
|
||||
Kind = "bridge";
|
||||
};
|
||||
extraConfig = ''
|
||||
[Bridge]
|
||||
STP=true
|
||||
'';
|
||||
bridgeConfig.STP = true;
|
||||
};
|
||||
};
|
||||
links = {
|
||||
@@ -175,10 +172,12 @@
|
||||
"50-lan-jim" = {
|
||||
matchConfig.Name = "lan-jim";
|
||||
networkConfig.Bridge = "lan";
|
||||
bridgeConfig.Cost = 100;
|
||||
};
|
||||
"50-lan-dave" = {
|
||||
matchConfig.Name = "lan-dave";
|
||||
networkConfig.Bridge = "lan";
|
||||
bridgeConfig.Cost = 10;
|
||||
};
|
||||
|
||||
"50-wan-ifb" = {
|
||||
|
||||
@@ -50,9 +50,9 @@ in
|
||||
};
|
||||
|
||||
nix = {
|
||||
package = pkgs'.mine.nix;
|
||||
package = pkgs'.mine.determinate-nix;
|
||||
channel.enable = false;
|
||||
settings = with lib.my.c.nix; {
|
||||
settings = with lib.my.c.nix; determinateSettings // {
|
||||
trusted-users = [ "@wheel" ];
|
||||
experimental-features = [ "nix-command" "flakes" "ca-derivations" ];
|
||||
extra-substituters = cache.substituters;
|
||||
|
||||
@@ -19,4 +19,12 @@ in
|
||||
update-docs-options = pkgs.writeShellScriptBin "update-docs-options" ''
|
||||
exec ${pkgs.python3}/bin/python3 ${../ci/update-docs-options.py} "$@"
|
||||
'';
|
||||
|
||||
update-docs-dns =
|
||||
let
|
||||
python = pkgs.python3.withPackages (ps: [ ps.dnspython ]);
|
||||
in
|
||||
pkgs.writeShellScriptBin "update-docs-dns" ''
|
||||
exec ${python}/bin/python3 ${../ci/update-docs-dns.py} "$@"
|
||||
'';
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user