# DNS strategy + run modes

How names resolve and which layers come up. Supersedes the "dnsmasq is core"
assumption in `01-dnsmasq.md`.

## Domain: `.lan`, not `.local`

`BASE_DOMAIN=ulicraft.lan` by default. `.local` is intercepted by mDNS on
macOS/Linux and does **not** resolve through a normal unicast DNS server
(that was the `DNS_PROBE_FINISHED_NXDOMAIN`). Use `.local` only with the mDNS
path below.

## Run modes — `tooling/build-stack.sh --up <mode>`

| Mode | Compose layers | Use |
|---|---|---|
| `online` | base + static | internet present; landing + launcher; no mirror |
| `airgap` | base + static + mirror | no internet; full asset mirror on `:443` (default) |
| `core` | base | debugging |

Base = drasl, minecraft, mc-backup, caddy(auth+packwiz). `static` = apex landing +
launcher + `/ca.crt`. `mirror` = upstream spoof vhosts (`tls internal`) + `:443` +
caddy aliases. `--up` prints the DNS records for the chosen mode.

## DNS: your own party server (primary)

You run the party DNS. Generate records with `tooling/dns-records.sh <mode>`:
- `online` → service names (`${BASE_DOMAIN}`, `auth.`, `packwiz.`, `mc.`) → `HOST_LAN_IP`
- `airgap` → the above **plus** the spoofed upstreams (`meta.prismlauncher.org`,
  `piston-meta`/`piston-data.mojang.com`, `libraries.minecraft.net`,
  `maven.neoforged.net`, `resources.download.minecraft.net`) → `HOST_LAN_IP`

Output formats: plain, `/etc/hosts`, BIND zone, dnsmasq `address=`, plus the
Minecraft SRV line. Minecraft needs no port (`mc.${BASE_DOMAIN}` defaults to 25565).

## Optional resolver layers

- **dnsmasq** (`docker-compose.dnsmasq.yml`) — turnkey DNS if you don't run your
  own. Binds `${HOST_LAN_IP}:53`. Serves the rendered `dnsmasq/dnsmasq.conf`
  (wildcard + airgap spoofs).
- **mDNS / Avahi** (`docker-compose.avahi.yml`, `ENABLE_MDNS=true`) — zero-config
  `.local` for guests (macOS/Linux native; Windows needs Bonjour). See below.

## mDNS path (ENABLE_MDNS=true + BASE_DOMAIN=*.local)

The `avahi` container publishes the service names to the **host's** avahi-daemon
over its D-Bus socket (it does NOT run its own daemon — that collides with the
host on `:5353`).

Requirements / gotchas (build-stack warns on these):
- host runs `avahi-daemon`; `/run/dbus/system_bus_socket` mounted in.
- `security_opt: apparmor=unconfined` on the container (docker-default AppArmor
  blocks the D-Bus publish).
- multi-interface hosts (e.g. many docker bridges) → avahi self-collides
  ("Local name collision"); set `allow-interfaces=<lan-nic>` in
  `/etc/avahi/avahi-daemon.conf`.
- mDNS resolves **only** `.local`, so it cannot serve the air-gap upstream
  spoofs (real public hostnames) — those always need real DNS.

## Status (tested 2026-05-24)

- `.lan` + own-DNS path: verified end-to-end (apex/auth/packwiz/ca.crt/mirror
  all 200, minecraft healthy).
- mDNS publish mechanism: verified reaching host avahi; full green-light blocked
  on this dev box only by its 38 docker bridges (allow-interfaces fixes it).
