- Replace HSL-based indigo theme with official shadcn violet OKLCH theme
in globals.css for proper contrast in both light and dark mode
- Update tailwind.config.ts to use var(--...) instead of hsl(var(--...))
for OKLCH color space compatibility
- Fix Radix UI crash: replace SelectItem value="" with "__none__" sentinel
in HostDialogs.tsx and L4HostDialogs.tsx (empty string value is invalid)
Form action parsers already return null for non-numeric values
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds 24 shadcn/ui component files to src/components/ui/ via the shadcn CLI, installs required @radix-ui/* and related dependencies, and updates components.json aliases to resolve under src/.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Remove unused Box/Collapse from L4PortsApplyBanner
- Remove unused Stack from RedirectsFields
- Remove unused updateL4ProxyHost import from validation test
- Add eslint-disable-next-line for require() in vi.hoisted() blocks
(necessary pattern since vi.hoisted runs before ESM imports)
- Add file-level eslint-disable no-explicit-any for test files that
intentionally pass invalid types to test validation logic
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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 l4_proxy_hosts table and Drizzle migration (0015)
- Full CRUD model layer with validation, audit logging, and Caddy config
generation (buildL4Servers integrating into buildCaddyDocument)
- Server actions, paginated list page, create/edit/delete dialogs
- L4 port manager sidecar (docker/l4-port-manager) that auto-recreates
the caddy container when port mappings change via a trigger file
- Auto-detects Docker Compose project name from caddy container labels
- Supports both named-volume and bind-mount (COMPOSE_HOST_DIR) deployments
- getL4PortsStatus simplified: status file is sole source of truth,
trigger files deleted after processing to prevent stuck 'Waiting' banner
- Navigation entry added (CableIcon)
- Tests: unit (entrypoint.sh invariants + validation), integration (ports
lifecycle + caddy config), E2E (CRUD + functional routing)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds two new UI-configurable Caddy patterns that previously required raw JSON:
- Per-path redirect rules (from/to/status) emitted as a subroute handler before
auth so .well-known paths work without login; supports full URLs, cross-domain
targets, and wildcard path patterns (e.g. /.well-known/*)
- Path prefix rewrite that prepends a segment to every request before proxying
(e.g. /recipes → upstream sees /recipes/original/path)
Config is stored in the existing meta JSON column (no schema migration). Includes
integration tests for meta serialization and E2E functional tests against a real
Caddy instance covering relative/absolute destinations, all 3xx status codes, and
various wildcard combinations. Adds traefik/whoami to the test stack to verify
rewritten paths actually reach the upstream.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- accept wildcard proxy host domains like *.example.com with validation and normalization
- make exact hosts win over overlapping wildcards in generated routes and TLS policies
- add unit coverage for host-pattern priority and wildcard domain handling
- add a single test:all entry point and clean up lint/typecheck issues so the suite runs cleanly
- run mobile layout Playwright checks under both chromium and mobile-iphone
- IssuedCertsPanel preview: only show active (non-revoked) certs
- ManageIssuedClientCertsDialog: filter out revoked by default; show
"Show revoked (N)" toggle when revoked certs exist
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Split ACME / Imported / CA-mTLS into tabs with count badges
- Add clickable status summary bar (expired / expiring soon / healthy)
- Per-tab search filter by name and domain
- Replace accordion cards with DataTable for imported certs
- Slide-in Drawers (480 px) for add/edit imported and CA certs
- File upload + show/hide toggle for private key in ImportCertDrawer
- CaCertDrawer: Generate / Import PEM tabs for add, simple form for edit
- CA tab: expandable rows showing issued client certs inline
- RelativeTime component: "in 45 days" / "EXPIRED 3 days ago" with date tooltip
- Remove CreateCaCertDialog and EditCaCertDialog (replaced by CaCertDrawer)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Two bugs fixed:
1. buildClientAuthentication was placing issued leaf cert PEMs into
trusted_ca_certs. Caddy uses that field for CA chain validation, not
leaf pinning — putting leaf certs there made chain verification fail
for every presented client cert, causing the browser to be asked
repeatedly. Fixed by using trusted_leaf_certs for managed CAs.
2. If all issued certs for a CA were revoked, the active cert map would
be empty and the code fell back to trusting the CA cert directly,
effectively un-revoking everything. Fixed by tracking which CAs have
ever had issued certs (including revoked) and keeping them in
trusted_leaf_certs mode permanently (empty list = no one trusted).
Also fix CA certificate delete action not surfacing the error message
to the user in production (Next.js strips thrown error messages in
server actions). Changed to return { success, error } and updated the
client dialog to check the result instead of using try/catch.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Old DB records may still have mode='DetectionOnly'. The previous
value?.mode ?? 'inherit' would pass that string into state, leaving no
engine mode button selected. Explicitly accept only 'Off'/'On'; anything
else (including legacy DetectionOnly) falls back to 'inherit'.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
DetectionOnly was fundamentally broken in coraza-caddy (actually blocks
requests via anomaly scoring), caused massive audit log flooding, and the
threshold workaround had several issues:
- t:none is meaningless in a SecAction (no target to transform)
- SecRuleEngine directive ordering relative to SecAction is implementation-
defined, making the override fragile
- host.mode ?? 'DetectionOnly' fallbacks silently gave any host without an
explicit mode the broken DetectionOnly behaviour
Changes:
- Remove DetectionOnly from UI (global settings radio, per-host engine mode)
- Coerce legacy DB values of 'DetectionOnly' to 'On' in buildWafHandler
- Fix fallback defaults: host.mode ?? 'DetectionOnly' → host.mode ?? 'On'
- Fix action parsers: unknown mode defaults to 'On' (was 'DetectionOnly')
- Fix global settings defaultValue: ?? 'DetectionOnly' → ?? 'On' (or 'Off')
- Remove the fragile threshold SecAction workaround
- Update types: mode is now 'Off' | 'On' throughout
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>
- WafEvent model: expose rawData field from DB
- DataTable: add optional onRowClick prop with hover cursor
- WafEventsClient: clicking a row opens a right-side drawer showing
all event fields plus the raw Coraza audit JSON (pretty-printed)
Safety: rawData is rendered via JSON.stringify into a <pre> element,
never via dangerouslySetInnerHTML, so attack payloads are displayed
as inert text.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- DataTable: add overflowX auto to TableContainer + minWidth 600
- WAF events: tighten column widths (Time 150, Host 150, IP 140,
Method 60), add ellipsis+tooltip on Host column, let Rule Message
expand to fill remaining space
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The enabled switch state was never submitted to the form, so the host
WAF config was always saved as enabled: false regardless of the toggle.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace DeleteOutline→Delete, CheckCircleOutline→CheckCircle, ErrorOutline→Error,
RemoveCircleOutline→RemoveCircle, InfoOutlined→Info across all dashboard components.
Replace custom SVG bar chart in OverviewClient with BarChartIcon.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Countries:
- Searchable chip grid (flag emoji + name + code) with 249 countries
- Instant search by name or ISO code prefix
- Select matching / Clear matching when search is active
- Select all / Clear all when no search
- Selected-count indicator in toolbar
- Summary strip showing all selected when search is active
- Custom thin scrollbar, 220px viewport
Continents:
- 7 clickable tiles with emoji + name + code
- Select all / Clear all toolbar
- Warning/success color theming per block/allow tab
Both pickers:
- accentColor prop (warning=orange for block, success=green for allow)
- Hidden form input for server compatibility
- Smooth 120ms transitions
Also simplified TagInput to a plain TextField with inline chips
(removes Autocomplete dependency for freeform fields like ASNs/CIDRs/IPs)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
New field from upstream plugin: when the real client IP is
indeterminate (trusted proxy present but no usable XFF entry),
fail_closed=true blocks the request instead of passing it through.
- Add fail_closed to GeoBlockSettings type
- Include in mergeGeoBlockSettings (OR semantics: either global or host enables it)
- Emit fail_closed in buildBlockerHandler (only when true)
- Parse geoblock_fail_closed from form in both settings and proxy-host actions
- Add Checkbox UI in the Advanced accordion of GeoBlockFields
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>