Skip to content

Endpoint Resolution

A hardcoded IP address is a time bomb with a long fuse. It works perfectly until the day the LAN shifts subnets, the router is swapped, or a Tailscale node re-authenticates with a new address — and then a dozen scripts fail at once, quietly, at 2 a.m. Sanctum has been burned by this enough times that it now treats a literal IP in code the way it treats a hardcoded password.

The doctrine is one sentence: every address a Sanctum service contacts is resolved at runtime from a single source of truth — never written as a literal in code or config.

  • 2026-04-26 — the Firewalla LAN shifted from 192.168.1.x to a new subnet. Screen-time enforcement contacted a hardcoded gateway and silently went offline for 24+ hours. Nobody noticed until a kid did.
  • 2026-06 — the haus moved to a Gold Pro firewall on 10.0.0.0/24. Overnight the Mac Mini’s LAN IP drifted again (10.0.0.24810.0.0.10). Every 192.168.1.x literal was already wrong; cert SANs asserted addresses the servers no longer bound to; a mDNS proxy was advertising a dead host.

In both cases the failure was invisible because the literal was plausible — a real IP, just the wrong one. The resolver makes the wrong-but-plausible state impossible.

Resolve

sanctum-endpoints discovers live addresses and writes them to a generated file every service reads. The map is never hand-edited.

Enforce

The no-hardcoded-ip lint fails any commit that reintroduces a literal. Discovery without enforcement just rots again.

~/.sanctum/bin/sanctum-endpoints reads instance.yaml for names and virtual anchors only, then resolves them to live addresses. Every source is dynamic:

AddressHow it’s resolved
Own LAN IPthe default-route interface (route get defaultipconfig getifaddr) — never a hardcoded interface
Firewalla boxthe bridge’s own discovery endpoint (GET :1984/health.discovery.ip)
Tailscale peerstailscale status --json, matched by MagicDNS DNSName stem (not the messy display name), gated on Online — an offline node resolves to null, not a stale address
Loopback / co-resident services127.0.0.1 (stable by definition)
vmnet bridge / VMthe 10.10.10.x anchors from instance.yaml — addresses we assign on a private vmnet, so they never drift

It writes two artifacts, atomically and content-hash idempotent (identical inputs produce a byte-identical file, so re-running never churns):

  • ~/.sanctum/endpoints.json — structured, for Python / Rust / TypeScript consumers.
  • ~/.sanctum/endpoints.env — flat KEY=value, sourced by shell.

Commands: generate (default), check (exit 1 on drift vs live), get <KEY>. A launchd job regenerates on network change (WatchPaths) and every ten minutes, so the map self-heals.

Terminal window
# shell — source the helper, resolve with a stable-anchor fallback
source ~/.sanctum/lib/config.sh
VM=$(sanctum_endpoint SANCTUM_VM_BRIDGE) # 10.10.10.10 (ip-allow: doc example output)
TS=$(sanctum_endpoint SANCTUM_MANOIR_TS) # 100.x, current
import json, os
ep = json.load(open(os.path.expanduser("~/.sanctum/endpoints.json")))
manoir_lan = ep["hosts"]["manoir"]["lan_ip"]

Plists can’t call a resolver from a string argument, so a launch wrapper sources endpoints.env and execs the binary — the plist itself carries no literal. Agent prompts get a generated <!-- AUTO:network --> block so the council never reasons on a stale address.

~/.sanctum/sentinels/no-hardcoded-ip.py scans tracked files for infra-IP and MagicDNS literals (10.0.0.x, legacy 192.168.x, vmnet 10.10.10.x, Tailscale 100.64–127.x, *.tail*.ts.net) and exits non-zero on any violation, so it can gate pre-commit and CI. It is the half that makes this productized rather than a one-time cleanup.

What it does not flag, because those are correct by construction:

  • the source of truth (instance.yaml) and the generated endpoints.*
  • loopback, binaries, test fixtures
  • a line carrying an inline ip-allow comment — or, for declarative formats like systemd units that forbid trailing comments, ip-allow on the preceding line
  • anything inside an <!-- AUTO:network --> block (machine-maintained from the SoT)

A justified ip-allow (a stable vmnet anchor used as a documented example, a dated historical runbook) is legitimate. A stale 192.168.x never is — the lint treats those as drift, not exceptions.

Roughly 920 literal sites across the Sanctum repos were moved onto the resolver in a single pass, dispatched as parallel review-and-fix agents — one per file, each syntax-checked and reverted if it broke. Drift-prone LAN and Tailscale literals became resolver lookups; stable vmnet anchors became lookups-with-fallback or justified ip-allows; dated docs and runbooks were ip-allow’d rather than rewritten. No services were restarted during the sweep; the changes take effect the next time each consumer runs.

  • Node Topology — the hub/satellite/mobile model instance.yaml describes.
  • Port Summary — the companion registry for ports (addresses’ other half).