8000 roji/CHANGELOG.md at main Β· kan/roji Β· GitHub
[go: up one dir, main page]

Skip to content

Latest commit

Β 

History

History
990 lines (776 loc) Β· 44.6 KB

CHANGELOG.md

File metadata and controls

990 lines (776 loc) Β· 44.6 KB

Changelog

All notable changes to this project will be documented in this file.

The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.

[Unreleased]

1.5.2 - 2026-11-03

Internal

  • Releases are prepared by tagpr: a release pull request carries the CHANGELOG section and the refreshed CREDITS, and merging it tags the release. The GitHub release notes are now this file's section for the version instead of a list of commits.
  • Workflows default to a read-only GITHUB_TOKEN; each job lists the permissions it needs.
  • The documentation site builds with Hugo 0.168.0 and the thulite 3 theme (#103), and pull requests that touch it now build it.
  • Dependency updates: github/codeql-action/upload-sarif 4.39.1 and docker/login-action 4.7.0.

1.5.1 - 2026-10-23

Security release. No changes to roji's behavior.

Security

  • Built with Go 1.27.3, which adds a denylist to crypto/x509 so that certificate verification rejects known compromised intermediate certificates (CVE-2026-111943). It covers systems whose trust store has not been updated to distrust them. v1.5.0 was built with Go 1.27.2.

Internal

  • Dependency updates: golang.org/x/text 0.43.0, goreleaser/goreleaser-action 7.3.0, and github/codeql-action/upload-sarif 4.39.0.
  • The documentation site build pins Hugo to 0.167.0 instead of latest. Hugo 0.168.0 removed a config key the site uses, which failed the deploy of an unchanged site. Moving to the replacement is tracked in #103.

1.5.0 - 2026-10-12

roji run tells the command the hostname it was given. v1.4.0 passed only the port, which left a server that needs its own origin with a hostname to be typed by hand.

Added

  • roji run passes the hostname and the URL of the route to the command, the way it passes the port: {host} and {url} in the arguments are replaced, and ROJI_HOST and ROJI_URL are set. A server that has to know its own origin no longer needs the hostname written into the command, where it would only be right for one machine's domain.

    roji run api -- npx wrangler dev --port {port} --host {host} --upstream-protocol https

Internal

  • The release build pins GoReleaser to an exact version instead of ~> v2. The .tar.gz and .zip bytes depend on the GoReleaser that packs them, so rebuilding a tag reproduced the binaries and not the archive checksums once GoReleaser had moved on.

1.4.0 - 2026-10-12

Dev servers on the host. roji has routed containers and static directories; a vite dev or wrangler dev started outside Docker had no way in. It gets a hostname now, either from the config file or by being started through roji run, which also picks its port.

Added

  • proxy_sites in the config file gives a hostname to a server running on the host itself, such as vite dev or wrangler dev, the way a container gets one. WebSocket, SSE, gRPC and the forwarding headers behave as they do for containers. When nothing is listening on the target, the request gets a page naming the address and what to start. See Dev Servers on the Host.

    proxy_sites:
      - host: app
        target: http://localhost:5173
  • roji run <name> -- <command> starts a dev server on a free port and routes https://<name>.<domain> to it for as long as the command runs. The port is passed as PORT and replaces {port} in the arguments, so projects that share a default port no longer collide and none needs a port in its config.

    roji run app -- npx vite --port {port}
  • roji config reload applies changes to static_sites and proxy_sites to the running roji, which until now took the dashboard's button or a call to the API. It lists every declared site and says why any was left out, so a site that did not make it is not mistaken for one that did. The static sites guide has documented this command since v1.0.0; it now exists.

    /_api/config/reload returns the same list under sites, along with the file it read and the base domain it judged the sites against.

  • Sites declared in the config file can be published through the tunnel with tunnel: true, under both proxy_sites and static_sites. Static sites had no way to opt in before and were always private. Through the tunnel a static site does not serve or list dotfiles (.env, .git/), .well-known/ excepted.

  • roji doctor checks the declared sites: a static site whose root is not a directory, a proxy site nothing is listening behind, a hostname roji's certificate does not cover, a hostname that is the dashboard's, a hostname declared twice, and a site that asks for the tunnel but has no public name.

Changed

  • Breaking: routes in /_api/routes and the /_api/events stream carry a kind field (docker, static or local) in place of the isStatic boolean.

  • Pages a static site generates, such as the directory listing, no longer link to the dashboard when the request came through the tunnel.

  • A static site declared on the dashboard's hostname or the base domain is no longer registered. The dashboard answers on those names first, so such a site was listed and never reachable.

  • Connecting to a backend gives up after 10 seconds instead of waiting out the operating system's connect timeout.

Fixed

  • --help and errors about a command's arguments were printed in English whatever the language. They are translated like the rest of the CLI.

  • A static site protected with Basic authentication served its files to an OPTIONS request without credentials. The check lets OPTIONS through for CORS preflight, and the static file server answered it like a GET. Static sites now answer only GET and HEAD.

  • A published container with a warning (no exposed port, a hostname conflict) answered tunnel requests with the warning page, which names the service and the dashboard. It now gets the same bare 404 as every other refusal.

  • A config reload cleared the declared sites before adding them back. A request arriving in between for a static site's hostname was answered by the Docker route the site was hiding, if there was one. The set is now replaced in one step.

  • The tunnel guard and the proxy each resolve a request's route. When the route changed in between β€” a config reload, a container stopping β€” a request the guard had approved could be answered by a route that never opted in. The proxy now checks the route it is about to serve.

Security

  • Built with Go 1.27.2, which fixes three denial-of-service vulnerabilities in the standard library that roji's listeners are exposed to: crafted HTTP Range headers (CVE-2026-78667), excessive HTTP/2 SETTINGS frames (CVE-2026-78669), and repeated ECH extension references in a TLS handshake (CVE-2026-97031). v1.3.0 was built with Go 1.27.1.

Internal

  • Dependency updates: github.com/moby/moby/api 1.56.1, github.com/moby/moby/client 0.6.2, golangci-lint v2.14.0, and golang.org/x/net 0.60.0. The last is not linked into the roji binary β€” the development tools in go.mod pull it in β€” and was raised for the vulnerabilities reported against 0.59.0.

1.3.0 - 2026-09-30

Distribution release. The installer runs the binary it downloads with sudo and puts a CA into the system trust store, yet it never checked what it had downloaded. It does now, and the releases carry what it checks against. The binary also ships the licenses of everything linked into it.

Added

  • The installer verifies the downloaded archive before installing anything. It compares the archive with the release's checksums.txt, and when the GitHub CLI is installed and logged in, it also checks the build provenance with gh attestation verify. Either failure stops the installation. Releases from this one on carry that attestation; older ones are checked against the checksums only. See Installation.

  • --version X.Y.Z (or ROJI_VERSION=X.Y.Z on the bash side of the pipe) installs a given release instead of the latest one.

  • roji credits prints roji's license, the licenses of the Go modules linked into the binary, and the notices for other bundled software such as Petite Vue. The same texts ship as CREDITS and THIRD_PARTY_NOTICES in the release archives and the Docker image. The one-liner installer keeps only the binary, so the command is how to read them after installing that way.

Changed

  • The install one-liner points at main instead of a release tag. The tag in the URL never decided which version was installed β€” the installer always fetched the latest release β€” so --version is now the way to pin one. The documented command also passes --proto '=https' --tlsv1.2 to curl.

Fixed

  • When the GitHub API rate limit was hit, the installer reported roji as already up to date and exited without upgrading. It now reads the latest version from the releases/latest redirect, which has no such limit, and stops with an error if it cannot determine it.

  • The installer left its temporary directory behind when extracting the archive failed.

Internal

  • GoReleaser creates the release as a draft, and the workflow publishes it only after the archives are attested. Until then releases/latest keeps pointing at the previous version, so the installer never picks up a release it cannot verify.
  • The installer runs nothing but its final main "$@" line at the top level, so a download cut off midway does not execute a partial script.
  • HTTP/2 for the HTTPS listener and for gRPC backends is configured through net/http's Protocols instead of the deprecated golang.org/x/net/http2 APIs.
  • Dependency updates: github.com/moby/moby/client 0.6.0, golang.org/x/net 0.59.0, Go 1.27.1.

1.2.0 - 2026-08-14

Feature release. v1.1.0 confined roji to loopback, which left nothing for the times something outside has to reach the machine. A Cloudflare Tunnel fills that in, one container at a time.

Added

  • Publish selected routes to the internet through a Cloudflare Tunnel, with a tunnel: block in the configuration file and a roji.tunnel label on each container that opts in. roji picks up every container on its network without being asked, so publishing is never the default.

    The tunnel gets a listener of its own on 127.0.0.1:{tunnel.port} (8080 unless set). cloudflared connects from 127.0.0.1 like any local browser, so nothing in a request distinguishes the two; the port it arrived on does, and that is what makes the guard possible. Requests reaching it are refused unless they name a route that opted in. The dashboard, the base domain, /_api/* and /_assets/* are refused outright β€” /_api/projects/{name}/up starts containers without credentials β€” and static sites have no opt-in spelling, so none are published. Every refusal answers with the same bare 404, since wording them apart would say which hostnames exist.

    A route published as web.dev.localhost answers to web.example.com. Only the suffix changes: the routing key stays local, and so does the Host the backend receives, which is what a dev server's allowed-hosts list expects.

    With auto_start: true roji runs cloudflared as a child process and stops it on shutdown. It is not restarted after a crash β€” roji is not a process manager, and a tunnel that keeps failing to authenticate should not retry in silence. Pointing *.{domain} at the tunnel stays manual, since roji holds no Cloudflare credentials. See Public Access.

  • roji doctor checks that cloudflared is installed, logged in, and that the named tunnel exists, and prints the DNS command it cannot run for you.

  • The dashboard marks published routes with a 🌐 badge and their public URL.

1.1.1 - 2026-08-07

Bug fix release. Three routing defects that share a shape: a route was stored but could not be reached, and nothing said so.

Fixed

  • Two backends claiming the same hostname and the same path prefix both joined the routing table without a warning. One answered by sort order and the other was unreachable while still looking healthy on the dashboard. The collision check asked whether a backend had a prefix rather than whether its routing key β€” hostname and prefix together β€” was already taken. The same pair without a prefix has always been reported.

  • Router.AddBackend did not update a route carrying a path prefix. Prefixed routes were appended rather than replaced, so re-adding a container left a second, stale entry behind (three adds gave three entries). Prefix-less routes replaced correctly.

  • roji config reload silently dropped a static site whose hostname a container had since taken, and it stayed gone until roji restarted, though nothing about the configuration had changed. Reload clears the static sites and registers them again, and registration refused a hostname a Docker route held.

    A static site declared in the config file now keeps its hostname, which is what request handling and startup ordering already did β€” only registration disagreed. A Docker route hidden that way is reported in the log. See Static Sites.

1.1.0 - 2026-08-07

Security and correctness release. roji now listens on loopback only, which is a breaking change for anyone reaching it from another device. Alongside that, six routing and WebSocket defects found by review, and the linting that would have caught several of them.

Changed

  • Breaking: roji listens on 127.0.0.1 and ::1 instead of every interface. Until now anyone on the same network segment β€” a shared office LAN, a cafe network, a guest VLAN β€” could reach ports 80 and 443 on the developer's machine, and through them every container on the roji network plus the dashboard. The Docker Compose endpoints (/_api/projects/{name}/up|down|restart) have no authentication, so that was container control rather than information disclosure.

    To go back to the previous behavior, or to reach roji from a phone on the same network, set bind to the addresses you want or to an empty string for all of them. See Configuration.

  • WebSocket upgrades go through httputil.ReverseProxy rather than a hand-rolled dialer. Two consequences beyond the fixes below: permessage-deflate is negotiated end to end, where roji previously stripped the offer and left every connection uncompressed; and a backend that refuses the upgrade now has its own response β€” status, headers and body β€” reach the client instead of a plain-text error.

Added

  • bind setting, as a comma-separated list, with ROJI_BIND and --bind. A loopback address that cannot be bound is skipped with a warning, so a machine with IPv6 disabled still starts on 127.0.0.1; any other configured address is required, since nothing substitutes for it.
  • roji doctor reports which addresses roji listens on.

Fixed

  • Path prefix routes matched without a segment boundary, so a /api route also captured /apifoo and /api-docs. The request reached the backend with a relative path and was rejected with 400; a prefix producing a valid-looking remainder would have been misrouted silently.
  • Stripping a path prefix rewrote only the decoded path, so /api/files/a%2Fb reached the backend as /files/a/b β€” one path segment turned into two. Only routes carrying a roji.path label were affected.
  • WebSocket upgrades reached the backend with the backend's own address in Host, while ordinary requests on the same route carried the hostname the client used. Vite and webpack check the upgrade request's Host against their allowed-hosts configuration, so HMR could be refused on a page that loaded fine.
  • WebSocket upgrades dropped the client's headers, Cookie and Authorization among them. A WebSocket behind session or token authentication was refused the upgrade while the page itself loaded.
  • roji.path=/ produced a path prefix of / rather than no prefix at all, which left a prefix-less route on the same hostname unreachable and unannounced by the hostname collision check.
  • roji.path was normalized with filepath.Clean, which rewrites slashes to the OS separator. On Windows /api became \api and matched nothing, so path-based routing failed entirely.
  • roji log did not notice a log rotation. The server rotates by renaming, so the handle a follower holds keeps pointing at the renamed file; roji watched its size, which never shrinks. The follower silently tailed a detached file and printed nothing more.
  • roji log -n 0 printed no history in follow mode, though the flag documents 0 as "show every line", and roji log -n with a negative count panicked.
  • Directory listing links were HTML-escaped but not URL-escaped, so a file whose name contained ? or # could not be opened from the listing.

Internal

  • go fix modernizers applied, gofmt run over the files that had drifted, and golangci-lint added to CI along with a gofmt check. The linter runs once per GOOS, because the service and certgen packages are split by build tag and a single Linux run never compiles the macOS and Windows files.

1.0.8 - 2026-08-01

Bug fix release. The roji routes and roji health commands ignored the resolved configuration when calling the local API, so both failed on any setup that did not use the built-in defaults.

Fixed

  • roji routes and roji health now honor the configured domain, https_port and dashboard values from the config file and ROJI_* environment variables. Neither command called config.Load(), and instead read globals that are only populated when the root command parses its own flags:
    • roji routes requested https://roji.:0/_api/routes and always failed to connect
    • roji health requested a hardcoded https://localhost/_api/health, which returns 404 whenever domain is not localhost, because the API is only served on the dashboard virtual host

Changed

  • Extract the local API client into a new apiclient package, shared by the CLI commands and the roji doctor port check. Requests connect to localhost and carry the dashboard hostname in the Host header, so no *.localhost DNS resolution is required.

1.0.7 - 2026-07-13

Dependency and security maintenance release. Clears all open security alerts (2 code scanning) and the failing govulncheck / Trivy checks, all rooted in the Go 1.26.4 standard library.

Security

  • Upgrade Go to 1.26.5, fixing 2 standard library vulnerabilities:
    • CVE-2026-39822 (HIGH): Symlink-following in os.Root allows directory traversal
    • CVE-2026-42505 (MEDIUM): Information disclosure in crypto/tls Encrypted Client Hello
  • Upgrade the golang builder image 1.26.4-alpine β†’ 1.26.5-alpine (#57), so the released binary is built with the patched standard library

Dependencies

  • Upgrade golang.org/x/net 0.56.0 β†’ 0.57.0 (#58), which also pulls golang.org/x/text 0.38.0 β†’ 0.40.0

1.0.6 - 2026-06-25

Dependency and security maintenance release. Resolves all 14 open security alerts (Dependabot + code scanning). The fixed issues are in build/website tooling (Hugo, vite, esbuild, Babel) rather than the roji proxy runtime.

Security

  • Upgrade github.com/gohugoio/hugo 0.161.0 β†’ 0.163.3, fixing 5 advisories:
  • Upgrade vite and esbuild (website), fixing:
    • GHSA-jqfw-vq24-v9c3 (HIGH): vite server.fs.deny bypass on Windows alternate paths
    • launch-editor: NTLMv2 hash disclosure via UNC path handling on Windows
    • esbuild arbitrary file read when running the dev server on Windows
  • Upgrade @babel/core 7.29.0 β†’ 7.29.7 (website), fixing CVE-2026-49356: arbitrary file read via sourceMappingURL comment
  • Remove unused @changesets/cli / @changesets/changelog-github dev dependencies (website). They were inherited from the Doks theme starter and are not used (this project uses GoReleaser + a hand-maintained CHANGELOG); removing them drops the only paths to the vulnerable js-yaml (GHSA-h67p-54hq-rp68: quadratic-complexity DoS in merge key handling)

Dependencies

  • golang.org/x/net 0.55.0 β†’ 0.56.0
  • golang.org/x/text 0.37.0 β†’ 0.38.0
  • github.com/moby/moby/api 1.54.2 β†’ 1.55.0
  • github.com/moby/moby/client 0.4.1 β†’ 0.5.0

CI

  • actions/checkout v6 β†’ v7

1.0.5 - 2026-06-08

Security

  • Upgrade Go to 1.26.4, fixing 2 standard library vulnerabilities:

CI

  • codecov/codecov-action v6 β†’ v7

1.0.4 - 2026-05-26

Security

  • Upgrade golang.org/x/net 0.54.0 β†’ 0.55.0, fixing GO-2026-5026: Punycode label decoding bypass in the idna package. ToASCII/ToUnicode incorrectly accept Punycode-encoded labels that decode to ASCII-only labels, enabling a hostname privilege-escalation bypass. Reachable in roji because the httputil.ReverseProxy in proxy/handler.go validates upstream hosts through golang.org/x/net/http2 β†’ httpguts β†’ idna.ToASCII.

Dependencies

  • brace-expansion 5.0.5 β†’ 5.0.6 (website)

1.0.3 - 2026-05-18

Security

  • Upgrade Go to 1.26.3, fixing 5 standard library vulnerabilities:
    • GO-2026-4982: Bypass of meta content URL escaping causes XSS in html/template
    • GO-2026-4980: Escaper bypass leads to XSS in html/template
    • GO-2026-4976: Issue in net/http/httputil
    • GO-2026-4971: Panic in Dial and LookupPort when handling NUL byte on Windows in net
    • GO-2026-4918: Issue in net/http/internal/http2 via golang.org/x/net
  • Upgrade go.opentelemetry.io/otel 1.40.0 β†’ 1.41.0, fixing CVE-2026-29181 (HIGH): multi-value baggage header extraction causes excessive allocations (remote DoS amplification)

Dependencies

  • golang.org/x/net 0.53.0 β†’ 0.54.0
  • github.com/moby/moby/client 0.4.0 β†’ 0.4.1
  • github.com/gohugoio/hugo 0.159.2 β†’ 0.161.0 (website)
  • @babel/plugin-transform-modules-systemjs 7.29.0 β†’ 7.29.4 (website)
  • postcss 8.5.8 β†’ 8.5.10 (website)

CI

  • actions/upload-pages-artifact v4 β†’ v5

1.0.2 - 2026-04-13

Security

  • Migrate Docker client from github.com/docker/docker to github.com/moby/moby/client v0.4.0, fixing CVE-2026-34040 (AuthZ bypass in Docker daemon)
  • Upgrade Go to 1.26.2, fixing 5 standard library vulnerabilities:

Dependencies

  • golang.org/x/net 0.52.0 β†’ 0.53.0
  • golang.org/x/text 0.35.0 β†’ 0.36.0
  • github.com/fatih/color 1.18.0 β†’ 1.19.0
  • github.com/gohugoio/hugo 0.149.1 β†’ 0.159.2 (website)

CI

  • actions/configure-pages v5 β†’ v6
  • codecov/codecov-action v5 β†’ v6
  • actions/deploy-pages v4 β†’ v5
  • docker/login-action v3 β†’ v4
  • docker/metadata-action v5 β†’ v6

1.0.1 - 2026-03-16

Security

  • Upgrade Go to 1.26.1, fixing 3 standard library vulnerabilities:
    • GO-2026-4601: Incorrect parsing of IPv6 host literals in net/url
    • GO-2026-4602: FileInfo can escape from a Root in os
    • GO-2026-4603: URLs in meta content attribute actions are not escaped in html/template

Dependencies

  • golang.org/x/net 0.51.0 β†’ 0.52.0
  • golang.org/x/text 0.34.0 β†’ 0.35.0
  • golang.org/x/sys 0.41.0 β†’ 0.42.0

CI

  • docker/build-push-action v6 β†’ v7
  • docker/setup-buildx-action v3 β†’ v4
  • actions/checkout, actions/setup-node, actions/upload-pages-artifact updated to latest versions

1.0.0 - 2026-02-17

Breaking Changes

  • Docker Mode removed: install-docker.sh has been deleted. roji now only supports Native Mode (standalone binary). Existing Docker Mode users should migrate manually by stopping the container, removing the installation, and re-running the native installer.
  • ROJI_DOMAIN default unified: docker-compose.yml now defaults to dev.localhost (was localhost), matching Native Mode behavior.

Added

  • Homebrew tap support: Install on macOS with brew install kan/roji/roji
    • GoReleaser auto-generates and pushes Formula to kan/homebrew-roji on release
  • Recent Projects delete: Remove projects from history via dashboard or API
    • DELETE /_api/projects/{name}/delete endpoint
    • Remove button on inactive projects in dashboard
    • Confirmation dialog before deletion
  • Internationalization (i18n): Japanese language support
    • CLI commands: All user-facing messages translated via i18n.T() / i18n.Tf()
    • Dashboard: Client-side translation with Accept-Language header detection
    • Not Found / Warning pages: Server-side Go template translation
    • Static file directory listing: Translated UI elements
    • install.sh: Language detection via LANG / LC_ALL / LC_MESSAGES
    • Doctor checks: All check names, messages, and details translated
    • Lightweight i18n package (i18n/) with JSON message files embedded via embed.FS
    • Language auto-detection: CLI uses LANG env var, HTTP uses Accept-Language header
    • Fallback chain: current language β†’ English β†’ key itself
  • README.md overhaul: Complete rewrite for v1.0.0
    • New Quick Start section for fastest path to first service
    • New Getting Started tutorial (multi-service project walkthrough)
    • New API Reference with all 16 endpoints
    • Expanded Troubleshooting with platform-specific guides (WSL, macOS, Linux)
    • Expanded CLI Reference with all commands and flags
    • Consolidated Configuration section with full config file example
    • Added missing ROJI_HTTP_PORT / ROJI_HTTPS_PORT environment variables to docs

Changed

  • install.sh now shows an error and migration instructions when Docker Mode is detected, instead of offering automatic migration
  • docker-compose.yml header updated to clarify it is for development and testing only
  • Documented roji.self as a reserved internal label

Removed

  • install-docker.sh (Docker Mode installer)
  • Docker Mode (Legacy) section from README.md
  • TLS Certificates manual installation section (replaced by roji ca install)
  • --migrate flag from install.sh

0.9.1 - 2026-02-06

Security

  • Go Update: Update to Go 1.25.7 to address crypto/tls vulnerability (CVE fix)

0.9.0 - 2026-02-06

Added

  • Docker Compose Operations: Control projects from dashboard and API
    • POST /_api/projects/{name}/up - Start project (docker compose up -d)
    • POST /_api/projects/{name}/down - Stop project (docker compose down)
    • POST /_api/projects/{name}/restart - Restart all services
    • GET /_api/projects/{name}/logs - Stream logs via F300 SSE
    • Dashboard buttons: Start/Stop/Restart on active and inactive projects
    • Stop button on individual routes in the routes list
    • Confirmation dialog before destructive operations
    • CORS support for cross-subdomain API calls
  • Not Found Page Project Detection: Auto-start inactive projects
    • Detects hostname matching an inactive project
    • Shows "Start" button to bring up the project directly
    • Supports both service.domain and service-project.domain hostname patterns
  • Basic Authentication: Protect routes with username/password
    • Docker labels: roji.auth.basic.user, roji.auth.basic.pass, roji.auth.basic.realm
    • Config file support for static sites
    • CORS preflight (OPTIONS) requests bypass authentication
    • Dashboard shows auth badge (πŸ”’) for protected routes
    • Config validation warns on unknown auth keys
  • Static File Hosting: Serve static files without Docker
    • Configure via static_sites in config.yaml
    • Directory listing with dark mode support
    • index.html auto-serving
    • Directory traversal protection
    • Basic authentication support
    • Dashboard "Reload Config" button for hot-reload
  • Service Management: Run roji as system service
    • roji service install/uninstall/start/stop/restart/status
    • Linux: systemd service (/etc/systemd/system/roji.service)
    • macOS: launchd agent (~/Library/LaunchAgents/com.roji.agent.plist)
    • Windows: NSSM-based Windows Service
  • Log File Output: Service mode logs to file
    • Linux/WSL: ~/.local/share/roji/roji.log
    • macOS: ~/Library/Logs/roji.log
    • Auto-rotation at 10MB
  • Config File Validation: Comprehensive validation on load and in doctor
    • Unknown keys warning (typo detection)
    • Type validation for all fields
    • Missing required fields check
    • roji doctor shows config validation results
  • New Native Mode Installer: install.sh now installs native binary
    • Downloads pre-built binary from GitHub Releases
    • Interactive install location selection (~/.local/bin or /usr/local/bin)
    • Auto-detects and migrates from Docker Mode
    • Runs doctor --fix, ca install, and service install automatically
    • Legacy Docker installer available as install-docker.sh

Changed

  • Dashboard now displays authentication status for each route
  • Dashboard active projects now have Start/Stop/Restart operation buttons
  • Dashboard inactive projects now have a Start button alongside copy command
  • Dashboard routes list now includes Stop button for Docker Compose projects
  • Static site index status shown in dashboard (πŸ“‹ enabled / πŸ”’ disabled)
  • roji doctor port check now detects if roji itself is using the ports
  • Not Found page and Warning page now use absolute URLs for dashboard assets

Fixed

  • certutil.exe not found on some WSL configurations

0.8.0 - 2026-01-24

Added

  • Native Mode: Run roji as a standalone binary without Docker
    • Single binary execution on host system
    • Direct access to Docker socket from host
    • Simplified deployment for local development
  • Configuration File: YAML-based configuration support
    • Default location: ~/.config/roji/config.yaml
    • XDG Base Directory compliant paths
    • Settings priority: CLI > Environment > Config file > Defaults
  • roji config Command: Manage configuration files
    • roji config show - Display current configuration
    • roji config path - Show configuration file paths
    • roji config init - Create default configuration file
    • roji config edit - Open configuration in editor
  • roji doctor Command: Environment diagnostics with auto-fix
    • Docker daemon and socket checks
    • Network existence verification
    • Port availability checks
    • CA certificate existence and installation status
    • Server certificate validity and domain matching
    • DNS resolution checks
    • --fix flag for automatic remediation
    • --json flag for machine-readable output
  • roji ca Command: CA certificate management
    • roji ca status - Check installation status
    • roji ca install - Install CA to system trust store
    • roji ca uninstall - Remove CA from trust store
    • roji ca export - Export CA certificate (PEM/DER)
    • Platform support: macOS (Keychain), Linux (system CA store), Windows (certutil), WSL (Windows user store)
    • --user flag for user-level installation (no sudo)
    • --windows flag for WSL to Windows installation
  • Makefile: Common development tasks
    • make build - Build binary to ./bin/roji
    • make test - Run all tests
    • make doctor - Build and run doctor

Changed

  • Dashboard host default corrected to roji.{domain} (was incorrectly {domain})
  • *.localhost DNS resolution failure now passes in doctor (Chrome auto-resolves per RFC 6761)
  • CA certificate names now include unique identifier for easier management

Fixed

  • Certificate domain mismatch detection in roji doctor
  • Auto-regeneration of server certificate when domain changes
  • Server startup now detects domain mismatch and regenerates certificate with helpful message

Technical Details

  • XDG paths: config in ~/.config/roji/, data in ~/.local/share/roji/
  • CA installer interface with platform-specific implementations
  • Doctor check interface for extensible diagnostics
  • Certificate domain validation using x509 DNSNames

0.7.0 - 2026-01-15

Added

  • WebSocket Proxy Support: Full bidirectional WebSocket proxying
    • Automatic detection via Upgrade: websocket header
    • Bidirectional message forwarding between client and backend
    • Support for text and binary messages
    • Proper connection cleanup on disconnect
  • gRPC Proxy Support: HTTP/2 based gRPC proxying
    • Automatic detection via Content-Type: application/grpc
    • HTTP/2 transport for backend connections (h2c)
    • gRPC-compatible error responses with Grpc-Status header
    • Streaming support with immediate flush
  • Log Export: Export request logs from dashboard
    • JSON and CSV download formats
    • Filter by host, service, method, and time range
    • Export buttons added to dashboard UI
    • /_api/logs/export endpoint

Changed

  • HTTPS server now explicitly configured with HTTP/2 support (required for gRPC)
  • Request log panel now includes JSON/CSV export buttons

Technical Details

  • WebSocket proxy using gorilla/websocket library
  • HTTP/2 transport via golang.org/x/net/http2 package
  • LogFilter type for flexible log querying
  • CSV escaping for safe export of paths with special characters

0.6.1 - 2026-01-10

Fixed

  • Dashboard crash when no containers connected: Fixed JavaScript error "Cannot read properties of null (reading 'forEach')" that occurred when accessing the dashboard with no containers in the roji network. Routes are now properly initialized as an empty array instead of null.

0.6.0 - 2026-01-10

Added

  • Dark Mode: Automatic theme switching based on system preferences
    • Manual toggle button (sun/moon icon) in dashboard
    • Settings persisted in localStorage
    • FOUC prevention (theme applied before page render)
    • Applied to all pages (dashboard, not found, warning)
  • Request Log Viewer: Real-time request logging in dashboard
    • Ring buffer storing last 100 requests
    • Real-time streaming via SSE
    • Filtering by hostname (dropdown) and path (text input)
    • Shows method, path, status code, latency, and hostname
  • Multiple Network Support: Monitor multiple Docker networks simultaneously
    • Comma-separated network names in ROJI_NETWORK
    • Network badge on each route in dashboard
    • Click network badge to filter routes
  • Container Restart Button: Restart containers directly from dashboard
    • Restart button on each route card
    • Confirmation dialog before restart
    • /_api/containers/{id}/restart API endpoint
  • Request Mocking: Define mock responses via container labels
    • roji.mock.{METHOD}.{PATH} for response body
    • roji.mock.status.{METHOD}.{PATH} for status code
    • Supports JSON and text responses
    • Useful for frontend development without backend
  • Upgrade Support in install.sh: Seamless upgrades via one-liner
    • Automatic detection of existing installation
    • Version comparison with GitHub API
    • Interactive upgrade menu (upgrade/keep/reinstall)
    • Automatic upgrade when running non-interactively
    • Configuration backup before upgrade
    • Configuration migration for new settings
    • Rollback instructions
  • Integration Tests: Docker Compose based testing
    • Real container lifecycle testing
    • Route detection and proxy verification
    • Warning case testing (no port, hostname conflicts)
    • GitHub Actions integration
  • E2E Tests: Full HTTP/HTTPS server testing
    • Real HTTP request verification
    • TLS certificate validation
    • SSE connection testing
    • Dashboard accessibility testing

Changed

  • ROJI_NETWORK now supports comma-separated values for multiple networks
  • Dashboard now shows network badges for each route
  • Request log panel added below routes in dashboard

Technical Details

  • CSS variables for theme management (theme.css)
  • LogBuffer ring buffer for efficient request log storage
  • SSE endpoint /_api/logs for real-time log streaming
  • Network-aware container discovery and routing
  • Docker API ContainerRestart integration

0.5.0 - 2026-01-08

Added

  • Misconfigured Container Warnings: Dashboard now shows containers with configuration issues
    • Containers without exposed ports are displayed with warning badge
    • Warning page with context-aware fix suggestions
    • Hostname conflict detection when multiple services use the same hostname
  • Warning Page: Dedicated error page for misconfigured routes
    • Shows specific issue and affected service
    • Provides tailored fix instructions based on warning type
    • Direct link back to dashboard
  • GitHub Link: Added GitHub repository link with Octocat icon to dashboard header

Fixed

  • Multi-service Hostname Format: Changed from service.project.localhost to service-project.localhost
    • Keeps hostname at single subdomain level
    • Ensures wildcard SSL certificate (*.localhost) compatibility
    • Prevents certificate errors for multi-service projects

Technical Details

  • Warning type detection in proxy handler for template switching
  • Hostname conflict check in Router.AddBackend with warning propagation
  • Inline SVG for GitHub Octocat icon (no external dependencies)

0.4.0 - 2026-01-07

Added

  • Live Dashboard: Real-time route updates via Server-Sent Events (SSE)
    • Automatic updates when containers start/stop without page refresh
    • Connection status indicator (Live/Disconnected)
    • Auto-reconnection on connection loss
  • Browser Notifications: Optional desktop notifications for route changes
    • Notification settings persisted in localStorage
    • "Route added" and "Route removed" notifications
  • Project History: Track active and recent Docker Compose projects
    • Automatically detects Docker Compose projects
    • Shows project name, working directory, and service count
    • Displays last active time for stopped projects
    • One-click copy of restart commands
    • Data persisted in /data/projects.json
  • Dashboard Redirect: Base domain now redirects to dashboard host
    • https://dev.localhost/ β†’ https://roji.dev.localhost/
    • Preserves path and query strings
    • Ensures consistent bookmarks and URL bar history
  • Version Info: Hover tooltip on dashboard showing build metadata
    • Commit hash, build date, and built by information
  • Data Persistence: New /data volume for storing project history
  • API Endpoints:
    • /_api/events - Server-Sent Events stream for real-time updates
    • /_api/projects - Active and inactive project listing

Changed

  • Dashboard now accessible at https://roji.dev.localhost (primary)
  • Base domain https://dev.localhost redirects to dashboard host
  • Environment variable ROJI_DASHBOARD default changed to roji.{domain}
  • Improved dashboard UI with Petite Vue framework (~6KB)
  • Enhanced version display with build information tooltip
  • Updated install.sh to:
    • Create /data directory for project history
    • Mount /data volume in generated docker-compose.yml
    • Set ROJI_DASHBOARD to roji.dev.localhost
    • Add ROJI_DATA_DIR environment variable

Removed

  • examples/ directory (install.sh now generates docker-compose.yml)

Fixed

  • SSE test race condition that occasionally caused test failures
  • Dashboard styling improvements for better visual consistency

Technical Details

  • Petite Vue (~6KB) for reactive UI without build step
  • SSE Pub/Sub pattern in Router for efficient event broadcasting
  • Graceful SSE reconnection handling
  • CSS transitions for smooth route add/remove animations
  • Project metadata collection from Docker Compose labels

0.3.0 - 2026-01-05

Added

  • GoReleaser integration for automated releases
  • Multi-platform binary builds (Linux/macOS/Windows, amd64/arm64)
  • Multi-architecture Docker images (amd64/arm64)
  • Build metadata embedding (version, commit, date, built by)
  • roji version command with accurate version information
  • GitHub Release automation
  • Semantic versioning tags (v1.2.3 β†’ 1.2.3, 1.2, 1, latest)

Changed

  • Switched from manual Docker builds to GoReleaser
  • Improved release workflow automation

0.2.0 - 2026-01-04

Added

  • Security enhancements:
    • Distroless base image (gcr.io/distroless/static:nonroot)
    • X-Forwarded header spoofing protection
    • Path traversal prevention for roji.path label
    • GitHub Actions security scanning (govulncheck, Trivy, hadolint)
  • Performance improvements:
    • SSE support (FlushInterval = -1)
    • Shared Transport for connection pooling
    • Docker Events auto-reconnection
  • Robustness improvements:
    • Explicit server timeouts
    • Docker API timeout handling
    • roji health command for Distroless compatibility

Changed

  • Updated Docker client library to v28.5.2
  • Migrated to Distroless image for better security

0.1.0 - 2026-01-03

Added

  • Initial release
  • Auto-discovery of Docker containers on shared network
  • TLS certificate auto-generation
  • Label-based configuration (roji.host, roji.port, roji.path)
  • Dynamic route updates via Docker Events
  • Dashboard for viewing routes
  • CLI commands: routes, version, health
  • Health check endpoints (/_api/health, /healthz)
  • Status API (/_api/status) with certificate expiration monitoring
  • One-liner installation script (install.sh)
  • HTTP to HTTPS redirection
  • Path-based routing support
  • Cobra-based CLI structure
0