Repository navigation
Loxy: self-hosted HAProxy management panel (frontends, backends, TLS, domains) - #1
Merged
Merged
Conversation
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159fEExV6P6AdgP7HbsJNBc
…add design skills Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159fEExV6P6AdgP7HbsJNBc
…golden + haproxy -c test; deploy/CI scaffolding; OpenAPI contract Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159fEExV6P6AdgP7HbsJNBc
…egration suite with all VERIFY items passing Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159fEExV6P6AdgP7HbsJNBc
…o v5), DNS providers via libdns, REST API, embedded SPA, loxy CLI Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159fEExV6P6AdgP7HbsJNBc
…d UI screenshots Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159fEExV6P6AdgP7HbsJNBc
…e add/remove, 0 failures); lint fixes Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159fEExV6P6AdgP7HbsJNBc
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159fEExV6P6AdgP7HbsJNBc
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159fEExV6P6AdgP7HbsJNBc
…s (webhook/Slack/Telegram/SMTP), controller /metrics, nightly backups Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159fEExV6P6AdgP7HbsJNBc
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159fEExV6P6AdgP7HbsJNBc
…ules) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159fEExV6P6AdgP7HbsJNBc
…s validate; ACME waits for applied config; Pebble HTTP-01 integration test Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159fEExV6P6AdgP7HbsJNBc
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159fEExV6P6AdgP7HbsJNBc
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159fEExV6P6AdgP7HbsJNBc
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159fEExV6P6AdgP7HbsJNBc
…ric rings (svc 15 min), server rows every 2 s, soft memory limit, opt-in pprof Measured with 50 LBs / 200 services / 1000 targets: RSS ~48 MB, list p95 6.9 ms, single-resource p95 ~1.2 ms, idle CPU 1.3-3%. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159fEExV6P6AdgP7HbsJNBc
…reset-at-connect Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159fEExV6P6AdgP7HbsJNBc
…zero-downtime results Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159fEExV6P6AdgP7HbsJNBc
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159fEExV6P6AdgP7HbsJNBc
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159fEExV6P6AdgP7HbsJNBc
… HAProxy) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159fEExV6P6AdgP7HbsJNBc
…zard and service editor Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159fEExV6P6AdgP7HbsJNBc
…le/disable and login enforcement Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159fEExV6P6AdgP7HbsJNBc
… stream), LOXY_LIVE_LOGS Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159fEExV6P6AdgP7HbsJNBc
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159fEExV6P6AdgP7HbsJNBc
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159fEExV6P6AdgP7HbsJNBc
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159fEExV6P6AdgP7HbsJNBc
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159fEExV6P6AdgP7HbsJNBc
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159fEExV6P6AdgP7HbsJNBc
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159fEExV6P6AdgP7HbsJNBc
… HAProxy, bounded metrics queries, dual-stack IP assignment + race, reject IPv6 zones (config injection), admin-only reads of users/alert rules, owner/2FA/session hardening, cert quota on domain issuance, nil guards, ordered snapshot refresh, rollback re-pushes ACME tokens Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159fEExV6P6AdgP7HbsJNBc
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159fEExV6P6AdgP7HbsJNBc
…y after runtime-then-failed-reload Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159fEExV6P6AdgP7HbsJNBc
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159fEExV6P6AdgP7HbsJNBc
Rewrite the spec, README and ADRs around frontends and backends instead of load-balancer objects, plans, rescaling and prices. Add ADR 0005 recording the direction change and retire the cross-frontend cap note. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159fEExV6P6AdgP7HbsJNBc
…iler Replace load balancers, services, targets and plans with reusable backends (server pools, health checks, sticky sessions, PROXY protocol) and frontends (binds, TLS, redirect, host/path routes to backends). Domains attach to a frontend; DNS verification and auto records use its bind addresses. CLI gains `frontends ls` / `backends ls`. Integration tests rebuilt on the new model; drop an accidentally committed test binary. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159fEExV6P6AdgP7HbsJNBc
Rebuild the UI for the frontend/backend model, remove the load-balancer wizard, rescale and networking pages, and fix date helpers to read the API's millisecond timestamps. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159fEExV6P6AdgP7HbsJNBc
…hots Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159fEExV6P6AdgP7HbsJNBc
The shared default transport keeps 2 idle conns per host, so 6 of the 8 load workers dialled per request and exhausted ephemeral ports on the CI runner, failing the zero-downtime test and the next test's dial. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159fEExV6P6AdgP7HbsJNBc
…icky cookie - "*" binds render 0.0.0.0 plus ipv6@:::port v6only when the host has IPv6 (startup probe, LOXY_IPV6=auto|on|off); HAProxy exits otherwise. - Listener collisions also checked when a frontend is enabled via PATCH, gains a managed HTTP-01 cert, or gets a domain (implicit :80 ACME). - Sticky cookie gets secure when every frontend using the backend has TLS. - Duplicate certificate ids are a field error instead of a 500. - Explicit health-check port is kept; http backends default to http checks; interval 3-3600 (spec 8.6); UI validation matches. - Node-wide events (apply/backup) are not narrowed by alert label filters. - Server views compute backend health once per request. - Refuse pre-release load-balancer databases with a clear error. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159fEExV6P6AdgP7HbsJNBc
… collisions - Node-wide event kinds (apply/config/backup/node) bypass alert label filters explicitly; domain events carry their frontend id. - Listener-changing requests are serialized so a collision check and its commit cannot interleave; domain DNS work stays outside the lock. - Collisions are family-aware (* both, 0.0.0.0 IPv4, :: IPv6); explicit :: binds are v6only. - One NeedsACME rule shared by the renderer and the API checks. - Legacy-schema refusal happens before anything is written. - LOXY_IPV6 also accepts booleans; secure-cookie map computed once per render. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159fEExV6P6AdgP7HbsJNBc
2shiraz
marked this pull request as ready for review
October 14, 2026 08:28
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Loxy is a self-hosted management panel for HAProxy 3.4 LTS. You manage frontends and backends; Loxy renders
haproxy.cfg, validates it withhaproxy -c, swaps it in atomically, and uses runtime API ops where it can (a reload otherwise).What's in it
*or a specific IP, many ports), HTTP or TCP, TLS termination with a list of certificates, HTTP→HTTPS redirect, host/path routing rules to backends, a default backend, maintenance (503 / reject), enable/disable, client timeout, maxconn, forwarded headers.add/del server, drain before removal).virt@acme_http01, DNS-01 through libdns), hot-swapped withset/commit ssl cert.*. A certificate issued from a domain is attached to its frontend if that frontend terminates TLS.loxy frontends ls,loxy backends ls,loxy api …,render --dry-run,backup/restore,admin reset-password.Direction change (ADR 0005)
The earlier load-balancer model is gone: LB objects, plans and sizes, rescaling, prices, quotas and delete protection. Everything is built on the frontend/backend model, and provider-specific wording has been removed. The schema was rewritten in place (pre-release). Loxy refuses an old load-balancer database with a clear error instead of failing later.
Decisions (defaults used)
*.suffix, case-insensitive, with the port ignored.nullmeans each server's own port.*renders0.0.0.0:<port>plusipv6@:::<port> v6only. The IPv6 listener is added only when the host supports IPv6, because HAProxy exits at startup otherwise (verified).LOXY_IPV6=auto|on|offcontrols this; see ADR 0004 §9.Review
Two review passes (correctness, security, performance). Fixed:
:80listener collisions:*,0.0.0.0,::);secureon sticky cookies when every frontend using the backend has TLS.:80listener.Left as is:
Verification
golangci-lint(0 issues),go vet,go test ./...— all green, with new regression tests for each fix.make test-integrationagainsthaproxy:3.4.6-alpinepasses, including Pebble ACME.npm run check: 0 errors and 0 warnings. The initial bundle is about 55 KB gzip (budget 150 KB).Not done yet
haproxy.cfg(backlog).Screenshots:
docs/screenshots/.🤖 Generated with Claude Code
https://claude.ai/code/session_0159fEExV6P6AdgP7HbsJNBc