markup.tips
Use the platform. Reach for a native API before a library.

Trusted Network Access

Owner vs guest on a self-hosted app. Localhost and HTTPS = owner. LAN HTTP = guest (read-only). Tailscale CGNAT and mDNS hostnames are trusted networks but not owners.

Why

A self-hosted app shared on a home network needs a simple, zero-config access model. Requiring a login for every family member adds friction. Instead: the person at the machine (localhost) or anyone with an HTTPS certificate (Tailscale, mkcert) gets full write access. Anyone on the LAN via a plain HTTP IP gets read-only access. No accounts required.

Code

// lib/isTrustedNetwork.ts
export function isTrustedNetwork(hostname: string): boolean {
  if (hostname === 'localhost' || hostname === '127.0.0.1') return true;
  if (hostname.endsWith('.local')) return true;           // mDNS
  if (hostname.endsWith('.ts.net')) return true;          // Tailscale MagicDNS
  if (/^192\.168\./.test(hostname)) return true;         // RFC1918 class C
  if (/^10\./.test(hostname)) return true;               // RFC1918 class A
  // Tailscale CGNAT range 100.64.0.0/10
  const match = hostname.match(/^100\.(\d+)\./);
  if (match && Number(match[1]) >= 64 && Number(match[1]) <= 127) return true;
  return false;
}

// isOwner: can create, edit, delete
export function isOwner(): boolean {
  if (typeof window === 'undefined') return false;       // SSR guard
  const { hostname, protocol } = window.location;
  return hostname === 'localhost' || hostname === '127.0.0.1' || protocol === 'https:';
}
// Gate write-access features in UI
const [isOwner, setIsOwner] = useState(false);
useEffect(() => { setIsOwner(checkIsOwner()); }, []);

{isOwner && <button onClick={handleDelete}>Delete</button>}

// Gate nav links by network trust (not ownership)
const [isSecure, setIsSecure] = useState(true);
useEffect(() => {
  setIsSecure(isTrustedNetwork(window.location.hostname) || window.location.protocol === 'https:');
}, []);

const links = [
  { href: '/pantry', label: 'Pantry' },
  ...(isSecure ? [{ href: '/cookware', label: 'Cookware' }] : []),
];

Notes

Reference

packages/app/lib/isTrustedNetwork.ts, packages/app/components/Nav.tsx in pantry-host