00c9bff8b4
Banner (L4PortsApplyBanner): - Accept refreshSignal prop; re-fetch /api/l4-ports when it changes - Signal fires immediately after create/edit/delete/toggle in L4ProxyHostsClient without waiting for a page reload Master-slave replication (instance-sync): - Add l4ProxyHosts to SyncPayload.data (optional for backward compat with older master instances that don't include it) - buildSyncPayload: query and include l4ProxyHosts, sanitize ownerUserId - applySyncPayload: clear and re-insert l4ProxyHosts in transaction; call applyL4Ports() if port diff requires it so the slave's sidecar recreates caddy with the correct ports - Sync route: add isL4ProxyHost validator; backfill missing field from old masters; validate array when present Tests (25 new tests): - instance-sync.test.ts: buildSyncPayload includes L4 data, sanitizes ownerUserId; applySyncPayload replaces L4 hosts, handles missing field, writes trigger when ports differ, skips trigger when ports already match - l4-ports-apply-banner.test.ts: banner refreshSignal contract + client increments counter on all mutation paths Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>