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`.
|
||||
+1
-10
@@ -108,16 +108,7 @@ no longer deployed sidecars here. See Recent decisions.)
|
||||
(no NOPASSWD)** — stage model pulls to `/home`, not root-owned `/worktank`.
|
||||
## Current state / in-flight
|
||||
|
||||
- **🟡 ESH IPv6 LAN naming — a DOCS CONVENTION ONLY, not wire-level.** Fun hexspeak names picked 2026-08-18 for the six ESH LANs: **RESERVED for a future DMZ** = **4411:DBAD** ("FOR ALL DA BAD" — no DMZ network exists on the ESH UDM yet; name claimed 2026-08-19 against the day one is built). `esh-cameras`=**1533:FACE5** ("I SEE FACES" — operator's pick 2026-08-19, and the best of the set), `esh-mgmt`=**15DA:B055** ("IS DA BOSS" — 15=IS, DA literal, B055=BOSS; revised 2026-08-19), `esh-server`=**4411:B105** ("FOR ALL BIOS" — operator's pick 2026-08-19; shares the 411 with userland's CAFE:4411), `esh-userland`=**CAFE:4411** ("CAFE FOR ALL" — operator's pick 2026-08-19; 411 also being US directory assistance is a bonus), `esh-iot`=**4DBA:D107** ("FOR DA BAD IOT" — 4=FOR, D=DA, BAD, 107=IOT; same 8 digits, re-glossed 2026-08-19), Default=**4BA5:3417** ("A BASE FOR IT" — operator's pick 2026-08-19; 8 digits, words straddle the group boundary like `4DBA:D107`). **Investigated and confirmed these CANNOT be made literal on the wire**: (1) a network's only nameable slot is its `/64` subnet id (`ipv6_pd_prefixid`), which is 2 hex digits (0-255) — not enough room to spell a 4-char word; (2) the gateway's own address is fixed at `::1` by the UniFi platform, no field to customize it (`ipv6_aliases` exists but schema unknown, not attempted blind); (3) UniFi has **no IPv6 equivalent of `use_fixedip`/`fixed_ip`** (checked the client schema directly) — individual devices cannot be pinned to a chosen v6 address the way v4 static reservations work; SLAAC devices self-assign via EUI-64 (MAC-derived, stable) or privacy-extension (random, rotates). So the names are a mnemonic for humans/docs, not something that will ever appear in a `dig`/`ip -6` output. Only `esh-cameras` has v6 live at all (`2607:73c0:402:1d00::/64`, PD from the ESH `/56`); the other five stay `ipv6_interface_type: none` pending a firewall-policy pass before enabling SLAAC fleet-wide (LAN devices would get globally reachable addresses).
|
||||
|
||||
**♻ RESTORED 2026-08-24** — this entry was silently dropped by the `memory: snapshot` commit `837fa36` and took a long hunt to recover from `8be8a51`. It is a naming convention, not temporal state: **do not prune it in a snapshot.**
|
||||
|
||||
⚠ **The "cannot ever appear in a dig/ip -6 output" claim above is now PARTLY WRONG and should not be trusted.** Points (1) and (2) still hold — UniFi cannot name a subnet id or the gateway address. But point (3) only means *UniFi* cannot assign a host address; **any Linux host can simply take one itself**, and that makes the names real on the wire. Verified 2026-08-24 on esh-docker-vm: `/etc/network/interfaces` sources `interfaces.d/`, and an `/etc/network/if-up.d/` hook can derive the live prefix and `ip -6 addr replace` a scheme address at boot without touching existing config or disturbing SLAAC.
|
||||
|
||||
**First real use, pending operator go-ahead:** AdGuard on `esh-server` at `<prefix>:4411:b105:50:45` — "FOR ALL BIOS" plus `10.0.50.45` read straight off the address. That is also the durable fix for the IPv6 resolver the ESH UDM now advertises via RDNSS, which currently points at a MAC-derived SLAAC address that dies if the NIC changes.
|
||||
|
||||
_As of 2026-08-23 — a long multi-party ops session. The Anaheim tunnel cipher is now settled (closed: the remedy does not exist). The one thing still open with the operator is how long WAN admin on ana-gw stays open._
|
||||
|
||||
- **🟢 ESH IPv6 LAN naming scheme — now a real doc, and LIVE on one host.** Six ESH LANs carry an 8-hex-digit phrase in the interface identifier (`esh-server`=`4411:B105` "FOR ALL BIOS", `esh-mgmt`=`15DA:B055` "IS DA BOSS", `esh-userland`=`CAFE:4411`, `esh-iot`=`4DBA:D107`, `esh-cameras`=`1533:FACE5`, `Default`=`4BA5:3417`, plus `4411:DBAD` reserved for a future DMZ). **Canonical: [`docs/pfi/ipv6-naming-scheme.md`](docs/pfi/ipv6-naming-scheme.md)** — full table, address structure, what UniFi can and cannot name, and the `if-up.d` recipe. ⚠ This entry was once the ONLY record and a `memory: snapshot` (`837fa36`) deleted it; it took a transcript hunt to recover. It is a convention, not temporal state — **the doc is now the home; do not let this line become the only copy again.** First live use 2026-08-24: AdGuard on esh-docker-vm at `2607:73c0:402:1d02:4411:b105:50:45`, which is what the ESH UDM advertises to clients as its IPv6 resolver.
|
||||
- **🔒 ana-gw WAN admin is CLOSED again (2026-08-23, operator-directed) and the FortiGate is scheduled for replacement.** The cutover contingency window is over: `wan1 allowaccess` is back to **`ping` only** (https + ssh removed) and `infra-ops` trusthost is back to **10.0.0.0/8 only** — verified from two sites that 443 and 22 are closed, and that management still works over the tunnel at 10.250.0.1. **There is no longer any out-of-band path to ana-gw**; if both tunnels drop, it is console-only. Re-open = `set allowaccess ping https` on wan1 plus widening the infra-ops trusthost (both one-liners, recorded in auto-memory). ⚠ **Port 80 on 38.120.12.42 is still open and it IS the FortiGate** — the FortiOS **ACME HTTP-01 challenge listener**, opened by `config system acme / set interface "wan1"`, which **bypasses `allowaccess` by design**. Body is literally `ACME Access Only` (403, 101 bytes) for every path; confirmed by sniffer showing `wan1 out 38.120.12.42.80 -> ...: syn ack`. Not a DNAT (only two VIPs exist on .42: Kokoro `:8880`, wg `:31337`) and not an admin surface — it serves only challenge tokens. Closing it = `config system acme` unset interface, which also ends cert renewal.
|
||||
|
||||
- **✅ CLOSED 2026-08-23: the Anaheim tunnel "problem" is mostly a measurement artefact, and the AES-GCM cutover is impossible.** Operator authorised the cutover; it was attempted NH3-side-first and **cannot be done — UniFi's manual site-to-site IPsec implements no AES-GCM** (8 spellings rejected `api.err.InvalidPayload` against a passing `aes256` control; accepted enum is `aes128/aes192/aes256/3des` only). This blocks the ESH tunnel too, since both far ends are UDMs. The framing was also wrong twice over: **NH3's uplink is 1 Gbps** (not Anaheim's 2 Gbps — that is the real ceiling), and the tunnel does **692 Mbit/s at 8 streams** (the original stopped at 4 and reported ~550). Against WireGuard on the same UDM and uplink, the gap collapses from 2.3× at one stream to **15% at eight** — so re-architecting onto WireGuard is not worth it. Real constraint = **per-stream ~245 Mbit/s**, both endpoints idle. **Standing mitigation: parallelise bulk transfers (2.8× for free); for single-stream NFS use `nconnect=N`** — the `/mnt/smithy` mount on ana-ml2 at 24.7 MB/s is exactly this case and is the obvious test. FortiGate phase2 `pfi-ana-nh3` was left widened to `aes256-sha1 aes256gcm` (inert while the peer offers only CBC); UDM verified byte-identical to its pre-change snapshot. → `persistent-memory.d/2026-08-23-anaheim-ipsec-tunnel-ceiling.md`
|
||||
|
||||
Reference in New Issue
Block a user