# ESH IPv6 naming scheme Every ESH LAN carries an **eight-hex-digit phrase** as the first half of the interface identifier. Picked 2026-08-18/19. This file is the canonical record. > **Why this file exists.** The scheme originally lived as a single line in > `persistent-memory.md` and was silently deleted by a `memory: snapshot` > commit (`837fa36`). Recovering it took a hunt through session transcripts to > find the commit that had held it. A naming convention is not temporal state — > it belongs in a document, so it now is one. ## The names | network | hex | reads as | |---|---|---| | `Default` | **`4BA5:3417`** | A BASE FOR IT | | `esh-mgmt` | **`15DA:B055`** | IS DA BOSS | | `esh-server` | **`4411:B105`** | FOR ALL BIOS | | `esh-userland` | **`CAFE:4411`** | CAFE FOR ALL | | `esh-iot` | **`4DBA:D107`** | FOR DA BAD IOT | | `esh-cameras` | **`1533:FACE5`** | I SEE FACES | | *(reserved)* DMZ | **`4411:DBAD`** | FOR ALL DA BAD | The DMZ name is **claimed against a network that does not exist yet** — there is no DMZ on the ESH UDM. Do not reuse it. Substitutions are the standard hexspeak set: `0`→O, `1`→I/L, `5`→S, plus letters that are already native hex (`A`–`F`). Anything outside `0-9a-f` is not expressible — `b0ss` and `c00l` do **not** work, which is why the set above uses `B055` and avoids `c00l` entirely. House style, arrived at rather than designed: **eight digits, and a complete phrase rather than a single word.** Words are allowed to straddle the group boundary (`4DBA:D107` is `4·D·BAD·107`); the phrase reads through the colon. ## Address structure ``` 2607:73c0:402:1d02 : 4411:b105 : 50 : 45 └──── ISP /64 ────┘ └ segment ─┘ └ 10.0.50.45 ┘ ``` - **Prefix** — Cityside's, not ours to name. ESH holds a `/56` (`2607:73c0:402:1d00::/56`, 256 × /64); the subnet id is assigned by UniFi's `ipv6_pd_prefixid`. `esh-cameras` is `1d00`, `esh-server` is `1d02`. - **Segment word pair** — 32 bits, from the table above. - **Host** — the last two IPv4 octets, written as literal digits so they read straight off the address. `10.0.50.45` → `:50:45`. The scheme lives entirely in the **interface identifier**, so it is **delegation-size independent**. It works identically on a `/56`, a `/48`, or NH3's single `/64`. It never competes with the subnet id, which is far too small to hold a word (8 bits at ESH — two hex digits). Note: `4411:b105:50:45` fills all four host groups, so there is **no `::`** in these addresses. Writing `...::4411:b105:50:45` is malformed and will be rejected. ## What can and cannot carry a name | slot | nameable? | |---|---| | `/64` subnet id (`ipv6_pd_prefixid`) | **No** — 8 bits at ESH, two hex digits, no room for a word | | the gateway's own address | **No** — fixed at `::1` by UniFi, no field for it | | a UniFi *client* reservation | **No** — UniFi has no IPv6 equivalent of `use_fixedip` | | **a host taking its own address** | **Yes** — this is the one that works | ⚠ **The original note concluded these names could never appear in a `dig` or `ip -6` output. That is wrong.** The first three rows are correct, but they only establish that *UniFi* cannot assign the address. A Linux host can simply take one within its own advertised prefix, and the router gets no vote. Appliances with no shell — cameras, most IoT — genuinely cannot, so `1533:FACE5` and `4DBA:D107` are likely to stay documentation-only. ## Applying it to a host Do **not** use an `iface … inet6 static` stanza: on Debian that sets `accept_ra=0`, killing SLAAC and the IPv6 default route — a good way to strand a headless box. Use an `if-up.d` hook that derives the live prefix instead. Live example, `/etc/network/if-up.d/ipv6-scheme-addr` on `esh-docker-vm`: ```sh #!/bin/sh [ "$IFACE" = ens18 ] || exit 0 ( i=0 while [ $i -lt 30 ]; do PFX=$(ip -6 -o addr show dev "$IFACE" scope global 2>/dev/null \ | awk '{print $4}' | cut -d/ -f1 | head -1 | cut -d: -f1-4) if [ -n "$PFX" ]; then ip -6 addr replace "${PFX}:4411:b105:50:45/64" dev "$IFACE" && exit 0 fi sleep 2 i=$((i + 1)) done ) >/dev/null 2>&1 & exit 0 ``` Three deliberate properties: - **The prefix is derived, never hardcoded** — self-heals if Cityside re-delegates. - **Backgrounded with a retry** — SLAAC may not have landed when `if-up.d` runs, and a hook that blocks or fails would stall interface bring-up. - **Additive** — `/etc/network/interfaces` already sources `interfaces.d/`; nothing existing is edited, and removal is one `rm`. Remaining gap: a *mid-life* prefix change is only picked up at the next interface-up. A timer would close it; not worth building until the prefix is observed to actually move. ## Deployed All three Linux hosts on `esh-server` now carry the segment name, with the last two groups reading straight off their IPv4 address: | host | address | v4 | applied via | |---|---|---|---| | `esh-docker-vm` (AdGuard) | `2607:73c0:402:1d02:4411:b105:50:45` | 10.0.50.45 | `if-up.d` on `ens18` | | `esh-pve-nas` | `2607:73c0:402:1d02:4411:b105:50:55` | 10.0.50.55 | `if-up.d` on `vmbr0` | | `esh-vm-db` | `2607:73c0:402:1d02:4411:b105:50:60` | 10.0.50.60 | `if-up.d` on `ens18` | `esh-docker-vm`'s is load-bearing, not decorative: the ESH UDM advertises an IPv6 resolver to clients via RDNSS, macOS prefers it over the DHCPv4-supplied one, so whatever sits there is what resolves `*.internal` for every Mac on the network. It previously pointed at AdGuard's **MAC-derived SLAAC address**, which would have broken if that VM's NIC ever changed. Both `esh-userland` and `esh-server` now advertise the scheme address instead (`dhcpdv6_dns_auto=false` + `dhcpdv6_dns_1=
`), verified on the wire by soliciting an RA and parsing option type 25. ### ⚠ Proxmox bridges need `accept_ra=2` or SLAAC never runs `esh-pve-nas` had **link-local only** despite `accept_ra=1`, `autoconf=1` and IPv6 enabled — every sysctl looked correct. The cause: **`vmbr0.forwarding = 1`** (Proxmox sets per-interface forwarding on bridges), and the kernel ignores RAs on a forwarding interface unless `accept_ra` is explicitly **`2`**. `accept_ra=1` means "accept only if not forwarding", so it silently did nothing. Fixed in `/etc/sysctl.d/60-ipv6-accept-ra.conf` on that host: ``` net.ipv6.conf.vmbr0.accept_ra = 2 net.ipv6.conf.vmbr0.accept_ra_defrtr = 0 ``` `accept_ra_defrtr=0` is deliberate — it takes the advertised **prefix** (so SLAAC configures an address) while **declining the default route**, so a hypervisor gains an IPv6 identity with no change to its routing behaviour. Verified after: SLAAC address present, v6 default routes still **0**, v4 intact. Expect the same on any other Proxmox node when its LAN gets IPv6. ### Getting into a host with no direct root `esh-vm-db` refuses key auth for `root` and `infra-ops`, and `lkraven`'s sudo wants a password. It is VMID 101 on `esh-pve`, and the **QEMU guest agent** runs as uid 0 inside it, so the hook was installed with: ``` qm guest exec 101 -- /bin/sh -c 'echo | base64 -d > /etc/network/if-up.d/... ' ``` base64 because quoting a multi-line script through two SSH layers mangles it. Worth remembering as the general path for guests whose credentials are not vaulted. Every Linux host on `esh-server` now carries its name. The remaining ESH segments have no eligible hosts: `esh-cameras` and `esh-iot` are appliances with no shell, and `esh-mgmt`, `esh-userland` and `Default` are still `ipv6_interface_type: none` pending the firewall-policy pass — enabling SLAAC there gives every client a globally reachable address.