Copy scripts/minify into the builder image so containerized builds can
run the minify step. Add the missing final newline to Dockerfile.
Add node_module to docker ignore
In goutils commit 5159888197c5a49605495be9b87525fde26c83d1 Copy with an http.ResponseWriter destination no longer flushes after every write unless Content-Type is text/event-stream, or Transfer-Encoding includes chunked without Content-Length.
This causes endpoints like /events not being flushed on write,
which delays notification like container creation/deletion.
This commit enforces flush on every write for socket proxy,
which also applies to agents
Downgrade moby API and client requirements, add excludes for newer patch
releases, and bump github.com/yusing/goutils packages plus godoxy agent
and internal/dnsproviders pseudo-versions.
Replace chained level checks with a validLevels list and includes().
Coalesce addConsoleLine call sites. Default missing payload.message to an
empty string so the console does not show undefined.
LXCGetIPsWithStatus reads net config for stopped and suspended guests instead
of the interfaces API. UpdateResources reuses IP lists for 30s when the
resource status is unchanged, caps concurrent LXC IP lookups, and sets
MaxConnsPerHost on the HTTP transport to match.
Add httptest coverage for offline IPs, second-poll IP reuse, and status
transitions. Replace math.Pow session backoff with fixed-duration retries.
Restyle the idle watcher loading UI with design tokens, light and dark
variants via `prefers-color-scheme`, and system font stacks (drop
Google Fonts). Tighten layout, animations, and console presentation.
In `loading.js`, pass event `level` into `addConsoleLine` and apply
`level-*` classes so log lines can reflect debug, info, warn, and
error. On the HTML side, set `color-scheme`, add an empty `alt` on the
logo, and mark the loading dots as `aria-hidden` for assistive
technology.
When a path includes a line number, use only the #L anchor in the blob URL
and do not append the old #fragment, which duplicated the anchor in the
href.
Ignore READMEs under scripts/ (alongside submodule paths), read a missing
destination mdx as empty, remove unexpected impl .mdx files when syncing, and
run `main` only when `import.meta.main` so the module is importable from
tests. Export `rewriteImplMarkdown` and `syncImplDocs` and add Bun tests for
link rewriting, scripts exclusion, and add/remove mdx output.
Clarify OpenAPI and the `Route` field: HTTP-based routes only, must
match a configured `inbound_mtls_profiles` entry, and ignored when
`entrypoint.inbound_mtls_profile` is set. Reorder `load_avg_5m` in the
host metrics schema alongside the other load average fields.
e744642a65d2cc7c13553f949d994e64ef46111d test(cache): add cold concurrent and in-flight publish regression tests
Cover keyed cache blocking until refreshMu publishes an in-flight entry,
asserting waiters do not trigger duplicate recomputation.
Add stress tests for cold key and cold func paths so concurrent callers
never observe int zero while the value computes exactly once.
24ea5610d1df3f581e92143aff7a6c976b180ed7 feat(cache): add debug-tagged cache observability with summarized logging
Emit zerolog debug events for hits, misses, TTL recompute, usage, and
overflow evictions when built with -tags=debug; non-debug builds compile
in no-op helpers. Summarize keys and values in logs via formatResult.
Re-check the keyed entry under refreshMu so a just-published cold entry
is not returned as a non-expired hit. Add tests and README notes.
98c1c1d20a85bd3e36c9609f8c23b1aad30d7dbb chore(deps): bump xsync, zerolog, go-proxyproto, and mongo-driver
Refresh go.sum across the root module plus http/reverseproxy,
http/websocket, and server submodules.
Upgrade xsync to v4.5.0 and zerolog to v1.35.1; bump go-proxyproto to
v0.12.0 in server and mongo-driver v2 to v2.5.1 in http/websocket.
b26d42233f2e295a0d80e9a853d3f9ac2c7fb711 fix(http,server): avoid noisy WebSocket close logs and forced HTTP/3 slog debug
Skip debug error logging in Manager.close for context.Canceled and
net.ErrClosed; log unexpected close errors with Err().
Stop setting slog.LevelDebug on slogzerolog.Option for *http3.Server so the
handler follows the configured zerolog level.
94557c5e86670864de3a95b340ed35f9249ca38c fix(http): align ModifyResponseWriter status with modifier and error paths
Set headerSent and code to 500 when the modifier fails, instead of the
deferred bookkeeping leaving the original WriteHeader argument.
After a successful modifier, write and record resp.StatusCode so the
sent status matches what the modifier set, not only the initial code.
Replace the defer with explicit updates on each branch so state stays
consistent with what is actually written.
7ba8ccefbd9ef415a4371a33150aad15d1179611 feat(http): add HTML 502 page when origin is unreachable
For GET requests that accept HTML, return an embedded page with a retry
countdown and no-store cache headers. Other requests keep the plain text
message.
Ignore **/*.min.* in .gitignore for generated minified HTML assets. Add
tests for HTML and plain text responses.
bdd71fab358c05a17d49fdf39067ecf45304a96f test(events): add History Get bounds, AddAll snapshot races, and benchmarks
Assert Get keeps one global window when mixing categories and that Get
during AddAll never reports a partial batch length.
Benchmark History.Add and SnapshotAndListen under background producers;
use WaitGroup.Go in the cancel stress test; group consts in history.go.
Update placeholder generation to use `base.min.ext`, skip files already named
with `.min.` and `internal/go-proxmox`, and discover JS/HTML under
`internal/` and `goutils/` so `go vet` matches the Bun minify script.
Update go.mod/sum across the root module, agent, internal/dnsproviders, and
socket-proxy (lego, docker/cli, Moby API/client, xsync, zerolog, goutils,
DNS SDKs, genproto RPC). Agent and dnsproviders take godoxy v0.28.0 and newer
replace pseudo-versions.
Switch the Dockerfile minify-stage Bun COPY to oven/bun:1-alpine instead of a
pinned patch tag.
Replace minify-js with minify delegating to `bun scripts/minify`. Add
modernize (go fix per module). Drop echo lines from tidy
and dependency update loops. Rename update-wiki entry to index.ts and
invoke it as `bun scripts/update-wiki`.
Relax response-body gating so HTML and XHTML can be buffered when
Transfer-Encoding is chunked-only, or when Content-Length is missing,
while still rejecting non-identity encodings that are not chunked HTML
and other non-HTML cases.
Update modifyHTML to cap reads for unknown length, splice the original
stream back when the cap is hit, and document the behavior in the
package README. Extend tests for themed middleware and the rewrite gate.
Point go:embed at block_page.min.html, loading_page.min.html,
loading.min.js, and captcha.min.html. Update the minified-files
gitignore glob from **/*-min.* to **/*.min.*.
Add `scripts/minify` with glob discovery, @swc/core for JavaScript, and
@swc/html for HTML, writing sibling `*.min.html` / `*.min.js` and
skipping `internal/go-proxmox` and files whose basename already contains
`.min.`. Ship Bun lock, tsconfig, README, and `.gitignore` for the tool.
Remove the xsync map cache and add a 1000-entry goutils keyed cache in
lookup.go. Rrate-limit failure logs, and populate IPInfo.City.
Adjust ACL Geo matchers to the new signature.
Remove the ForceCacheControl wrapper from the ready ServeHTTP path so
proxied responses keep upstream Cache-Control and Expires.
Centralize strong no-store headers via setNoStoreHeaders for the loading
page, static CSS/JS, favicon, and SSE wake events. Add tests covering
loading responses and upstream header preservation.
Added details on request-variable substitution, explaining how it
reads fields from the active outbound request and resolves upstream
variables from the current route context.
Replace the xsync map plus manual expiry on checkCache with
cache.NewKeyFunc(evaluateIP).WithTTL. Move deny/allow/default logic into
evaluateIP; wire getCachedCity and IPAllowed through the cache API.
Refresh README security notes and add tests showing cached decisions persist
across in-memory rule changes until TTL expires.
Add per-provider obtain serialization and an RWMutex around shared
provider fields. GetCert and SNI matching use snapshot helpers; rebuild
the matcher atomically; GetExpiries returns a cloned map.
Clone Config.HTTPClient (including Transport) per lego.Config so parallel
providers do not mutate shared client state.
Document the concurrency model in README.
* test(events): add History Get bounds, AddAll snapshot races, and benchmarks
Assert Get keeps one global window when mixing categories and that Get
during AddAll never reports a partial batch length.
Benchmark History.Add and SnapshotAndListen under background producers;
use WaitGroup.Go in the cancel stress test; group consts in history.go.
* test(synk): refactor pool benchmarks with workerpool
Replace manual work channels and WaitGroup workers with workerpool,
deterministic microsecond jitter, and cpuWork instead of simulateWork.
Adjust concurrent allocation size bands, unsized GetAtLeast, and
ReportAllocs; remove verbose timing metrics. Drop unused newReader in
http body benchmarks.
* fix(io): gate HTTP response flushing on streaming headers
Copy with an http.ResponseWriter destination no longer flushes after
every write unless Content-Type is text/event-stream, or Transfer-Encoding
includes chunked without Content-Length. Buffered responses with
Content-Length skip Flush so fixed-size bodies are not flushed per chunk.
Document the narrowed behavior in README and add tests.
* fix(http): cap modifier buffer preallocation by maxBufferedBytes
When buffering begins, clamp the initial pool buffer size to maxBufferedBytes
when that limit is set. Previously we used max(len(b), Content-Length), which
could reserve a very large slice for a declared body we would never fully
buffer before switching to passthrough.
* fix(http): honor response modifier status in WriteHeader
After a successful modifier run, pass resp.StatusCode to the wrapped writer
instead of the original code so modifiers that change the status code take
effect.
* feat(cache): atomic snapshots, per-refresh backoff, and access-log eviction
Replace mutex-held cached fields with atomic.Pointer snapshots for single-value
and keyed caches so hot reads avoid locking while refreshes stay serialized per
entry.
Build fresh backoff instances per refresh, add context-aware backoff waits, and
skip storing cached values when cancellation matches the returned error.
Keyed caches record access via sequences and logs instead of a global MRU list;
fix janitor pending cleanup when the signal channel is full. Add benchmarks and
tests for cancellation, TTL, and eviction behavior.
* chore(deps): update dependencies
Httptest and similar callers often leave Host unset; fall back to URL
for scheme, host, port, and addr substitution.
jsonstore drops the IsTest load short-circuit and duplicate loadNS map
registration; tests isolate storesPath. Skip MaxMind background updates
when IsTest. Tests restore APISkipOriginCheck, use app-scoped OIDC
state cookies, attach route context in middleware helpers, and use
locked buffers for concurrent log capture.
Header-only modifiers no longer use LazyResponseModifier with buffering
always enabled. They wrap the response with ModifyResponseWriter and
return after invoking the next handler.
Body modifiers still use LazyResponseModifier with canBufferAndModifyResponseBody.
Add a regression test that uses a 64MiB Content-Length with a small body.
Use `bun build --minify --target browser` instead of `bunx uglify-js` in the
minify-js target.
Exclude `internal/go-proxmox/` from the find pipeline so that tree is not
minified.
Introduce reusable `inbound_mtls_profiles` in root config and support
`entrypoint.inbound_mtls_profile` to require client certificates for all
HTTPS traffic on an entrypoint. Profiles can trust the system CA store,
custom PEM CA files, or both, and are compiled into TLS client-auth
pools during entrypoint initialization.
Also add route-scoped `inbound_mtls_profile` support for HTTP-based
routes when no global entrypoint profile is configured. Route-level mTLS
selection is driven by TLS SNI, preserves existing behavior for open and
unmatched hosts, and returns the intended 421 response when secure
requests omit SNI or when Host and SNI resolve to different routes.
Add validation for missing profile references and unsupported non-HTTP
route usage, update config and route documentation/examples, expand
inbound mTLS handshake and routing regression coverage, and bump
`goutils` for HTTPS listener test support.
Sort proxy.* keys by dot depth, then name, before building the tree so
broader paths apply before deeper ones. When a new value would sit on a
node that is already a map, parse it as a YAML object (tabs normalized to
two spaces), deep-merge, and treat an empty string as an empty object.
Return clear errors when a scalar and a nested map disagree.
Drop the preallocated refPrefixes table in favor of refPrefix(n). Add
internal tests for parseLabelObject, mergeLabelMaps, key order, and
flatten; extend export tests for mixed OIDC-style labels and conflicts.
* refactor(docker): extract label parse and flatten helpers
Refactor ParseLabels by moving proxy label application into applyLabel,
descendLabelMap, and setLabelValue so traversal and leaf merge share one path
without labelLoop continues.
Add splitAliasLabel for ExpandWildcard so proxy.* prefix handling stays in one
place and uses CutPrefix/Cut consistently.
Deduplicate flattenMap and flattenMapAny value handling with flattenValue plus
joinLabelKey and stringifyLabelKey for flattened key construction.
* refactor(docker): structured errors for label type clashes
Replace ad hoc fmt.Errorf messages in descendLabelMap, setLabelValue, and
mergeLabelMaps with UnexpectedTypeError so wording is consistent and mapping
vs scalar conflicts stay explicit.
Hoist requireMap in label tests to a shared helper.
Normalize tabs to two spaces in expandYamlWildcard so wildcard YAML matches
the indentation used in the object-merge path.
* refactor(docker): optional UnexpectedTypeError message for merge conflicts
Extend UnexpectedTypeError with an optional Message field; when set, Error()
returns it instead of the default expect-versus-actual formatting.
mergeLabelMaps sets that message when a mapping would merge into an existing
scalar, so the error states the situation instead of only "expect scalar".
Update TestMergeLabelMaps to assert the new wording.
Refresh indirect and direct golang.org/x pins (crypto, net, sys, text,
arch, mod, tools) across the root module, agent, dnsproviders,
socket-proxy, and h2c_test_server.
Advance the goutils submodule and align pseudo-versions for reverseproxy,
websocket, and server; bump workspace replace commits for agent and
internal/dnsproviders.
Update vultr/govultr to v3.30.0 and mattn/go-isatty to v0.0.21 where they
appear in the graph.
Validate GODOXY_LOCAL_API_ADDR before starting the unauthenticated local
API. Loopback listeners still succeed by default; addresses that bind
all interfaces, unspecified IPs, LAN hosts, or non-loopback names need
GODOXY_LOCAL_API_ALLOW_NON_LOOPBACK=true.
When that opt-in is set and the host is not loopback, log a warning so
non-local exposure is obvious. Wire common.LocalAPIAllowNonLoopback from
LOCAL_API_ALLOW_NON_LOOPBACK and document it (with a risk note) in
.env.example.
Add TestValidateLocalAPIAddr for loopback, wildcard, LAN, and hostname
cases with the allow flag on and off.
Drop the `--axios` flag from the `gen-api-types` target and reflow the
`bunx` `swagger-typescript-api generate` arguments for clearer
continuation lines.
Now generated api.ts is fetch API based and no longer rely on axios.
When a path exists but reads as empty or whitespace-only, return nil
without touching dst, matching the no-file case. This avoids
unmarshaler errors on blank files and matches the updated doc comment.
Finish the file API traversal fix by rooting both GET and SET operations at the
actual file-type directory instead of the process working directory. This blocks
`..` escapes from `config/` and `config/middlewares/` while preserving valid
in-root reads and writes.
Also harden the optional unauthenticated local API listener so it only starts on
loopback addresses (`localhost`, `127.0.0.1`, `::1`). This preserves same-host
automation while preventing accidental exposure on wildcard, LAN, bridge, or
public interfaces.
Add regression tests for blocked traversal on GET and SET, valid in-root writes,
and loopback-only local API address validation. Fix an unrelated config test
cleanup panic so the touched package verification can run cleanly.
Constraint: `GODOXY_LOCAL_API_ADDR` is documented for local automation and must remain usable without adding a new auth flow
Constraint: File API behavior must keep valid config/provider/middleware edits working while blocking path escapes
Rejected: Mirror the previous GET `OpenInRoot(".", ...)` approach in SET | still allows escapes from `config/` to sibling paths under the working directory
Rejected: Keep unauthenticated non-loopback local API binds and document the risk | preserves a high-severity pre-auth network exposure
Confidence: high
Scope-risk: moderate
Reversibility: clean
Directive: Treat `LOCAL_API_ADDR` as same-host only; if non-loopback unauthenticated access is ever needed, gate it behind a separately named explicit insecure opt-in
Tested: `go test -count=1 -ldflags='-checklinkname=0' ./internal/api/v1/file -run 'Test(Get|Set)_PathTraversalBlocked' -v`
Tested: `go test -count=1 -ldflags='-checklinkname=0' ./internal/config -run '^TestValidateLocalAPIAddr$|^TestRouteValidateInboundMTLSProfile$' -v`
Tested: `go test -count=1 -ldflags='-checklinkname=0' ./internal/api/... ./internal/config/...`
Not-tested: End-to-end runtime verification of fsnotify reload behavior after a valid in-root provider edit
Update Docker builder images and all staged go.mod `go` lines to 1.26.2 for the
root module, agent, cli, bench_server, h2c_test_server, dnsproviders, and
socket-proxy.
Upgrade coreos/go-oidc, docker/cli, valyala/fasthttp, OpenTelemetry HTTP
instrumentation and SDK, Google Cloud auth and API clients, genproto RPC,
OCI DNS SDK, and pinned goutils/http packages; advance the goutils submodule
pointer.
Bump direct and indirect dependencies across the main module, agent,
dnsproviders package, and socket-proxy: zerolog, lego, validator, Docker CLI,
OpenTelemetry, cloud SDKs, DNS provider clients, and related transitives.
Advance goutils, go-proxmox, and gopsutil submodules; refresh internal
godoxy/agent and dnsproviders pseudo-versions. Extend Moby exclude list for
newer API and client releases to keep older daemon compatibility.
Add exclusion for all paths under /src/* and modify the websocket protocol header to 'vite-hmr' for improved compatibility with development environments.
Implement Signed Double Submit Cookie pattern to prevent CSRF attacks.
Adds CSRF token generation, validation, and middleware for API endpoints.
Safe methods (GET/HEAD/OPTIONS) automatically receive CSRF cookies, while
unsafe methods require X-CSRF-Token header matching the cookie value with
valid HMAC signature. Includes same-origin exemption for login/callback
endpoints to support browser-based authentication flows.
Use os.OpenRoot to restrict file access to the application root,
preventing directory traversal attacks through the file download endpoint.
Also add test to verify path traversal attempts are blocked.
Add a new GitHub Actions workflow for building Docker images with the "compat" tag on the compat branch. Also update the existing nightly workflow to only run on the compat branch instead of all branches.