# Reading and projecting estate/.json. Sourced, never executed. # # python3 rather than jq: berth's floor already includes python3, so it is a # dependency berth has rather than one it adds. estate_get() { python3 -c ' import json, sys d = json.load(open(sys.argv[1])) for k in sys.argv[2].split("."): if isinstance(d, list): try: k = int(k) except ValueError: sys.exit(0) try: d = d[k] except Exception: sys.exit(0) print("" if d is None else d if isinstance(d, str) else json.dumps(d)) ' "$ESTATE_FILE" "$1" } # Services in scope for one target. A service names its targets; absent = all. # Fields are US-separated (0x1f), not tab: tab is IFS whitespace, so bash # collapses a run of them and an empty field would shift every later column. estate_services() { local target="${1:-$TARGET}" python3 -c ' import json, sys d = json.load(open(sys.argv[1])) target = sys.argv[2] for s in d.get("services", []): tg = s.get("targets") if tg is not None and target not in tg: continue print("\x1f".join([ s.get("name", ""), s.get("host", ""), str(s.get(target + "_upstream", s.get("upstream", "")) or ""), s.get("kind", "proxy"), "raw" if s.get("raw") else "", s.get("placement", "box"), s.get("peer", ""), str(s.get("port", "") or ""), s.get("local_host", s.get("host", "")), ])) ' "$ESTATE_FILE" "$target" } # The SAN list the services need, derived — never a literal list. estate_sans() { python3 -c ' import json, sys d = json.load(open(sys.argv[1])) domain = d["domain"] sans = [domain] depths = set() for s in d.get("services", []): h = s.get("host", "") if not h: continue # A wildcard matches exactly ONE label. "git" needs *.domain; "dlt.spr" # needs *.spr.domain. The parent of the leaf is what has to be covered. parent = h.split(".", 1)[1] if "." in h else "" depths.add(parent) for p in sorted(depths): sans.append("*." + (p + "." if p else "") + domain) for s in sans: print(s) ' "$ESTATE_FILE" } # Is covered by ? A wildcard matches exactly one label. san_covers() { local fqdn="$1" san="$2" [ "$fqdn" = "$san" ] && return 0 case "$san" in \*.*) local suffix="${san#\*.}" # Must end in .suffix AND have exactly one extra label. case "$fqdn" in *".$suffix") [ "${fqdn%".$suffix"}" = "${fqdn%%.*}" ] && return 0 ;; esac ;; esac return 1 } # ── the overlay ──────────────────────────────────────────────────────────── # Every overlay name, one per line. overlay_names() { python3 -c ' import json, sys d = json.load(open(sys.argv[1])) for n in d.get("vpn", {}).get("overlays", {}): print(n) ' "$ESTATE_FILE" } # Peers of one overlay, US-separated: # name, address, role, endpoint, public_key, allowed_ips, keepalive overlay_peers() { python3 -c ' import json, sys d = json.load(open(sys.argv[1])) ov = d.get("vpn", {}).get("overlays", {}).get(sys.argv[2], {}) for name, p in ov.get("peers", {}).items(): print("\x1f".join(str(x) if x is not None else "" for x in [ name, p.get("address"), p.get("role"), p.get("endpoint"), p.get("public_key"), p.get("allowed_ips"), p.get("keepalive"), ])) ' "$ESTATE_FILE" "$1" } overlay_get() { estate_get "vpn.overlays.$1.$2"; } # Is an address inside a CIDR? Pure python so there is no ipcalc dependency — # berth's floor already includes python3 because the IaC side needs it. addr_in_subnet() { python3 -c ' import ipaddress, sys try: sys.exit(0 if ipaddress.ip_address(sys.argv[1]) in ipaddress.ip_network(sys.argv[2], strict=False) else 1) except ValueError: sys.exit(2) ' "$1" "$2" } # The upstream a service actually resolves to, as "host:port". # # A PLACED service has no literal `upstream` field: ✖ B9 replaced langfuse's # hand-written `10.8.0.2:3000` with placement+peer+port, because being reached # by address on the overlay is ONE decision, not three properties. Everything # that asks "what does this service point at" must therefore resolve it the # same way, or it silently sees an empty string and skips the service — which # is exactly how vpn.sh's bindings invariant went quiet after B9 landed. # # usage: service_upstream service_upstream() { local up="$1" placement="$2" peer="$3" port="$4" case "$placement" in local|instance) local addr; addr="$(overlay_get estate "peers.${peer}.address")" [ -z "$addr" ] && return 1 printf '%s:%s' "$addr" "$port" ;; *) printf '%s' "$up" ;; esac }