Introduces a new `$redacted` dynamic variable that wraps its single
argument with `strutils.Redact`, allowing sensitive values (e.g.,
authorization headers, query parameters) to be masked in rule
expressions.
The variable accepts exactly one argument, which may itself be a nested
dynamic variable expression such as `$header(Authorization)` or
`$arg(token)`, enabling patterns like `$redacted($header(Authorization))`.
Adds corresponding tests covering plain string redaction, nested header
and query arg wrapping, and the error case when no argument is provided.
The repoRootAbs was resolving to the script directory instead of the
repository root. Fixed by resolving two levels up from import.meta.dir.
Also optimized writeImplDocToMdx to skip writes when content is unchanged
and removed unused return value from syncImplDocs.
* chore(deps): update submodule goutils
* docs(http): remove default client from README.md
* refactor(rules): introduce block DSL, phase-based execution, and flow validation
- add block syntax parser/scanner with nested @blocks and elif/else support
- restructure rule execution into explicit pre/post phases with phase flags
- classify commands by phase and termination behavior
- enforce flow semantics (default rule handling, dead-rule detection)
- expand HTTP flow coverage with block + YAML parity tests and benches
- refresh rules README/spec and update playground/docs integration
- Default rules act as fallback handlers that execute only when no matching non-default rule exists in the pre phase
- IfElseBlockCommand now returns early when a condition matches with a nil Do block, instead of falling through to else blocks
- Add nil check for auth handler to allow requests when no auth is configured
* fix(rules): buffer log output before writing to stdout/stderr
* refactor(api/rules): remove IsResponseRule field from ParsedRule and related logic
* docs(rules): update examples to use block syntax
Refactor response body modification to only allow text-like content types
(JSON, YAML, XML, etc.) instead of all HTML responses.
Body modification is now
blocked for binary content and transfer/content encoded responses, while status
code and headers can still be modified.
This prevents issues with compressed or streaming responses while
maintaining the ability to modify text-based API responses.
Previously, up notifications were sent whenever a service recovered,
even if no down notification had been sent (e.g., when recovering
before the failure threshold was met). This could confuse users who
would receive "service is up" notifications without ever being
notified of a problem.
Now, recovery notifications are only sent when a prior down
notification exists, ensuring notification pairs are always complete.
- Add minimum: 0 validation to LogRetention properties (days, keep_size, last)
- Add "interned" descriptions to fstype, path, and name fields
- Rename operationId and x-id from "routes" to "list" for GET /route endpoint
Add required websocket check at the beginning of both journalctl and tail endpoint handlers to ensure these endpoints only accept websocket connections.
Add a new CLI application (`cmd/cli/`) that generates command-line interface commands from the API swagger specification. Includes:
- Main CLI entry point with command parsing and execution
- Code generator that reads swagger.json and generates typed command handlers
- Makefile targets (`gen-cli`, `build-cli`) for generating and building the CLI
- GitHub Actions workflow to build cross-platform CLI binaries (linux/amd64, linux/arm64)
Replace the existing HTTP client with a custom-configured client that skips TLS verification for favicon fetching,
and add explicit Content-Type validation to ensure only valid image responses are accepted.
This fixes potential issues with SSL certificate validation and prevents processing of non-image responses.
Replace simple path prefix-based enforcement/bypass mechanism with a more
flexible function-based approach. This allows for more complex conditions
to determine when middleware should be enforced or bypassed.
- Add checkReqFunc and checkRespFunc types for flexible condition checking
- Replace enforcedPathPrefixes with separate enforce and bypass check functions
- Add static asset path detection for automatic bypassing
- Separate request and response check logic for better granularity
Restructure icon search to use a tiered ranking system:
- Exact matches get highest priority (rank 0)
- Prefix matches ranked by name length (rank 100+)
- Contains matches ranked by relevance (rank 500+)
- Fuzzy matches as fallback (rank 1000+)
Also refactors InitCache to use switch statements for clarity
and updates goutils submodule.
Support the ":proxy" format where only the proxy port is specified.
When the listening port part is empty, it defaults to 0 instead of
returning a parse error.
- Convert markdown output to fumadocs MDX
- Add api-md2mdx.ts for markdown to MDX transformation
- Remove sidebar auto-update functionality
- Change output directory from src/impl to content/docs/impl
- Update DOCS_DIR path in Makefile to local wiki directory
- Copy swagger.json directly instead of generating markdown
- Add argparse dependency for CLI argument parsing
Split the monolithic AllSystemInfo handler into smaller, focused functions:
- Extract streamSystemInfo for channel consumption
- Add queueSystemInfo for safe non-blocking queue operations
- Create collectSystemInfoRound for parallel agent data collection
- Implement handleRoundResult for consistent round result processing
- Replace custom exponential backoff with cenkalti/backoff/v5 library
This improves code maintainability and separates concerns within the metrics API endpoint.
Add fallback logic to rewriteImplMarkdown to traverse parent directories when resolving directory links, allowing paths like "internal/watcher/events" to resolve via their parent "internal/watcher" if no exact match exists. Also update built-in import to use explicit "node:" protocol.
- Commented out the SetConnectionClose method to enable connection reuse for HTTP requests, improving performance.
- This is a follow up commit to be9af03a1e setting MaxConnsPerHost to 1000
Add automatic container cleanup for benchmark target using trap to ensure
containers are torn down after benchmarking completes. Also update the
gen-api-types output directory to src/lib and remove the prettier step.
- Exclude gin.Context.Error from errcheck for error handler pattern
- Add godoclint with ignore pattern for internal/api/v1
- Remove gomoddirectives replace-allow-list
- Disable govet fieldalignment check
- Add QF1008 to staticcheck exceptions for embedded field clarity
- Simplify exclusion paths and disable test running
Add a reason parameter throughout the ACL system to track and log why
each IP was allowed or denied. This provides better visibility into
ACL decisions by recording specific reasons such as "allowed by
allow_local rule", "blocked by deny rule: [rule]", or "deny by default".
Changes include:
- Add reason field to checkCache and ipLog structs
- Update LogACL interface and implementations to accept reason
- Generate descriptive reasons for all ACL decision paths
- Include reason in console log output
fix(oidc): ignore OIDC middleware when OIDC is not enabled
The OIDC middleware now gracefully handles the case when OIDC is not enabled by:
- Returning early in the before() hook when IsOIDCEnabled() is false
- Logging an error instead of returning an error in finalize() when OIDC is not configured
Block non-GET and WebSocket requests through the OIDC middleware with a 403 Forbidden response.
This avoids API clients receiving unexpected redirect and HTML response.
Added a log to hint user to add bypass rule if needed.
Also fix logout handler to not short-circuit middleware chain.
Add a DisplayNameKey struct to pass display names from routes through the task
parent hierarchy to the health monitor. This allows the health monitor to use
more descriptive names for logging instead of internal task names.
BREAKING CHANGE: The monitor.DisplayNameKey struct is now part of the public API
- Changed the response type of the health endpoint to use a new HealthMap type for better clarity.
- Updated the health information retrieval method to GetHealthInfoWithoutDetail for improved accuracy in the response.
- Adjusted Swagger documentation to reflect the new response structure.
- Add `minify-js` target to Makefile that minifies JavaScript files in internal/
- Update `build` and `run` targets to run minification before building/running
- Replace `html/loading.js` embed with `html/loading-min.js` in loading_page.go
This change optimizes the loading page performance by embedding minified JavaScript
instead of the full source file. The Makefile now automatically generates minified
versions of JavaScript files during build and run operations.
This refactor replaces the custom event broadcasting implementation with the centralized goutils/events package across idlewatcher.
The changes include simplifying the WakeEvent struct, removing manual SSE channel management, and adopting a cleaner event history pattern.
The frontend JavaScript has been updated to work with the new event format.
- Replace gperr.Error return types with standard error across test files
- Replace gperr.New with errors.New in validation and serialization tests
- Update API documentation in README files to use error instead of gperr.Error
- Simplify string parsing using strings.Cut in docker/label.go
- Update benchmarks to use NewTestEntrypoint and remove task package dependency
Updates VMID parameter and field types from int to uint64 throughout the Proxmox provider implementation,
including API request structures, provider structs, client methods, and LXC-related functions.
Also updates string conversion calls from strconv.Itoa to strconv.FormatUint.
Move CertInfo struct from provider.go to internal/autocert/types/cert_info.go and
replace global ActiveProvider.Load() with context-based autocertctx.FromCtx() pattern in API handlers.
This improves separation of concerns and eliminates global state dependency in request handling.
- Updated trunk plugin reference from v1.7.2 to v1.7.4
- Upgraded Go runtime from 1.24.3 to 1.25.6
- Updated linting tools: checkov to 3.2.500, golangci-lint2 to 2.8.0, actionlint to 1.7.10, osv-scanner to 2.3.2, oxipng to 10.1.0, and prettier to 3.8.1
- Updated trufflehog to 3.93.1
- Added ignore rule for all linters in internal/api/v1/docs/**
This is a large-scale refactoring across the codebase that replaces the custom
`gperr.Error` type with Go's standard `error` interface. The changes include:
- Replacing `gperr.Error` return types with `error` in function signatures
- Using `errors.New()` and `fmt.Errorf()` instead of `gperr.New()` and `gperr.Errorf()`
- Using `%w` format verb for error wrapping instead of `.With()` method
- Replacing `gperr.Subject()` calls with `gperr.PrependSubject()`
- Converting error logging from `gperr.Log*()` functions to zerolog's `.Err().Msg()` pattern
- Update NewLogger to handle multiline error message
- Updating `goutils` submodule to latest commit
This refactoring aligns with Go idioms and removes the dependency on
custom error handling abstractions in favor of standard library patterns.
Updated logging statements across multiple files to utilize EmbedObject for enhanced context in log messages. This change improves the readability and consistency of log outputs, particularly in health monitoring and route validation processes.
- Introduced `NewTestRoute` function to simplify route creation in benchmark tests.
- Replaced direct route validation and starting with error handling using `require.NoError`.
- Updated server retrieval to use `common.ProxyHTTPAddr` for consistency.
- Improved logging for HTTP route addition errors in `AddRoute` method.
* fix(tcp): wrap proxy proto listener before acl
* refactor(entrypoint): propagate errors from route registration and stream serving
* fix(docs): correct swagger and package README
Send early HTTP 100 Continue response before processing to avoid
timeouts, and propagate request context through the verification flow
for proper cancellation handling.
The ObtainCertAll method was missing a call to rebuildSNIMatcher(),
which could leave the SNI configuration stale after certificate
renewals. Both ObtainCertIfNotExistsAll and ObtainCertAll now
consistently rebuild the SNI matcher after their operations.
This was introduced in 3ad6e98a17,
not a bug fix for previous version
Add ExcludedReasonYAMLAnchor to explicitly identify routes with "x-" prefix
used for YAML anchors and references. These routes are removed before
validation.
Add port validation to return an unhealthy result with descriptive
message when URL has no port specified, preventing potential dialing
errors on zero port.
Previously, ACME keys were stored at a single default path regardless of
which CA directory URL was configured. This caused key conflicts when
using multiple different ACME CAs.
Now, the key path is derived from a SHA256 hash of the CA directory URL,
allowing each CA to have its own key file:
- Default CA (Let's Encrypt): certs/acme.key
- Custom CA: certs/acme_<url_hash_16chars>.key
This enables running certificates against multiple ACME providers without
key collision issues.
- ObtainCertIfNotExistsAll longer fail on fs.ErrNotExists
- Separate public LoadCertAll (loads all providers) from private loadCert
- LoadCertAll now uses allProviders() for iteration
- Updated tests to use LoadCertAll
Add context parameter to TCP/UDP stream health checks and client constructors
for proper cancellation and deadline propagation. Switch from encoding/json
to sonic for faster JSON unmarshaling.
Standardize HTTP client timeouts to 5 seconds
across agent pool and health check.
Proxmox validation errors are now logged and ignored rather than
causing route validation to fail, allowing routes to function even
when proxmox integration encounters issues.
- Extract proxmox validation into dedicated validateProxmox() method
- Log warnings/errors instead of returning validation errors
- Add warning when proxmox config exists but no node/resource found
- Fix bug in mapUnmarshalValidate where checkValidateTag parameter
was incorrectly negated when passed to Convert()
- Remove obsolete validateWithValidator helper function
Adds a new `/route/validate` endpoint that accepts YAML-encoded route
configurations for validation. Supports both synchronous HTTP requests
and real-time streaming via WebSocket for interactive validation workflows.
Changes:
- Implement Validate handler with YAML binding in route/validate.go
- Add WebSocket manager for streaming validation results
- Register GET/POST routes in handler.go
- Regenerate Swagger documentation
- Introduce SubstituteEnvReader that replaces ${VAR} patterns with environment variable
values, properly quoted for JSON/YAML compatibility
- Gin bindings (JSON/YAML) that use the environment-substituting reader
for request body binding with validation support
Replace YAML-specific functions with generic ones accepting unmarshaler/marshaler
function parameters. This enables future support for JSON and other formats
while maintaining current YAML behavior.
- UnmarshalValidateYAML -> UnmarshalValidate(unmarshalFunc)
- UnmarshalValidateYAMLXSync -> UnmarshalValidateXSync(unmarshalFunc)
- SaveJSON -> SaveFile(marshalFunc)
- LoadJSONIfExist -> LoadFileIfExist(unmarshalFunc)
- Add UnmarshalValidateReader for reader-based decoding
Testing: all 12 staged test files updated to use new API
Add Validate() method to NodeConfig that implements the CustomValidator
interface. The method checks all services and files for invalid shell
metacharacters (&, $(), etc.) to prevent shell injection attacks.
Testing: Added validation_test.go with 6 table-driven test cases covering
valid inputs and various shell metacharacter injection attempts.
- Format tail command with fallback retry logic
- Add /var/log/messages fallback when no services specified
Improves log viewing reliability on systems without systemd support.
Replace synchronous log writing with zerolog's diode-based non-blocking
writer to prevent logging from blocking the main application during
log bursts. The diode writer buffers up to 1024 messages and logs a
warning when messages are dropped.
- Extract multi-writer logic into separate `multiWriter` function
- Wrap with `diode.NewWriter` for async buffering
- Update both `NewLogger` and `NewLoggerWithFixedLevel` to use diode
Streamlined the `loadRouteProviders()` function by:
- Replacing channel-based concurrency with a simpler sequential registration pattern after agent initialization
- Using `gperr.NewGroup` and `gperr.NewBuilder` for more idiomatic error handling
- Adding mutex protection for concurrent result building
- Removing the `storeProvider` helper method
Replaced hardcoded 10-second initialization timeout with a configurable `INIT_TIMEOUT` environment variable.
The new default is 1 minute, allowing operators to adjust startup behavior based on their infrastructure requirements.
Introduced a new session refresh mechanism in the Proxmox configuration to ensure the API session remains active. This includes:
- Added `SessionRefreshInterval` constant for configurable session refresh timing.
- Implemented `refreshSessionLoop` method to periodically refresh the session and handle errors with exponential backoff.
This enhancement improves the reliability of interactions with the Proxmox API by preventing session expiry.
Add new `/proxmox/tail` API endpoint for streaming file contents from Proxmox
nodes and LXC containers via WebSocket. Extend journalctl endpoint to support
filtering by multiple services simultaneously.
Changes:
- Add `GET /proxmox/tail` endpoint supporting node-level and LXC container file tailing
- Change `service` parameter from string to array in journalctl endpoints
- Add input validation (`checkValidInput`) to prevent command injection
- Refactor command formatting with proper shell quoting
Security: All command inputs are validated for dangerous characters before
- Add BaseURL field to Client for node-level route configuration
- Change VMID from int to *int to support three states:
- nil: auto-discover node or VM from hostname/IP/alias
- 0: node-level route (direct to Proxmox node API)
- >0: LXC/QEMU resource route with container control
- Change Service string to Services []string for multi-service support
- Implement proper node-level route handling: HTTPS scheme,
hostname from node BaseURL, default port 8006
- Move initial UpdateResources call to Init before starting loop
- Move proxmox auto-discovery earlier in route validation
BREAKING: NodeConfig.VMID is now a pointer type; NodeConfig.Service
renamed to Services (backward compatible via alias)
Refactored the journalctl API to accept `node`, `vmid`, and `service` parameters as query strings in addition to path parameters. Added a new route `/proxmox/journalctl` that accepts all parameters via query string while maintaining backward compatibility with existing path-parameter routes.
- Changed `JournalctlRequest` struct binding from URI-only to query+URI
- Simplified Swagger documentation by consolidating multiple route definitions
- Existing path-parameter routes remain functional for backward compatibility
Decouple the types package from the internal/proxmox package by defining
a standalone ProxmoxConfig struct. This reduces circular dependencies
and allows the types package to define its own configuration structures
without importing the proxmox package.
The route validation logic now converts between types.ProxmoxConfig and
proxmox.NodeConfig where needed for internal operations.
Added sections for Proxmox integration, including automatic route binding, WebUI management, and API endpoints. Updated existing content to reflect LXC lifecycle control and real-time logging capabilities for both Docker and Proxmox environments.
Add new `/proxmox/stats/{node}` API endpoint for retrieving Proxmox node
statistics in JSON format. The endpoint returns kernel version, CPU
usage/model, memory usage, rootfs usage, uptime, and load averages.
The existing `/proxmox/stats/{node}/{vmid}` endpoint has been corrected `VMStats` to return`text/plain` instead of `application/json`.
Both endpoints support WebSocket streaming for real-time stats updates
with a 1-second poll interval.
This change enables Proxmox node-level operations without requiring a specific
LXC container VMID.
**Features added:**
- New `/proxmox/journalctl/{node}` API endpoint for streaming node journalctl
- Route configuration support for Proxmox nodes (VMID = 0)
- `ReverseLookupNode` function for node discovery by hostname/IP/alias
- `NodeJournalctl` method for executing journalctl on nodes
**Behavior changes:**
- VMID parameter in journalctl endpoints is now optional
- Routes targeting nodes (without specific containers) are now valid
**Bug fixes:**
- Fixed error message variable reference in route validation
The LXCCommand method contained duplicate websocket handling logic for connecting to Proxmox's VNC terminal proxy. This refactoring extracts the common websocket connection, streaming, and cleanup logic into a new NodeCommand method on the Node type, allowing LXCCommand to simply format the pct command and delegate.
The go-proxmox submodule was also updated to access the NewNode constructor, which provides a cleaner API for creating node instances with the HTTP client.
- Moves ~100 lines of websocket handling from lxc_command.go to node.go
- Adds reusable NodeCommand method for executing commands via VNC websocket
- LXCCommand now simply calls NodeCommand with formatted command
- Maintains identical behavior and output streaming semantics
Added a function to close idle HTTP connections in the LXCCommand method. This addresses potential goroutine leaks caused by the go-proxmox library's TermWebSocket not closing underlying HTTP/2 connections. The websocket closer is now wrapped to ensure proper cleanup of transport connections when the command execution is finished.
Add a 10-second timeout mechanism during application initialization. If initialization
fails to complete within the timeout window, the application logs a fatal error and exits.
This prevents the proxy from becoming unresponsive during startup due to blocking operations
in parallel initialization tasks (DNS providers, icon cache, system info poller, middleware
loading, Docker client, API server, debug server, config watcher).
The timeout guard uses a background goroutine that listens for either a completion signal
(via closing the done channel) or the timeout expiration, providing a safety net for
long-running or blocked initialization scenarios.
Add start, stop, and restart endpoints for LXC containers via the Proxmox API:
- POST /api/v1/proxmox/lxc/:node/:vmid/start
- POST /api/v1/proxmox/lxc/:node/:vmid/stop
- POST /api/v1/proxmox/lxc/:node/:vmid/restart
Added new Proxmox journalctl endpoint `/journalctl/:node/:vmid` for viewing all
journalctl output without requiring a service name. Made the service parameter
optional across both endpoints.
Introduced configurable `limit` query parameter (1-1000, default 100) to both
proxmox journalctl and docker logs APIs, replacing hardcoded 100-line tail.
Added container status check in LXCCommand to prevent command execution on
stopped containers, returning a clear status message instead.
Refactored route validation to use pre-fetched IPs and improved References()
method for proxmox routes with better alias handling.
- Add VMResource wrapper type with cached IP addresses for efficient lookups
- Implement concurrent IP fetching during resource updates (limited concurrency)
- Add ReverseLookupResource for discovering VMs by IP, hostname, or alias
- Prioritize interfaces API over config for IP retrieval (offline container fallback)
- Enable routes to auto-discover Proxmox resources when no explicit config provided
- Fix configuration type from value to pointer slice for correct proxmox client retrievel
- Ensure Proxmox providers are initialized before route validation
Implement a new API endpoint to retrieve real-time statistics for Proxmox
LXC containers, similar to `docker stats` functionality.
Changes:
- Add `GET /api/v1/proxmox/stats/:node/:vmid` endpoint with HTTP and WebSocket support
- Implement resource polling loop to cache VM metadata every 3 seconds
- Create `LXCStats()` method with streaming (websocket) and single-shot modes
- Format output as: STATUS|CPU%|MEM USAGE/LIMIT|MEM%|NET I/O|BLOCK I/O
- Add `GetResource()` method for efficient VM resource lookup by kind and ID
- Fix task creation bug using correct client reference
Example response:
running|31.1%|9.6GiB/20GiB|48.87%|4.7GiB/3.3GiB|25GiB/36GiB
Centralize Proxmox node configuration by moving `ProxmoxConfig` from `internal/types/idlewatcher.go` to a new `NodeConfig` struct in `internal/proxmox/node.go`.
- Add `proxmox` field to route; allowing `proxy.app.proxmox` labels and corresponding route file config
- Added `service` optional field to NodeConfig for service identification
- Integrated Proxmox config directly into Route struct with proper validation
- Propagate Proxmox settings to Idlewatcher during route validation
- Updated swagger documentation to reflect schema changes
Add new /api/v1/proxmox/journalctl/:node/:vmid/:service endpoint that
streams real-time journalctl output from Proxmox LXC containers via
WebSocket connection. This enables live monitoring of container services
from the GoDoxy WebUI.
Implementation includes:
- New proxmox API handler with path parameter validation
- WebSocket upgrade for streaming output
- LXCCommand helper for executing commands over Proxmox VNC websocket
- LXCJournalctl wrapper for convenient journalctl -u service -f invocation
- Updated API documentation with proxmox integration
refactor(proxmox): support for PAM authentication
- Added support for username and password authentication alongside existing token-based authentication.
- Updated validation rules to require either token or username/password for authentication.
- Modified the Init function to handle session creation based on the selected authentication method.
- Increased timeout duration for context in the Init function.
Add the go-proxmox library as a Git submodule to enable Proxmox
integration for container/VM management.
Submodule: https://github.com/yusing/go-proxmox
- Removed `display_name`, `is_docker`, and `is_excluded` fields from the `RouteAggregate` struct and corresponding Swagger documentation.
- Updated references in the README and code to reflect the removal of these fields, ensuring consistency across the codebase.
- Updated route retrieval in the API and idle watcher to use GetIncludeExcluded, allowing for the inclusion of excluded routes.
- Simplified the route status aggregation logic by directly using GetIncludeExcluded for display name resolution.
- Removed redundant code that separately handled excluded routes, streamlining the route management process.
- Introduced a new GET endpoint `/docker/stats/:id` to fetch statistics for a specified container by its ID or route alias.
- Implemented the `Stats` function in the `dockerapi` package to handle the request and return container stats in both JSON and WebSocket formats.
- Added error handling for invalid requests and container not found scenarios.
- Added a step to checkout the repository for accurate tag resolution.
- Implemented logic to determine the build version based on the Git reference type, supporting tags and branch names.
- Updated the Docker build arguments to use the computed version for better versioning in images.
Removes the embedded HTTP handler and WebSocket streaming capability from the
in-memory logger, leaving only the core io.Writer interface and event subscription
via Events(). Simplifies buffer management by eliminating position-based tracking
and using slices.Clone() for safe message passing to listeners.
- Removes HandlerFunc(), ServeHTTP(), wsInitial(), wsStreamLog() methods
- Removes logEntryRange struct and connChans map (no longer needed)
- Refactors buffer field from embedded to explicit buf with named mutexes
- Adds buffered channel (64) for event listeners to prevent blocking
- Improves concurrency with double-checked locking in truncation logic
Refactored the pool implementation to use a tombstone-based deletion strategy
instead of immediate removal. This allows correct logging "reload"
instead of "removed" + "added" when an item is quickly deleted
and re-added within a short time window.
Changes:
- Items are now marked as tombstones upon deletion and retained for 1 second
- Added `PurgeExpiredTombs()` method for cleanup of expired tombstones
- Updated `Get`, `Iter`, and `Slice` to skip tombstoned entries
- Updated `Del` and `DelKey` to cleanup tombstones when exceeding threshold
- `AddIfNotExists` can now "reload" recently deleted items within the TTL
- Added tomb counter for tracking active tombstones and triggering purge
- Updated NewHandler function to accept a requireAuth parameter for authentication control.
- Introduced a new local API server that allows unauthenticated access when LocalAPIHTTPAddr is set.
- Adjusted server startup logic to handle both authenticated and unauthenticated API routes.
Major refactoring of the access logging infrastructure to improve code organization and add proper console/stdout logging support.
- Renamed `Writer` interface to `File` and consolidated with `SupportRotate`
- Renamed `Log(req, res)` to `LogRequest(req, res)` for clarity
- Added new `ConsoleLogger` with zerolog console writer for formatted stdout output
- Moved type definitions to new `types.go` file
- Changed buffer handling from `[]byte` returns to `*bytes.Buffer` parameters
- Renamed internal files for clarity (`access_logger.go` → `file_access_logger.go`)
- Fixed fileserver access logging timing: moved logging after handler execution with defer
- Correct response handling in Fileserver
- Remove deprecated field `buffer_size`
- Simplify and removed unnecessary code
All callers have been updated to use the new APIs.
Replace per-scan byte slice allocations with a sized buffer pool,
significantly reducing memory pressure during log file scanning.
- Add Release() method to return buffers to pool (callers must invoke)
- Remove Reset() method - create new scanner instead for simpler lifecycle
- Refactor chunk prepending to reuse pooled buffers instead of append
Benchmark results show allocations dropped from ~26k to 1 per scan
for small chunk sizes, with better throughput.
BREAKING CHANGE: Reset() removed; callers must call Release() and
create a new BackScanner instance instead.
- Changed error returned for invalid arguments in CommandPass and CommandPassAlt to ErrExpectNoArg.
- Added validation to ensure response handlers are the last commands in the execution order.
- Updated error messages for command sequence validation to clarify requirements for terminating and bypass commands.
- Add OnDefault rule type that matches when no other rules match
- Add validation to prevent multiple default rules
- Fix typo: extension → extensions in route config JSON tag
Enhanced the ConvertSlice function to include validation for destination slices that implement the CustomValidator interface. If validation fails, errors are collected and returned, ensuring data integrity during slice conversion.
- Convert intra-repo README links to VitePress routes for SPA navigation
- Rewrite source file references (e.g., config.go:29) to GitHub blob links
- Makefile now passes REPO_URL to update-wiki for link rewriting
- Correct agent README.md file links from full to relative paths
- skip introduction.md when syncing
The icon fetching logic now checks if the target service is healthy before
attempting to fetch icons. If the health monitor reports an unhealthy status,
the function returns HTTP 503 Service Unavailable instead of proceeding.
Additionally, the icon cache lookup now includes infinite retry logic with a
15-second backoff interval, improving resilience during transient service
outages. Previously, failed lookups would not be retried.
The `route` interface was extended with a `HealthMonitor()` method to support
the health check functionality.
* **New Features**
* Multiplexed TLS port: HTTP API and a custom stream protocol can share one port via ALPN.
* Agent-side TCP and DTLS/UDP stream tunneling with health-check support and runtime capability detection.
* Agents now advertise per-agent stream support (TCP/UDP).
* **Documentation**
* Added comprehensive stream protocol documentation.
* **Tests**
* Extended integration and concurrency tests covering multiplexing, TCP/UDP streams, and health checks.
* **Chores**
* Compose/template updated to expose both TCP and UDP ports.
- Added information about the loading page (HTML + JS + CSS) and the SSE endpoint for wake events.
- Clarified the health monitor implementation and readiness tracking in the architecture overview.
- Correct state machine syntax.
- Introduced a new `update-wiki` script to automate the synchronization of implementation documentation from the repository to the wiki.
- Added necessary configuration files including `package.json`, `tsconfig.json`, and `.gitignore` for the new script.
- Updated the Makefile to include a target for running the `update-wiki` script.
- Correct BaseContext nil check in Context() method
- Move NewMonitor from monitor.go to new.go
- Export ErrDockerHealthCheckFailedTooManyTimes and add ErrDockerHealthCheckNotAvailable
- Return ErrDockerHealthCheckNotAvailable when container has no health check configured
- Only log first docker health check failure and skip logging for ErrDockerHealthCheckNotAvailable
- Use mon.Context() instead of mon.task.Context() to avoid nil panic
- Move health check implementations from monitor/ to new check/ package
- Add h2c, tcp4/6, udp4/6 scheme support to agent health check API
- Add timeout URL parameter to agent health check endpoint
- Remove unused agent dependencies (dnsproviders, lego, various cloud SDKs)
- Use net.JoinHostPort instead of fmt.Sprintf for port joining
Moved non-agent-specific logic from agent/pkg/agent/ to internal/agentpool/:
- pool.go: Agent pool management (Get, Add, Remove, List, Iter, etc.)
- http_requests.go: HTTP utilities (health checks, forwarding, websockets, reverse proxy)
- agent.go: Agent struct with HTTP client management
This separates general-purpose pool management from agent-specific configuration,
improving code organization and making the agent package focused on agent config only.
- Introduced a new `Bind` field in the route configuration to specify the address to listen on for TCP and UDP routes.
- Defaulted the bind address to "0.0.0.0" if not provided.
- Enhanced validation to ensure the bind address is a valid IP.
- Updated stream initialization to use the correct network type (tcp4/tcp6 or udp4/udp6) based on the bind address.
- Refactored stream creation functions to accept the network type as a parameter.
- Introduced a new method `GetCertInfos` to fetch details of all available certificates.
- Updated the `Info` handler to return an array of `CertInfo` instead of a single certificate.
- Improved error handling for cases with no available certificates.
- Refactored related error messages for clarity.
- Updated various files to utilize gperr.Group for cleaner concurrency error handling.
- Removed sync.WaitGroup usage, simplifying the code structure.
- Ensured consistent error reporting across different components.
Extra providers were not being properly initialized during NewProvider(),
causing certificate registration and renewal scheduling to be skipped.
- Add ConfigExtra type with idx field for provider indexing
- Add MergeExtraConfig() for inheriting main provider settings
- Add setupExtraProviders() for recursive extra provider initialization
- Refactor NewProvider to return error and call setupExtraProviders()
- Add provider-scoped logger with "main" or "extra[N]" name
- Add batch operations: ObtainCertIfNotExistsAll(), ObtainCertAll()
- Add ForceExpiryAll() with completion tracking via WaitRenewalDone()
- Add RenewMode (force/ifNeeded) for controlling renewal behavior
- Add PrintCertExpiriesAll() for logging all provider certificate expiries
Summary of staged changes:
- config.go: Added ConfigExtra type, MergeExtraConfig(), recursive validation with path uniqueness checking
- provider.go: Added provider indexing, scoped logger, batch cert operations, force renewal with completion tracking, RenewMode control
- setup.go: New file with setupExtraProviders() for proper extra provider initialization
- setup_test.go: New tests for extra provider setup
- multi_cert_test.go: New tests for multi-certificate functionality
- renew.go: Updated to use new provider API with error handling
- state.go: Updated to handle NewProvider error return
Multi-certificate, SNI matching with exact map and suffix tree
Add support for multiple TLS certificates with SNI-based selection. The
root provider maintains a single centralized SNI matcher that uses an
exact match map for O(1) lookups, falling back to a suffix tree for
wildcard matching.
Key features:
- Add `Extra []Config` field to autocert.Config for additional certificates
- Each extra entry must specify unique `cert_path` and `key_path`
- Extra certs inherit main config (except `email` and `extra` fields)
- Extra certs participate in ACME obtain/renew cycles independently
- SNI selection precedence: exact match > wildcard match, main > extra
- Single centralized SNI matcher on root provider rebuilt after cert changes
The SNI matcher structure:
- Exact match map: O(1) lookup for exact domain matches
- Suffix tree: Efficient wildcard matching (e.g., *.example.com)
Implementation details:
- Provider.GetCert() now uses SNI from ClientHelloInfo for selection
- Main cert is returned as fallback when no SNI match is found
- Extra providers are created as child providers with merged configs
- SNI matcher is rebuilt after Setup() and after ObtainCert() completes
- Updated dev.compose.yml to define a new bench service that serves 4096 bytes of random data.
- Modified configurations for Traefik, Caddy, and Nginx to route traffic to the new bench service.
- Added Dockerfile and Go application for the bench server, including necessary Go modules.
- Updated benchmark script to target the new bench service endpoint.
- Replaced req.Clone with req.WithContext and url/header/trailer cloning.
- Added conditional handling for "Expect" headers to manage 1xx responses with appropriate tracing.
- Introduced a sync.Pool for ResponseRecorder to optimize memory usage.
- Updated ServeHTTP method to utilize the new GetResponseRecorder and PutResponseRecorder functions.
- Adjusted NewResponseRecorder to leverage the pooling mechanism.
- Added benchmark services (whoami, godoxy, traefik, caddy, nginx) to dev.compose.yml.
- Introduced a new benchmark.sh script for load testing using wrk and h2load.
- Updated Makefile to include a benchmark target for easy execution of the new script.
- Updated health-related functions to return simplified health information.
- Introduced HealthStatusString type for correct swagger and schema generation.
- Refactored HealthJSON structure to utilize the new HealthStatusString type.
- Modified functions to accept context.Context as a parameter for better context management.
- Updated Init methods in Proxmox and Config to use the provided context.
- Adjusted UpdatePorts and NewProxmoxProvider to utilize the context for operations.
- Added ShortLinkMatcher to handle short link routing.
- Integrated short link handling in Entrypoint.
- Introduced tests for short link matching and dispatching.
- Configured default domain suffix for subdomain aliases.
- Added NextProtos to TLSConfig to prefer HTTP/2 and fallback to HTTP/1.1.
- Configured the server to handle HTTP/2 connections, with error logging for configuration failures.
- Added support for health checks using the h2c scheme.
- Refactored common header setting into a dedicated function.
- Updated CheckHealth method to differentiate between HTTP and h2c checks.
- Added logic to strip the trailing :port from the host when searching for routes.
- Updated findRouteByDomains function to ensure consistent host formatting.
- Added related tests
- Introduced CommandRoute to handle routing requests to other defined routes.
- Added validation to ensure a single argument is provided for the route.
- Implemented command handler to serve the specified route or return a 404 error if not found.
- Updated route iteration to include all routes, including excluded ones.
- Renamed existing functions for clarity.
- Adjusted health info retrieval to reflect changes in route iteration.
- Improved route management by adding health monitoring capabilities for excluded routes.
- Added `listenDebugServer` function to handle debug requests.
- Introduced table based debug page with different functionalities.
- Updated Makefile to use `scc` for code analysis instead of `cloc`.
- Introduced a map for idlewatcher labels to simplify the loading of configuration values.
- Simplify logic to check for the presence of an idle timeout and handle dependencies.
- Updated the wakeFromHTTP method to send a 100 Continue response to prevent client wait-header timeout.
- Implemented logic for non-HTML requests to wait for the container to become ready, returning an error message if it times out, or redirecting if successful.
- Adjusted the waitForReady method to return true upon receiving a ready notification.
- Introduced a new checker for HTTP protocols (http, https, h3) in the routing rules.
- Added corresponding test cases to validate protocol matching behavior in requests.
- Updated FindIcon to accept an additional variant parameter for improved icon fetching.
- Adjusted FavIcon and GetFavIconFromAlias functions to utilize the new variant handling logic.
- Introduced a new Variant field in GetFavIconRequest to specify icon variants (light/dark).
- Updated GetFavIconFromAlias function to handle the variant when fetching favicons.
- Added WithVariant method in IconURL to manage icon variants effectively.
- Updated the Hijack method in LazyResponseModifier and ResponseModifier to return a wrapped error for unsupported hijacking.
- Added a nil check in LazyResponseModifier's Unwrap method to ensure safe access to the underlying ResponseWriter.
- Removed unnecessary requestInternal struct and directly accessed the context field of http.Request.
- Simplified the initialization of ctxFieldOffset.
- Modified Config structs in various packages to replace string fields with strutils.Redacted to prevent logging sensitive information.
- Updated serialization methods to accommodate new data types.
- Adjusted API token handling in Proxmox configuration.
- Introduced origContentLength and bodyModified fields to track original content length and body modification status.
- Updated ContentLength and ContentLengthStr methods to return accurate content length based on body modification state.
- Adjusted Write and FlushRelease methods to ensure proper handling of Content-Length header.
- Modified middleware to use the new ContentLengthStr method.
- Added multiple test cases for the ExpandWildcard function to cover various scenarios including basic wildcards, no wildcards, empty labels, and YAML configurations.
- Improved handling of nested maps and invalid YAML inputs.
- Ensured that explicit labels and reference aliases are correctly processed and expanded.
- Moved health check constants from common package alongside type definition.
- Updated health check configuration to use struct directly instead of pointers.
- Introduced global default health check config
Refactor ServeHTTP to properly handle response body mutations by:
- Using ResponseModifier to capture response before modification
- Reading body content and allowing middleware to modify it
- Writing modified body back if changed during modification
- Ensuring proper order: RequestModifier before, ResponseModifier after next()
Previously, httputils.NewModifyResponseWriter did not correctly handle
body mutations. The new implementation captures the full response,
allows modification via modifyResponse, and properly writes back any
changes to the body.
Add BodyReader() and SetBody() methods to ResponseModifier to support
reading and replacing response body content.
- Introduced a new handler for unknown paths in the OIDCProvider to prevent fallback to the default login page.
- Forced OIDC middleware to treat unknown path as logic path to redirect to login property when bypass rules is declared.
- Refactored OIDC path constants.
- Updated checkBypass middleware to enforce path prefixes for bypass rules, ensuring proper request handling.
- Introduced a temporary fix for loading the "webui.yml" rule preset when the container image is "godoxy-frontend".
- Added error handling for cases where the rule preset is not found.
- Marked the change with a FIXME comment to investigate the underlying issue in the future.
- Refactor load balancer interface to separate server selection (ChooseServer) from request handling
- Add cookie-based sticky session support with configurable max-age and secure cookie handling
- Integrate idlewatcher requests with automatic sticky session assignment
- Improve algorithm implementations:
* Replace fnv with xxhash3 for better performance in IP hash and server keys
* Add proper bounds checking and error handling in all algorithms
* Separate concerns between server selection and request processing
- Add Sticky and StickyMaxAge fields to LoadBalancerConfig
- Create dedicated sticky.go for session management utilities
This major overhaul of the idlewatcher system introduces a modern, real-time loading experience with Server-Sent Events (SSE) streaming and improved error handling.
- **Real-time Event Streaming**: New SSE endpoint (`/$godoxy/wake-events`) provides live updates during container wake process
- **Enhanced Loading Page**: Modern console-style interface with timestamped events and color-coded status messages
- **Improved Static Asset Management**: Dedicated paths for CSS, JS, and favicon to avoid conflicting with upstream assets
- **Event History Buffer**: Stores wake events for reconnecting clients and debugging
- Refactored HTTP request handling with cleaner static asset routing
- Added `WakeEvent` system with structured event types (starting, waking_dep, dep_ready, container_woke, waiting_ready, ready, error)
- Implemented thread-safe event broadcasting using xsync.Map for concurrent SSE connections
- Enhanced error handling with detailed logging and user-friendly error messages
- Simplified loading page template system with better asset path management
- Fixed race conditions in dependency waking and state management
- Removed `common.go` functions (canceled, waitStarted) - moved inline for better context
- Updated Waker interface to accept context parameter in Wake() method
- New static asset paths use `/$godoxy/` prefix to avoid conflicts
- Console-style output with Fira Code font for better readability
- Color-coded event types (yellow for starting, blue for dependencies, green for success, red for errors)
- Automatic page refresh when container becomes ready
- Improved visual design with better glassmorphism effects and responsive layout
- Real-time progress feedback during dependency wake and container startup
This change transforms the static loading page into a dynamic, informative experience that keeps users informed during the wake process while maintaining backward compatibility with existing routing behavior.
- Added a check to ensure the buffer's capacity is sufficient before reusing it.
- Included a FIXME comment to address an unexpected condition in buffer allocation.
- Changed API_SECRET to API_JWT_SECRET in dev.compose.yml
- Updated base image from alpine to debian in dev.Dockerfile
- Upgraded golang version from 1.25.2 to 1.25.3 in Dockerfile
- Replace template syntax ({{ .Request.Method }}) with $-prefixed variables ($req_method)
- Implement custom variable parser with static ($req_method, $status_code) and dynamic ($header(), $arg(), $form()) variables
- Replace templateOrStr interface with templateString struct and ExpandVars methods
- Add parser improvements for reliable quote handling
- Add new error types: ErrUnterminatedParenthesis, ErrUnexpectedVar, ErrExpectOneOrTwoArgs
- Update all tests and help text to use new variable syntax
- Add comprehensive unit and benchmark tests for variable expansion
- Remove BytesPoolWithMemory; split into UnsizedBytesPool and 11-tier SizedBytesPool
- Track buffer capacities with xsync Map to prevent capacity leaks
- Improve buffer reuse: split large buffers and put remainders back in pool
- Optimize small buffers to use unsized pool
- Expand test coverage and benchmarks for various allocation sizes
- Refactors the fmtMessage function to use strings.Builder
- Simplifies multi-writer creation with a helper function
- Updates the new console writer initialization pattern
- Moves InitLogger function to the top
- Fixed NewLoggerWithFixedLevel
* Add comprehensive post-request rules support for response phase
* Enable response body, status, and header manipulation via set commands
* Refactor command handlers to support both request and response phases
* Implement response modifier system for post-request template execution
* Support response-based rule matching with status and header checks
* Add comprehensive benchmarks for matcher performance
* Refactor authentication and proxying commands for unified error handling
* Support negated conditions with !
* Enhance error handling, error formatting and validation
* Routes: add `rule_file` field with rule preset support
* Environment variable substitution: now supports variables without `GODOXY_` prefix
* new conditions:
* `on resp_header <key> [<value>]`
* `on status <status>`
* new commands:
* `require_auth`
* `set resp_header <key> <template>`
* `set resp_body <template>`
* `set status <code>`
* `log <level> <path> <template>`
* `notify <level> <provider> <title_template> <body_template>`
- Remove MultiWriter complexity and use single writer interface
- Disable buffering for stdout logging to ensure immediate output
- Replace slice-based closer/rotate support with type assertions
- Simplify rotation result handling by passing result pointer
- Update buffer size constants and improve memory management
- Remove redundant stdout_logger.go and multi_writer.go files
- Fix test cases to match new rotation API signature
- Remove sync.RWMutex and Cache struct in favor of atomic Value
- Implement background goroutine for periodic icon updates
- Add backward compatibility for old cache format
- Improve concurrent access to icon cache
- Simplify ListAvailableIcons()
- Add tmpLogBuf and tmpLog fields to capture config loading logs
- Flush temporary logs only when reload succeeds
- Extract NewLogger function for creating custom loggers
- Update State interface to include FlushTmpLog method
- Add Notify configuration with To field and interval
- Track allowed/blocked IP counts per address
- Send periodic summary notifications with access statistics
- Optimize logging with channel-based processing for concurrent safety
- Replace heap allocation with stack-allocated array in Entries.Get() method.
- Also refactor uptime module to use value types instead of pointer types.
- Added "Star History" section with a chart link.
- Replaced outdated screenshots with new "Routes" and "Servers" images.
- Removed references to deleted screenshots for better clarity.
- Updated set_non_nullable function to ensure required properties are processed correctly.
- Added logic to handle cases where 'required' is not present, maintaining existing functionality for non-nullable properties.
- Implemented GetContainer function to retrieve container details by ID.
- Added error handling for missing ID, container not found, and client creation failures.
- Enhanced Container struct to support omitempty for state field in JSON responses.
- Updated API documentation with Swagger annotations for the new endpoint.
- Implemented Restart, Start, and Stop endpoints for managing Docker containers.
- Each endpoint includes request validation, error handling, and appropriate responses.
- Enhanced API documentation with Swagger annotations for all new routes.
- Implemented AllSystemInfo function to retrieve and stream system information from agents via WebSocket.
- Introduced AllSystemInfoRequest struct for query parameter binding and validation.
- Enhanced error handling for invalid requests and WebSocket upgrades.
- Utilized goroutines for concurrent data fetching from multiple agents, with retry logic for robustness.
- Introduced ReverseProxy method to handle requests to the agent with context, method, and body.
- Updated Forward method to return *http.Response instead of byte data.
- Enhanced SystemInfo function to support querying by agent name in addition to agent address.
- Updated JSON marshaling in SystemInfo to use quoted keys.
- Refactored aggregation logic to dynamically append entries.
- Adjusted test cases to reflect changes in data structure and ensure accurate serialization.
- Introduced addWithTime method for adding entries with specific timestamps.
- Added validateInterval and fixInterval methods to ensure correct interval settings.
- Updated JSON unmarshalling to respect entry timestamps and validate intervals post-load.
- Refactored poller to use a constant PollInterval for consistency across the codebase.
- Introduced benchmark tests for Entrypoint and ReverseProxy to evaluate performance.
- Updated Entrypoint's ServeHTTP method to improve route context management.
- Added new test file for entrypoint benchmarks and refined existing tests for route handling.
- Added SearchRoute method to Config for searching routes by alias.
- Updated Route function to check for excluded routes if the initial lookup fails, returning the found route or a 404 status accordingly.
- Added ItemClick endpoint to increment item click counts.
- Refactored Categories function to dynamically generate categories based on available items.
- Introduced sorting methods for homepage items and categories.
- Updated item configuration to include visibility, favorite status, and sort orders.
- Improved handling of item URLs and added support for websocket connections in item retrieval.
- Introduced ContainerRuntime field in AgentConfig and AgentEnvConfig.
- Added IterAgents and NumAgents functions for agent pool management.
- Updated agent creation and verification endpoints to handle container runtime.
- Enhanced Docker Compose template to support different container runtimes.
- Added runtime endpoint to retrieve agent runtime information.
- Simplified the wakeFromHTTP and wakeFromStream methods by removing unnecessary loops and integrating direct checks for container readiness.
- Introduced a waitForReady method to streamline the waiting process for container readiness notifications.
- Enhanced the checkUpdateState method to include timeout detection for container startup.
- Added health check retries and logging for better monitoring of container state transitions.
- Updated Go version from 1.24.5 to 1.25.0 across multiple modules.
- Incremented versions for go-acme/lego from v4.25.1 to v4.25.2 and yusing/go-proxy from v0.16.1 to v0.16.2.
- Updated indirect dependencies including cloud.google.com/go/auth, golang.org/x/net, and others to their latest versions.
- Cleaned up and organized go.mod and go.sum files.
- These changes makes the API incombatible with previous versions
- Added new types for error handling, success responses, and health checks.
- Updated health check logic to utilize the new types for better clarity and structure.
- Refactored existing handlers to improve response consistency and error handling.
- Updated Makefile to include a new target for generating API types from Swagger.
- Updated "new agent" API to respond an encrypted cert pair
- Upgraded go-acme/lego from v4.24.0 to v4.25.1.
- Updated quic-go from v0.53.0 to v0.54.0.
- Incremented versions for yusing/go-proxy and related dependencies.
- Updated aws/smithy-go from v1.22.4 to v1.22.5 and baidubce/bce-sdk-go from v0.9.235 to v0.9.236.
- Updated barcode library from v1.0.2 to v1.1.0.
- Updated google.golang.org/api from v0.242.0 to v0.243.0 and grpc from v1.73.0 to v1.74.2.
- Cleaned up unused dependencies and updated indirect dependencies.
- Added a new `downNotificationSent` flag to track if a service down notification has been sent.
- Reset the notification state when a service comes back up.
- Updated logic to ensure notifications are sent only once after reaching the configured retry threshold for consecutive failures.
- Introduced a new themed middleware that allows for dynamic theme application.
- Added support for multiple themes: dark, dark-grey, solarized-dark, and custom CSS.
- Included CSS files for each theme and a font CSS template for font customization.
- Updated middleware registry to include the new themed middleware.
- Added last failure tracking to the Provider struct to manage certificate renewal failures.
- Implemented methods to get, update, and clear the last failure timestamp.
- Introduced cooldown durations to prevent immediate retries after failures.
- Updated ObtainCert and ScheduleRenewal methods to utilize the new failure handling logic.
- Introduced NotifyFunc type for customizable notification handling in tests.
- Added Retries field to HealthCheckConfig for controlling notification thresholds.
- Implemented tests for notification behavior under various health check scenarios.
* refactor: simplify io code and make utils module independent
* fix(docker): agent and socket-proxy docker event flushing with modified reverse proxy handler
* refactor: remove unused code
* refactor: remove the use of logging module in most code
* refactor: streamline domain mismatch check in certState function
* tweak: use ecdsa p-256 for autocert
* fix(tests): update health check tests for invalid host and add case for port in host
* feat(acme): custom acme directory
* refactor: code refactor and improved context and error handling
* tweak: optimize memory usage under load
* fix(oidc): restore old user matching behavior
* docs: add ChatGPT assistant to README
---------
Co-authored-by: yusing <yusing@6uo.me>
- Updated Dockerfile and Makefile for socket-proxy build.
- Modified go.mod to include necessary dependencies.
- Updated CI workflows for socket-proxy integration.
- Better module isolation
- Code refactor
- Introduced Docker socket proxy handling in the agent.
- Added environment variables for Docker socket configuration.
- Implemented new Docker handler with endpoint permissions based on environment settings.
- Added tests for Docker handler functionality.
- Updated go.mod to include gorilla/mux for routing.
* chore(deps): update go-playground/validator to v10.26.0
* chore(deps): update Go version to 1.24.2 and dependencies, reorganize dependencies into categorized sections
* chore(deps): update Go version to 1.24.2 in Dockerfile
* refactor(agent): replace deprecated context import with standard context package
* feat(http3): add HTTP/3 support and refactor server handling code into utility functions
---------
Co-authored-by: yusing <yusing@6uo.me>
* cleanup code for URL type
* fix makefile for trace mode
* refactor, merge Entry, RawEntry and Route into one.
* Implement fileserver.
* refactor: rename HTTPRoute to ReverseProxyRoute to avoid confusion
* refactor: move metrics logger to middleware package
- fix prometheus metrics for load balanced routes
- route will now fail when health monitor fail to start
* fix extra output of ls-* commands by defer initializaing stuff, speed up start time
* add test for path traversal attack, small fix on FileServer.Start method
* rename rule.on.bypass to pass
* refactor and fixed map-to-map deserialization
* updated route loading logic
* schemas: add "add_prefix" option to modify_request middleware
* updated route JSONMarshalling
---------
Co-authored-by: yusing <yusing@6uo.me>
* implement OIDC middleware
* auth code cleanup
* allow override allowed_user in middleware, fix typos
* fix tests and callbackURL
* update next release docs
* fix OIDC middleware not working with Authentik
* feat: add groups support for OIDC claims (#41)
Allow users to specify allowed groups in the env and use it to inspect the claims.
This performs a logical AND of users and groups (additive).
* merge feat/oidc-middleware (#49)
* api: enrich provider statistifcs
* fix: docker monitor now uses container status
* Feat/auto schemas (#48)
* use auto generated schemas
* go version bump and dependencies upgrade
* clarify some error messages
---------
Co-authored-by: yusing <yusing@6uo.me>
* cleanup some loadbalancer code
* api: cleanup websocket code
* api: add /v1/health/ws for health bubbles on dashboard
* feat: experimental memory logger and logs api for WebUI
---------
Co-authored-by: yusing <yusing@6uo.me>
---------
Co-authored-by: yusing <yusing@6uo.me>
Co-authored-by: Peter Olds <peter@olds.co>
This allows the API to trigger an OAuth workflow to create the JWT for authentication. For now the workflow is triggered by manually visiting `/api/login/oidc` on the frontend app until the UI repo is updated to add support.
Co-authored-by: Peter Olds <peter@olds.co>
Optionally allow a user to specify a “warm-up” endpoint to start the container, returning a 403 if the endpoint isn’t hit and the container has been stopped.
This can help prevent bots from starting random containers, or allow health check systems to run some probes.
- save ACME private key to reuse previous registered ACME account
- properly renew certificate with `Certificate.RenewWithOptions` instead of re-obtaining with `Certificate.Obtain`
- fixed "API JWT secret empty" warning output format
- fixed metrics initialized when it should not
- fixed middlewares.modifyRequest Host header not working properly
- Incorrect name being shown on dashboard "Proxies page"
- Apps being shown when homepage.show is false
- Load balanced routes are shown on homepage instead of the load balancer
- Route with idlewatcher will now be removed on container destroy
- Idlewatcher panic
- Performance improvement
- Idlewatcher infinitely loading
- Reload stucked / not working properly
- Streams stuck on shutdown / reload
- etc...
Added:
- support idlewatcher for loadbalanced routes
- partial implementation for stream type idlewatcher
Issues:
- graceful shutdown
[](https://sonarcloud.io/summary/new_code?id=yusing_go-proxy)

[](https://sonarcloud.io/summary/new_code?id=go-proxy)
Have questions? Ask [ChatGPT](https://chatgpt.com/g/g-6825390374b481919ad482f2e48936a1-godoxy-assistant)! (Thanks to [@ismesid](https://github.com/arevindh))
</div>
## Table of content
<!-- TOC -->
- [go-proxy](#go-proxy)
- [Table of content](#table-of-content)
- [Key Points](#key-points)
- [Getting Started](#getting-started)
- [Setup](#setup)
- [Commands line arguments](#commands-line-arguments)
- [Environment variables](#environment-variables)
- [Use JSON Schema in VSCode](#use-json-schema-in-vscode)

[](https://sonarcloud.io/summary/new_code?id=yusing_go-proxy)
The `agent` package provides the client-side implementation for interacting with GoDoxy agents. It handles agent configuration, secure communication via TLS, and provides utilities for agent deployment and management.
| [`config.go`](config.go) | Core configuration, initialization, and API client logic. |
| [`new_agent.go`](new_agent.go) | Agent creation and certificate generation logic. |
| [`docker_compose.go`](docker_compose.go) | Generator for agent Docker Compose configurations. |
| [`bare_metal.go`](bare_metal.go) | Generator for bare metal installation scripts. |
| [`env.go`](env.go) | Environment configuration types and constants. |
| `common/` | Shared constants and utilities for agents. |
## Core Types
### [`AgentConfig`](config.go:29)
The primary struct used by the GoDoxy server to manage a connection to an agent. It stores the agent's address, metadata, and TLS configuration.
### [`AgentInfo`](config.go:45)
Contains basic metadata about the agent, including its version, name, and container runtime (Docker or Podman).
### [`PEMPair`](new_agent.go:53)
A utility struct for handling PEM-encoded certificate and key pairs, supporting encryption, decryption, and conversion to `tls.Certificate`.
## Agent Creation and Certificate Management
### Certificate Generation
The [`NewAgent`](new_agent.go:147) function creates a complete certificate infrastructure for an agent:
- **CA Certificate**: Self-signed root certificate with 1000-year validity.
- **Server Certificate**: For the agent's HTTPS server, signed by the CA.
- **Client Certificate**: For the GoDoxy server to authenticate with the agent.
All certificates use ECDSA with P-256 curve and SHA-256 signatures.
### Certificate Security
- Certificates are encrypted using AES-GCM with a provided encryption key.
- The [`PEMPair`](new_agent.go:53) struct provides methods for encryption, decryption, and conversion to `tls.Certificate`.
- Base64 encoding is used for certificate storage and transmission.
## Key Features
### 1. Secure Communication
All communication between the GoDoxy server and agents is secured using mutual TLS (mTLS). The [`AgentConfig`](config.go:29) handles the loading of CA and client certificates to establish secure connections.
### 2. Agent Discovery and Initialization
The [`Init`](config.go:231) and [`InitWithCerts`](config.go:110) methods allow the server to:
- Fetch agent metadata (version, name, runtime).
- Verify compatibility between server and agent versions.
- Test support for TCP and UDP stream tunneling.
### 3. Deployment Generators
The package provides interfaces and implementations for generating deployment artifacts:
- **Docker Compose**: Generates a `docker-compose.yml` for running the agent as a container via [`AgentComposeConfig.Generate()`](docker_compose.go:21).
- **Bare Metal**: Generates a shell script to install and run the agent as a systemd service via [`AgentEnvConfig.Generate()`](bare_metal.go:27).
### 4. Fake Docker Host
The package supports a "fake" Docker host scheme (`agent://<addr>`) to identify containers managed by an agent, allowing the GoDoxy server to route requests appropriately. See [`IsDockerHostAgent`](config.go:90) and [`GetAgentAddrFromDockerHost`](config.go:94).
## Usage Example
```go
cfg:=&agent.AgentConfig{}
cfg.Parse("192.168.1.100:8081")
ctx:=context.Background()
iferr:=cfg.Init(ctx);err!=nil{
log.Fatal(err)
}
fmt.Printf("Connected to agent: %s (Version: %s)\n",cfg.Name,cfg.Version)
This package implements a small header-based handshake that allows an authenticated client to request forwarding to a `(host, port)` destination. It supports both TCP-over-TLS and UDP-over-DTLS transports.
Checksum[4]byte// CRC32 checksum of header without checksum
}
```
**Methods:**
-`NewStreamRequestHeader(host, port string) (*StreamRequestHeader, error)` - Creates a header for the given host and port. Returns error if host exceeds 255 bytes or port exceeds 5 bytes.
-`NewStreamHealthCheckHeader() *StreamRequestHeader` - Creates a header with `FlagCloseImmediately` set for health check probes.
-`Validate() bool` - Validates the version and checksum.
-`GetHostPort() (string, string)` - Extracts the host and port from the header.
-`ShouldCloseImmediately() bool` - Returns true if `FlagCloseImmediately` is set.
### TCP Functions
- [`NewTCPClient()`](tcp_client.go:26) - Creates a TLS client connection and sends the stream header.
- [`NewTCPServerHandler()`](tcp_server.go:24) - Creates a handler for ALPN-multiplexed connections (no listener).
- [`NewTCPServerFromListener()`](tcp_server.go:36) - Wraps an existing TLS listener.
- [`NewTCPServer()`](tcp_server.go:45) - Creates a fully-configured TCP server with TLS listener.
### UDP Functions
- [`NewUDPClient()`](udp_client.go:27) - Creates a DTLS client connection and sends the stream header.
- [`NewUDPServer()`](udp_server.go:26) - Creates a DTLS server listening on the given UDP address.
## Health Check Probes
The protocol supports health check probes using the `FlagCloseImmediately` flag. When a client sends a header with this flag set, the server validates the header and immediately closes the connection without establishing a destination tunnel.
This is useful for:
- Connectivity testing between agent and server
- Verifying TLS/DTLS handshake and mTLS authentication
- Monitoring stream protocol availability
**Usage:**
```go
header:=stream.NewStreamHealthCheckHeader()
// Send header over TLS/DTLS connection
// Server will validate and close immediately
```
Both TCP and UDP servers silently handle health check probes without logging errors.
See [`NewStreamHealthCheckHeader()`](header.go:66) and [`FlagCloseImmediately`](header.go:28).
## TCP behavior
1. Client establishes a TLS connection to the stream server.
2. Client sends exactly one header as a handshake.
3. After the handshake, both sides proxy raw TCP bytes between client and destination.
Server reads the header using `io.ReadFull` to avoid dropping bytes.
See [`NewTCPClient()`](tcp_client.go:26) and [`(*TCPServer).redirect()`](tcp_server.go:116).
## UDP-over-DTLS behavior
1. Client establishes a DTLS connection to the stream server.
2. Client sends exactly one header as a handshake.
3. After the handshake, both sides proxy raw UDP datagrams:
- client -> destination: DTLS payload is written to destination `UDPConn`
- destination -> client: destination payload is written back to the DTLS connection
Responses do **not** include a header.
The UDP server uses a bidirectional forwarding model:
- One goroutine forwards from client to destination
- Another goroutine forwards from destination to client
The destination reader uses `readDeadline` to periodically wake up and check for context cancellation. Timeouts do not terminate the session.
See [`NewUDPClient()`](udp_client.go:27) and [`(*UDPServer).handleDTLSConnection()`](udp_server.go:89).
## Connection Management
Both `TCPServer` and `UDPServer` create a dedicated destination connection per incoming stream session and close it when the session ends (no destination connection reuse).
| `ErrInvalidHeader` | Header validation failed (version or checksum). |
| `ErrCloseImmediately` | Health check probe - server closed immediately. |
Errors from connection creation are propagated to the caller.
See [`header.go`](header.go:23).
## Integration
This package is used by the agent to provide stream tunneling capabilities. See the parent [`agent`](../README.md) package for integration details with the GoDoxy server.
### Certificate Requirements
Both TCP and UDP servers require:
- CA certificate for client verification
- Server certificate for TLS/DTLS termination
Both clients require:
- CA certificate for server verification
- Client certificate for mTLS authentication
### ALPN Protocol
The `StreamALPN` constant (`"godoxy-agent-stream/1"`) is used to multiplex stream tunnel traffic and HTTPS API traffic on the same port. Connections negotiating this ALPN are routed to the stream handler.
Package for configuring HTTP proxy connections through the GoDoxy Agent using HTTP headers.
## Overview
This package provides types and functions for parsing and setting agent proxy configuration via HTTP headers. It supports both a modern base64-encoded JSON format and a legacy header-based format for backward compatibility.
## Architecture
```mermaid
graph LR
A[HTTP Request] --> B[ConfigFromHeaders]
B --> C{Modern Format?}
C -->|Yes| D[Parse X-Proxy-Config Base64 JSON]
C -->|No| E[Parse Legacy Headers]
D --> F[Config]
E --> F
F --> G[SetAgentProxyConfigHeaders]
G --> H[Modern Headers]
G --> I[Legacy Headers]
```
## Public Types
### Config
```go
typeConfigstruct{
Schemestring// Proxy scheme (http or https)
Hoststring// Proxy host (hostname or hostname:port)
HTTPConfig// Extended HTTP configuration
}
```
The `HTTPConfig` embedded type (from `internal/route/types`) includes:
Certificate management package for creating and extracting certificate archives.
## Overview
This package provides utilities for packaging SSL certificates into ZIP archives and extracting them. It is used by the GoDoxy Agent to distribute certificates to clients in a convenient format.
Generates the file path for storing agent certificates.
**Parameters:**
-`host` - Agent hostname
**Returns:**
- Full file path within `certs/` directory
-`false` if host is invalid (contains path separators or special characters)
### isValidAgentHost
```go
funcisValidAgentHost(hoststring)bool
```
Validates that a host string is safe for use in file paths.
## Constants
```go
constAgentCertsBasePath="certs"
```
Base directory for storing certificate archives.
## File Format
The ZIP archive uses `zip.Store` compression (no compression) for fast creation and extraction. Each file is stored with its standard name (`ca.pem`, `cert.pem`, `key.pem`).
Environment configuration package for the GoDoxy Agent.
## Overview
This package manages environment variable parsing and provides a centralized location for all agent configuration options. It is automatically initialized on import.
| `/health` | GET | Health check with scheme query param |
| `/system-info` | GET | System metrics via SSE or WebSocket |
| `/proxy/http/{path...}` | GET/POST | HTTP proxy with config from headers |
| `/*` | \* | Docker socket proxy |
## Sub-packages
### proxy_http.go
Handles HTTP proxy requests by reading configuration from request headers and proxying to the configured upstream.
**Key Function:**
-`ProxyHTTP(w, r)` - Proxies HTTP requests based on `X-Proxy-*` headers
### check_health.go
Handles health check requests for various schemes.
**Key Function:**
-`CheckHealth(w, r)` - Performs health checks with configurable scheme
**Supported Schemes:**
-`http`, `https` - HTTP health check
-`h2c` - HTTP/2 cleartext health check
-`tcp`, `udp`, `tcp4`, `udp4`, `tcp6`, `udp6` - TCP/UDP health check
-`fileserver` - File existence check
## Usage Example
```go
packagemain
import(
"net/http"
"github.com/yusing/godoxy/agent/pkg/handler"
)
funcmain(){
mux:=http.NewServeMux()
mux.Handle("/",handler.NewAgentHandler())
http.ListenAndServe(":8890",mux)
}
```
## WebSocket Support
The handler includes a permissive WebSocket upgrader for internal use (no origin check). This enables real-time system metrics streaming via Server-Sent Events (SSE).
## Docker Socket Integration
All unmatched requests fall through to the Docker socket handler, allowing the agent to proxy Docker API calls when configured.
Main entry point package for GoDoxy, a lightweight reverse proxy with WebUI for Docker containers.
## Overview
This package contains the `main.go` entry point that initializes and starts the GoDoxy server. It coordinates the initialization of all core components including configuration loading, API server, authentication, and monitoring services.
## Architecture
```mermaid
graph TD
A[main] --> B[Init Profiling]
A --> C[Init Logger]
A --> D[Parallel Init]
D --> D1[DNS Providers]
D --> D2[Icon Cache]
D --> D3[System Info Poller]
D --> D4[Middleware Compose Files]
A --> E[JWT Secret Setup]
A --> F[Create Directories]
A --> G[Load Config]
A --> H[Start Proxy Servers]
A --> I[Init Auth]
A --> J[Start API Server]
A --> K[Debug Server]
A --> L[Uptime Poller]
A --> M[Watch Changes]
A --> N[Wait Exit]
```
## Main Function Flow
The `main()` function performs the following initialization steps:
1.**Profiling Setup**: Initializes pprof endpoints for performance monitoring
1.**Logger Initialization**: Configures zerolog with memory logging
1.**Parallel Initialization**: Starts DNS providers, icon cache, system info poller, and middleware
1.**JWT Secret**: Ensures API JWT secret is set (generates random if not provided)
1.**Directory Preparation**: Creates required directories for logs, certificates, etc.
1.**Configuration Loading**: Loads YAML configuration and reports any errors
1.**Proxy Servers**: Starts HTTP/HTTPS proxy servers based on configuration
1.**Authentication**: Initializes authentication system with access control
1.**API Server**: Starts the REST API server with all configured routes
1.**Debug Server**: Starts the debug page server (development mode)
1.**Monitoring**: Starts uptime and system info polling
1.**Change Watcher**: Starts watching for Docker container and configuration changes
1.**Graceful Shutdown**: Waits for exit signal with configured timeout
## Configuration
The main configuration is loaded from `config/config.yml`. Required directories include:
-`logs/` - Log files
-`config/` - Configuration directory
-`certs/` - SSL certificates
-`proxy/` - Proxy-related files
## Environment Variables
-`API_JWT_SECRET` - Secret key for JWT authentication (optional, auto-generated if not set)
## Dependencies
-`internal/api` - REST API handlers
-`internal/auth` - Authentication and ACL
-`internal/config` - Configuration management
-`internal/dnsproviders` - DNS provider integration
- [Supported DNS Providers](#supported-dns-providers)
- [Cloudflare](#cloudflare)
- [CloudDNS](#clouddns)
- [DuckDNS](#duckdns)
- [OVHCloud](#ovhcloud)
- [Implement other DNS providers](#implement-other-dns-providers)
## Cloudflare
`auth_token` your zone API token
Follow [this guide](https://cloudkul.com/blog/automcatic-renew-and-generate-ssl-on-your-website-using-lego-client/) to create a new token with `Zone.DNS` read and edit permissions
## CloudDNS
-`client_id`
-`email`
-`password`
## DuckDNS
-`token`: DuckDNS Token
Tested by [earvingad](https://github.com/earvingad)
## OVHCloud
_Note, `application_key` and `oauth2_config`**CANNOT** be used together_
-`api_endpoint`: Endpoint URL, or one of
-`ovh-eu`,
-`ovh-ca`,
-`ovh-us`,
-`kimsufi-eu`,
-`kimsufi-ca`,
-`soyoustart-eu`,
-`soyoustart-ca`
-`application_secret`
-`application_key`
-`consumer_key`
-`oauth2_config`: Client ID and Client Secret
-`client_id`
-`client_secret`
## Implement other DNS providers
See [add_dns_provider.md](docs/add_dns_provider.md)
| `proxy.aliases` | comma separated aliases for subdomain and label matching | `gitlab,gitlab-reg,gitlab-ssh` | `container_name` | any |
| `proxy.exclude` | to be excluded from `go-proxy` | | false | boolean |
| `proxy.idle_timeout` | time for idle (no traffic) before put it into sleep **(http/s only)**<br> _**NOTE: idlewatcher will only be enabled containers that has non-empty `idle_timeout`**_ | `1h` | empty or `0` **(disabled)** | `number[unit]...`, e.g. `1m30s` |
| `proxy.wake_timeout` | time to wait for target site to be ready | | `10s` | `number[unit]...` |
| `proxy.stop_method` | method to stop after `idle_timeout` | | `stop` | `stop`, `pause`, `kill` |
| `proxy.stop_timeout` | time to wait for stop command | | `10s` | `number[unit]...` |
| `proxy.stop_signal` | signal sent to container for `stop` and `kill` methods | | docker's default | `SIGINT`, `SIGTERM`, `SIGHUP`, `SIGQUIT` and those without **SIG** prefix |
| `proxy.<alias>.<field>` | set field for specific alias | `proxy.gitlab-ssh.scheme` | N/A | N/A |
| `proxy.$<index>.<field>` | set field for specific alias at index (starting from **1**) | `proxy.$3.port` | N/A | N/A |
| `proxy.*.<field>` | set field for all aliases | `proxy.*.set_headers` | N/A | N/A |
| `scheme` | proxy protocol | <ul><li>`http` for numeric port</li><li>`tcp` for `x:y` port</li></ul> | `http`, `https`, `tcp`, `udp` |
| `host` | proxy host | <ul><li>Docker: docker client IP / hostname </li><li>File: `localhost`</li></ul> | IP address, hostname |
| `port` | proxy port **(http/s)** | first port returned from docker | number in range of `1 - 65535` |
| `port` **(required)** | proxy port **(tcp/udp)** | N/A | `x:y` <br><ul><li>**x**: port for `go-proxy` to listen on.<br>**x** can be 0, which means listen on a random port</li><li>**y**: port or [_service name_](../src/common/constants.go#L55) of target container</li></ul> |
| `path_patterns` | proxy path patterns **(http/s only)**<br> only requests that matched a pattern will be proxied | empty **(proxy all requests)** | yaml style list[<sup>1</sup>](#list-example) of ([path patterns](https://pkg.go.dev/net/http#hdr-Patterns-ServeMux)) |
| `set_headers` | header to set **(http/s only)** | empty | yaml style key-value mapping[<sup>2</sup>](#key-value-mapping-example) of header-value pairs |
| `hide_headers` | header to hide **(http/s only)** | empty | yaml style list[<sup>1</sup>](#list-example) of headers |
[🔼Back to top](#table-of-content)
#### Key-value mapping example
Docker Compose
```yaml
services:
nginx:
...
labels:
# values from duplicated header keys will be combined
proxy.nginx.set_headers: | # remember to add the '|'
X-Custom-Header1: value1, value2
X-Custom-Header2: value3
X-Custom-Header2: value4
# X-Custom-Header2 will be "value3, value4"
```
File Provider
```yaml
service_a:
host: service_a.internal
set_headers:
# do not duplicate header keys, as it is not allowed in YAML
X-Custom-Header1: value1, value2
X-Custom-Header2: value3
```
[🔼Back to top](#table-of-content)
#### List example
Docker Compose
```yaml
services:
nginx:
...
labels:
proxy.nginx.path_patterns: | # remember to add the '|'
- GET /
- POST /auth
proxy.nginx.hide_headers: | # remember to add the '|'
- X-Custom-Header1
- X-Custom-Header2
```
File Provider
```yaml
service_a:
host: service_a.internal
path_patterns:
- GET /
- POST /auth
hide_headers:
- X-Custom-Header1
- X-Custom-Header2
```
[🔼Back to top](#table-of-content)
## Troubleshooting
- Container not showing up in proxies list
Please check that either `ports` or label `proxy.<alias>.port` is declared, e.g.
```yaml
services:
nginx-1: # Option 1
...
ports:
- 80
nginx-2: # Option 2
...
container_name: nginx-2
network_mode: host
labels:
proxy.nginx-2.port: 80
```
- Firewall issues
If you are using `ufw` with vpn that drop all inbound traffic except vpn, run below:
`sudo ufw allow from 172.16.0.0/16 to 100.64.0.0/10`
Explaination:
Docker network is usually `172.16.0.0/16`
Tailscale is used as an example, `100.64.0.0/10` will be the CIDR
You can also list CIDRs of all docker bridge networks by:
Some files were not shown because too many files have changed in this diff
Show More
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.