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>
- New `ca_certificates` table for reusable CA certs (migration 0011)
- CA cert CRUD model, server actions, and UI dialogs
- Proxy host create/edit dialogs include mTLS toggle + CA cert selection
- Caddy config generates `client_authentication` TLS policy blocks with
`require_and_verify` mode for hosts with mTLS enabled
- CA certs sync to slave instances via instance-sync payload
- Certificates page shows CA Certificates section
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Both "waf" and "geoblock" settings were missing from the sync payload,
meaning slaves used their own (potentially unconfigured) values.
Per-host WAF was already synced via the proxyHosts table.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>