Skip to content
Hallway is out (npm i -g hallway): v0, v1 and v2 have shipped, apart from checking it all on a real Mac. See the roadmap.

Custom domains

v1 The default base domain, localhost, needs no DNS at all. If you want your own namespace instead, say https://slides.myname.internal, set a custom base domain:

Terminal window
hallway setup --domain myname.internal --print # see the plan first
hallway setup --domain myname.internal

With a custom base, the dashboard lives at the bare apex (https://myname.internal) and apps live underneath it (https://slides.myname.internal). Everything in fronted mode applies; a custom domain only adds a way to resolve the names.

Use a name under .internal. ICANN reserved .internal for private use in 2024, so it will never be delegated on the public internet. The domain needs at least two labels (myname.internal, not internal).

Base domainVerdictWhy
localhost✅ The defaultNo DNS, reserved forever (RFC 6761).
*.internal✅ Recommended custom choiceReserved by ICANN for private use; never delegated.
*.test✅ FineReserved (RFC 6761).
*.home.arpa✅ FineMeant for home networks (RFC 8375). Long.
A made-up TLD, e.g. name.surname⚠️ Allowed, with a warningNot reserved: someone could register that TLD later.
A name under another real TLD, e.g. dev.example.com⚠️ Allowed, with a warningFine if it’s a domain you own; otherwise you’d be shadowing someone else’s names on this machine. Certificates still come from Hallway’s local CA, never a public one.
*.local❌ RefusedReserved for mDNS/Bonjour (RFC 6762). macOS sends .local lookups to mDNS, and wildcards don’t work.
*.dev, *.app and the other TLDs on browsers’ HSTS preload list❌ RefusedReal, delegated TLDs on which browsers force HTTPS; the old “.dev for local development” setups all broke when it was delegated.
*.example, *.invalid❌ RefusedReserved for documentation and for names that must never resolve.

Every name gets its certificate from Hallway’s local certificate authority, so .test and made-up TLDs never go to a public certificate authority.

They work. Nothing stops you resolving names under an undelegated TLD on your own machine, and if that TLD is delegated later, your setup keeps working: your machine’s resolver configuration wins, and since fronted mode is always HTTPS with a locally trusted CA, the .dev-style HSTS failure doesn’t apply. You only lose the ability to reach the real public names.

But ICANN’s 2026 round of new top-level domains is under way, so hallway setup and hallway doctor check your TLD against IANA’s list of delegated TLDs (cached for a week in the data home) and warn if it has become real. Prefer .internal.

Unlike *.localhost, a custom domain needs something to answer DNS for it. Hallway runs its own dnsmasq, as you, and points your system resolver at it for that one domain:

  1. dnsmasq, if it is missing: on macOS, setup installs it with Homebrew (brew install dnsmasq, as you, no sudo). On Linux, install it yourself (on Debian and Ubuntu the dnsmasq-base package, which starts no system service), then run setup again.

  2. Its configuration, ~/.hallway/generated/dnsmasq.conf, answers the apex and every name under it with your own machine, a wildcard, so adding an app never touches DNS again, and answers nothing else:

    port=4253
    listen-address=127.0.0.1
    bind-interfaces
    no-resolv
    no-hosts
    address=/myname.internal/127.0.0.1
    address=/myname.internal/::1

    listen-address and bind-interfaces keep it on loopback, so it can’t become an open resolver on your network.

  3. It runs as a user-level job, hallway.dnsmasq (a LaunchAgent on macOS, a systemd user unit on Linux), on 127.0.0.1:4253 (domain.dnsPort in config.yaml). It never needs root, because it doesn’t use port 53.

  4. The resolver entry, the one privileged step a custom domain adds, printed and run through sudo:

    • macOS: /etc/resolver/myname.internal, containing nameserver 127.0.0.1 and port 4253. The file is named after the full base domain, never just the TLD, so only your own names are captured. It covers the apex as well as subdomains.
    • Linux: a systemd-resolved drop-in, /etc/systemd/resolved.conf.d/hallway.conf, routing ~myname.internal to 127.0.0.1:4253. Other resolvers aren’t supported by setup.
  5. Sets domain.base (and mode: fronted) in config.yaml.

hallway doctor then checks dnsmasq is running and bound to loopback only, the resolver entry is in place, and both the apex and a name under it resolve to your machine. By hand: scutil --dns and dscacheutil -q host -a name slides.myname.internal on macOS, resolvectl query slides.myname.internal on Linux.

hallway teardown removes the resolver entry and the dnsmasq job.

Why not /etc/hosts, as some tools do? It needs sudo for every new app and can’t express wildcards. A resolver entry needs sudo once.