docs: give the ESH IPv6 naming scheme a home, and make it real on one host
The scheme has existed since August as a single line of persistent memory, which a snapshot then deleted. It is a naming convention rather than temporal state, so it now lives in docs/pfi as a proper document, and the memory entry is reduced to a pointer at it. The document carries the full table, the address structure, the reasoning about which slots can and cannot hold a name, and the recipe for applying one to a host. It also corrects the conclusion the original note ended on. That note held that these names could never appear on the wire, which is true of everything UniFi is able to assign but not of what a host can assign to itself, and the distinction is the whole difference between a joke and an address. AdGuard on esh-docker-vm now holds the esh-server name, at 2607:73c0:402:1d02:4411:b105:50:45, where the segment identity and the IPv4 address are both legible. It is applied by an if-up.d hook that derives the prefix at runtime rather than hardcoding it, backgrounds itself with a retry so it cannot stall interface bring-up, and adds nothing to the existing interface configuration. This is load-bearing rather than decorative. The gateway advertises an IPv6 resolver to clients, macOS prefers it over the IPv4 one, and it previously pointed at an address derived from that host's MAC.
This commit is contained in:
@@ -0,0 +1,133 @@
|
||||
# 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
|
||||
|
||||
| host | address | status |
|
||||
|---|---|---|
|
||||
| `esh-docker-vm` (AdGuard) | `2607:73c0:402:1d02:4411:b105:50:45` | **live** 2026-08-24 |
|
||||
|
||||
This is not decorative. The ESH UDM advertises an IPv6 resolver to clients via
|
||||
RDNSS, and 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 break if that
|
||||
VM's NIC ever changed. It now points at the scheme address, which will not.
|
||||
|
||||
Both `esh-userland` and `esh-server` advertise it
|
||||
(`dhcpdv6_dns_auto=false` + `dhcpdv6_dns_1=<address>`); verified on the wire by
|
||||
soliciting an RA and parsing option type 25. See
|
||||
[`reference_unifi_dns_rdnss_limits`] in auto-memory for why that field is only
|
||||
honoured when an explicit server is supplied.
|
||||
|
||||
Natural next candidates, both on `esh-server` and both real Linux hosts:
|
||||
`esh-pve-nas` → `…:4411:b105:50:55`, `esh-vm-db` → `…:4411:b105:50:60`.
|
||||
Reference in New Issue
Block a user