a meads (md) managed task log
- created: 2026-06-10T10:22:47Z
- updated: 2026-06-12T11:52:45Z
- status: closed
- priority: P1
- type: bug
- created: 2026-06-10T10:22:47Z
- updated: 2026-06-12T10:22:12Z
share/tunnel/tunnel.go:178 — keepAliveLoop calls sshConn.SendRequest("ping", true, nil) which blocks until the peer replies. On a dead TCP path (OS sleep/wake, NAT timeout, server hard reboot) no RST arrives, so the request blocks for the kernel retransmit timeout (15+ min). The tunnel looks connected but every OpenChannel hangs; the client never reconnects.
This is the most-reported problem in the tracker.
- Race SendRequest against a timer (keepalive interval or a
CHISEL_PING_TIMEOUTenv), close the ssh conn on timeout so the reconnect loop kicks in - Replace the
time.Sleeploop with a stoppable ticker - Review/merge PR #581 (current attempt, has tests)
- #445 — Keepalive timers for disconnected chisel client
- #560 — Client keeps disconnecting every 3-5 minutes
- #579 — Cant reconnect after server reboot
- PR #581 — add timeout to keepAliveLoop (preferred fix)
- PR #583, PR #488, PR #442, PR #481 — earlier attempts
- status: closed
- priority: P1
- type: bug
- created: 2026-06-10T10:22:47Z
- updated: 2026-06-12T10:26:30Z
share/settings/users.go:100-121 — addWatchEvents watches the file path and only reacts to fsnotify.Write. Editors that write via tmp+rename (vim), truncate-then-write scripts, and Kubernetes configmap symlink swaps either kill the watch (inode replaced) or emit Create/Rename events that are ignored. A truncate+write can also race the reader into loading a half-written file: reload fails and stale users stay active until restart.
- Watch the parent directory filtered by filename
- Re-add the watch after Remove/Rename events
- Debounce (~100ms) and only swap users when the JSON parses
- Review/merge PR #587 (adds debounce + path normalization)
- #493 — Intermittent authentication failure after updating users.json (k8s, restart required)
- #485 — Version mismatch when using auth file (related reports)
- PR #587 — debounce and improve config file watcher
- status: closed
- priority: P1
- type: bug
- created: 2026-06-10T10:22:47Z
- updated: 2026-06-12T10:37:22Z
share/tunnel/tunnel_out_ssh_udp.go:65-72 — handleWrite: when udpConns.len() > maxConns (100), the new conn is still dialed and stored in the map but no handleRead goroutine is spawned. Consequences:
- Responses for that flow are never read — silent blackhole
- The entry is never removed (removal only happens in the handleRead defer), so the map never drops below the cap and all subsequent new flows stay broken until the SSH channel closes
- Memory grows unbounded
Affects busy DNS/QUIC tunnels.
Review/merge PR #515 (fixes removal, makes the cap configurable). Longer term, implement the TODO at line 62: replace goroutine-per-flow with a periodic idle sweep.
- PR #515 — fix the udpConns map does not release new conns when its length is over 100
- #406 — Improve udp tunnel stability
- #456 — Exposing a Minecraft Bedrock Server (UDP)
- status: closed
- priority: P1
- type: bug
- created: 2026-06-10T10:22:47Z
- updated: 2026-06-12T10:31:28Z
share/tunnel/tunnel_out_ssh.go:50 skips the ACL for socks channels:
if t.Config.ACL != nil && !socks && !t.Config.ACL(hostPort) {With --socks5 and --authfile both enabled, a modified client can open a socks channel directly — without declaring a socks remote in its config — and get unrestricted egress through the SOCKS server, regardless of its authfile address list. The config-time check (server/server_handler.go:113-122) only validates remotes the client chooses to declare; channels are not required to match declared remotes.
Note: settings.Remote.UserAddr() (share/settings/remote.go:220-225) returns : for socks remotes, so authfiles cannot even express "allow socks" cleanly — users allow it by accident via unanchored regexes.
Channel ACLs were added in commit 44310b6 but left this hole.
- Subject socks channels to the ACL with a well-known token (e.g. require
HasAccess("socks")) - Make
UserAddr()returnsocksfor socks remotes - Document the migration for existing authfiles
- #563 — Are Unauthorized clients allowed to connect and use Chisel server?
- #518 — How could configure authfile when using remote socks
- status: closed
- priority: P2
- type: bug
- created: 2026-06-10T10:22:47Z
- updated: 2026-06-12T11:04:40Z
share/tunnel/tunnel.go:148-163 — NewProxy binds each remote eagerly (tcp/udp listen in tunnel_in_proxy.go:45-70). If remote N fails to bind after remotes 1..N-1 succeeded, BindRemotes returns the error without closing the earlier proxies, so their sockets stay bound for the process lifetime. On the server (reverse mode) a failed client config can orphan ports forever.
The CanListen() precheck in server_handler.go shrinks the window but is TOCTOU (another bind can win between precheck and the real bind) and does not cover resolve errors.
Add Proxy.Close() and close proxies[0..i-1] on error before returning.
- #492 — multiple sockets bind to the same address (related)
- status: closed
- priority: P2
- type: bug
- created: 2026-06-10T10:22:47Z
- updated: 2026-06-12T11:07:30Z
All in the server auth path (server/server.go authUser + server/server_handler.go handleWebsocket):
Session map leaks — authUser stores the user in s.sessions keyed by SSH SessionID (server.go:199-215); server_handler.go:67-77 deletes it only after a successful NewServerConn:
- Client drops after PasswordCallback but before the handshake completes — entry never deleted
- Config request timeout path (
server_handler.go:83-89) returns without Del
Unbounded (if slow) growth on a public server.
Panic race — if users.Len()==0 during auth (allow-all, nothing stored) and an authfile reload makes Len()>0 before the check at server_handler.go:69-74, the handler panics ("bug in ssh auth handler"). net/http recovers it, but the connection dies with a stack trace in the log.
authUser nits (server.go:207-209):
errors.New("Invalid authentication for username: %s")— the%sis literal, never formatteduser.Pass != string(password)is not constant time — a (mild) timing oracle on a public endpoint
- Delete the session entry via defer in handleWebsocket; replace the panic with a logged close
- Better: return the user via
ssh.Permissions.Extensionsfrom PasswordCallback instead of a side map — removes the map, the race, and the TODO atserver.go:212entirely - Fix the error string; use
crypto/subtle.ConstantTimeCompare; add tests
- status: closed
- priority: P2
- type: bug
- created: 2026-06-10T10:22:47Z
- updated: 2026-06-12T11:17:02Z
Three related defects in how the user store is built and reloaded:
(a) Silently disabled — settings.ParseAuth (share/settings/user.go:10-16) returns empty strings when the value has no colon. server/server.go:71-77 only adds the user when Name != "", so chisel server --auth secretword starts with no authentication and no warning. The client likewise silently sends empty creds.
(b) --auth user silently dropped — when --auth is combined with --authfile, the --auth user is added to the same UserIndex that authfile reloads Reset() (share/settings/users.go:61-69 and :157), so the first file reload deletes the --auth user and that client can no longer connect.
(c) Reload does not affect connected clients — the per-connection ACL closure captures the *settings.User object at handshake time (server/server_handler.go:146-148: tunnelConfig.ACL = user.HasAccess). After a reload, removed users keep tunneling until they disconnect, and updated Addrs lists are not applied to existing connections.
- Make auth strings without a colon a fatal config error on both sides
- Keep the --auth user separate from file-sourced users, or re-add it after each reload
- Resolve the user by name from
s.usersat channel-open time so current Addrs always apply; optionally track activessh.Connper user andClose()the ones removed/changed on reload (#549 explicitly expects disconnection). At minimum document the behavior
Pairs with the external-auth (--authurl) idea (task 28).
- #549 — clients are not disconnected when auth file is replaced
- status: closed
- priority: P2
- type: bug
- created: 2026-06-10T10:23:15Z
- updated: 2026-06-12T11:11:58Z
All in client/client_connect.go connectionLoop:
Exit code (:49-52) — after exhausting --max-retry-count the loop breaks, calls c.Close() and returns nil, so the process exits 0. Scripts and systemd units cannot distinguish "tunnel worked and was interrupted" from "never connected".
Backoff Min (:22) — backoff.Backoff{Max: MaxRetryInterval} leaves Min at the library default of 100ms, so a down server gets hit at 100ms/200ms/400ms... by every client — a reconnect stampede when a popular server restarts. Related wart: client/client.go:78 silently raises any MaxRetryInterval < 1s to 5 minutes.
Robustness nits:
:33callserr.Error()before the nil check at:37— connectionOnce currently never returns(true, nil)becausessh.Conn.Wait()returns io.EOF on clean close, but any future change makes this a nil-pointer panic:46—c.Infof(msg)passes pre-built text as the format string; any literal%garbles output
- Return an error when attempts are exhausted so
main.golog.Fatal exits non-zero; keep exit 0 for ctx-cancelled shutdown - Set a saner backoff Min (e.g. 1s) and/or add
--min-retry-interval; warn or document the 5-minute floor - Guard
err != nilfirst; usec.Infof("%s", msg)(grep for the pattern elsewhere)
- status: closed
- priority: P3
- type: bug
- created: 2026-06-10T10:23:15Z
- updated: 2026-06-12T11:23:56Z
share/settings/remote.go:154-163 — the l4Proto regex is case-insensitive ((?i)/(tcp|udp)$) but the returned proto keeps the original case, while every comparison expects lowercase (remote.go:114-127, tunnel_in_proxy.go:48-67, tunnel_out_ssh.go:42). So chisel client SERVER 1.1.1.1:53/UDP decodes successfully, then dies at listen time with "unknown local proto". The head is lowercased but the proto is not — inconsistent.
strings.ToLower the proto in L4Proto; add a remote_test case.
- status: closed
- priority: P3
- type: bug
- created: 2026-06-10T10:23:15Z
- updated: 2026-06-12T11:25:35Z
server/server.go:79-111 — the key-loading paths call log.Fatalf/log.Fatal (failed to read key file, invalid key, failed to generate key, failed to parse key) inside NewServer, which otherwise returns (*Server, error). Chisel is also consumed as a library, and log.Fatal kills the embedding process and skips deferred cleanup.
Return errors; main.go already log.Fatals on the returned error.
- status: closed
- priority: P2
- type: task
- created: 2026-06-10T10:23:53Z
- updated: 2026-06-12T10:59:53Z
share/cnet/conn_ws.go:25-53 reads entire websocket messages into memory (ReadMessage) and buffers the remainder, with no size cap; gorilla/websocket defaults to unlimited message size. On the server this happens before SSH authentication, so an unauthenticated peer can send a multi-GB frame and OOM the process.
Call SetReadLimit on both server (server/server_handler.go after Upgrade) and client (client/client_connect.go after Dial), or switch wsConn.Read to NextReader-based streaming. The pinned x/crypto/ssh supports transport packets up to 256 KiB, so the limit must sit above that plus framing; 512 KiB clears it with room to spare while retaining a finite pre-auth bound. The WS_BUFF_SIZE env already tunes buffer sizes.
- status: closed
- priority: P2
- type: task
- created: 2026-06-10T10:23:53Z
- updated: 2026-06-12T10:56:34Z
share/cos/common.go:12-22InterruptContext only registersos.Interrupt; SIGTERM (docker stop, kubernetes) takes the default kill path so context-based cleanup never runs (the code comment even wonders "windows compatible?")share/cnet/http_server.gois documented as "adds graceful shutdowns" but GoServe callsClose()(immediate) when the ctx ends; thelistenErrfield is dead code
Add syscall.SIGTERM (unix) and make a second signal force-exit. Use http.Server.Shutdown with a grace period, then Close.
- PR #564 — Reuse existing function to implement graceful shutdown logic (review alongside)
- status: closed
- priority: P2
- type: task
- created: 2026-06-10T10:23:53Z
- updated: 2026-06-12T10:52:18Z
cio.Pipe (share/cio/pipe.go:9-30) closes both directions as soon as either io.Copy finishes, so TCP half-close semantics (send, shutdown(WR), read reply) break across the tunnel, and a FIN from one end becomes a full teardown.
Related: share/tunnel/tunnel_out_ssh.go:87-95 accepts the SSH channel before dialing the target, so inbound TCP clients see a successful connection even when the target is down/refusing. Also net.Dial there has no timeout/context (hangs the channel for the OS dial timeout).
Design pass:
- CloseWrite-aware Pipe (
ssh.Channeland*net.TCPConnboth have CloseWrite) - Reject channel on dial failure (or dial-before-accept)
- Context-aware dial with timeout
- e2e coverage in test/e2e
- #535 — Non graceful closing of remote connection
- #447 — when channel is closed, outbound always by use
- PR #536 — Half closing of connections when CloseWrite() is available
- PR #548 — Add CloseWrite() to rwcConn (SOCKS path)
- PR #538 — TCP reset on remote connection failure
- status: closed
- priority: P2
- type: task
- created: 2026-06-10T10:23:53Z
- updated: 2026-06-12T10:45:45Z
.github/workflows/ci.ymlsetspermissions: write-allfor all jobs on every push AND pull_request (also causes duplicate runs on PR branches).github/Dockerfilestamps the version viagit describe --abbrev=0 --tagsduring docker build; the release_docker checkout has no tags fetched (nofetch-depth: 0), so docker images get wrong/empty versions
Scope to least privilege (contents: read for test, contents: write only on release jobs). Review/merge PR #584: goreleaser-built docker images (multi-arch, GHCR + Docker Hub), single release job, scoped permissions.
- status: closed
- priority: P2
- type: task
- created: 2026-06-10T10:23:53Z
- updated: 2026-06-12T10:40:41Z
Superseded by the dependency updates in PR #568 (2025-09) and PR #578 (2026-02) — close: #448, #449, #450, #451, #467, #470, #478, #496, #513, #516, #517.
The dependabot config evidently rots — consider grouped monthly updates or renovate (#559; #452 reported confusing "fake pushes").
- #561 — Heroku demo dead (close via docs task 23)
- #585 — fixed by PR #586
- #445, #560 — fold into keepalive fix (task 1)
- #541 — unsubstantiated buffer-overflow claim; no evidence provided; close with explanation
- #504, #550, #485 — close via version fallback (task 22)
- #498 — close via docs task 23
Keepalive fixes PR #581 / PR #488 / PR #442 — pick #581, close the rest with thanks.
- status: closed
- priority: P2
- type: task
- created: 2026-06-10T10:23:53Z
- updated: 2026-06-12T11:02:07Z
go install github.com/jpillora/chisel@latest produces BuildVersion 0.0.0-src, which then logs "Client version (0.0.0-src) differs from server version (...)" on every connect — recurring user confusion.
In share/version.go, when BuildVersion is the default, fall back to runtime/debug.ReadBuildInfo().Main.Version (gives v1.x.y for module installs). Skip the mismatch warning in server/server_handler.go:104-111 when either side reports a dev/unknown version.
- #504 — Client version (0.0.0-src) differs from server version (v1.9.1)
- #550 — Client version difference on Kali Linux
- #485 — Version mismatch when using auth file
- status: closed
- priority: P3
- type: task
- created: 2026-06-10T10:23:53Z
- updated: 2026-06-12T11:30:50Z
- Demo section: chisel-demo.herokuapp.com is dead (Heroku free tier removed; issue #561). Replace with a fly.io demo (example/fly.toml already exists) or drop the section.
main.go:317claims "remote-host defaults to 0.0.0.0 (server localhost)" — the code defaults RemoteHost to127.0.0.1(share/settings/remote.go:110-112), which is what "server localhost" actually means. Fix and re-render--keyfilehelp is misleading: the "inline base64" example (chisel server --keygen - | base64) is wrong because keygen already outputs ack-...base64 string; no extra base64 step is needed — #498, PR #461- Sweep for accuracy: "fallsback" (
main.go:105), ragged tab indentation in the --fingerprint paragraph
- Demo section: chisel-demo.herokuapp.com is dead (Heroku free tier removed). Replace with a fly.io demo (
example/fly.tomlexists) or drop the section — #561 - Verify install one-liner
curl https://i.jpillora.com/chisel! | bash— PR #562 claims broken - Demo text says
--proxywhere the flag docs say--backend;main.go:189-190registers both names for the same field — document the alias — PR #556 - Document that
--proxycredentials must be URL-encoded (a#in the password truncates the URL) — #396 - Dead links/badges: microbadger badge, Google App Engine tracker (code.google.com)
- Requested examples: TLS setup walkthrough (#533), reverse socks + authfile (#518), cloudflare/CDN fronting notes (#490)
- Document the AUTH env var for both sides and the CHISEL_* env knobs (WS_TIMEOUT, SSH_TIMEOUT, UDP_MAX_SIZE, UDP_DEADLINE, CONFIG_TIMEOUT, SSH_WAIT) — currently undocumented
- status: closed
- priority: P3
- type: task
- created: 2026-06-10T10:23:53Z
- updated: 2026-06-12T11:33:12Z
client/client.go:265-294 setProxy accepts only socks:// and socks5h:// and rejects socks5:// — the most common spelling.
Add socks5 to the allowed schemes (same SOCKS5 dialer). Note in help/docs: all variants resolve DNS via the proxy (golang.org/x/net/proxy.SOCKS5), so socks5 vs socks5h semantics are identical here. One-liner plus a test and help-text update.
- #474 — Support socks5:// protocol as client proxy protocol
- status: closed
- priority: P3
- type: task
- created: 2026-06-10T10:24:37Z
- updated: 2026-06-12T11:36:27Z
server/server_listen.go:41-43: the "LetsEncrypt will attempt to connect to your domain on port 443" warning is computed inside the hasKeyCert branch but guarded by hasDomains — impossible, sincehasDomains && hasKeyCertalready returned an error at line 27. Move the warning into the hasDomains path (port != 443) where it was clearly intended- Line 56
if err == nilis always true (err checked at line 47) — PR #546 removes it; merge/credit
- PR #588 / PR #575 — "forwaring" -> "forwarding" (
server/server_handler.go:126, user-facing error message) - PR #528 — typo fix
- PR #430 — add codespell to CI (prevents recurrence)
- "respresent" —
server/server.go:37 - "recieved" —
share/cos/signal.go:27(user-facing log line) - "aquired" —
share/tunnel/tunnel_in_proxy_udp.go:181 - "extacts" —
share/settings/remote.go:156 - "faily" —
share/settings/remote.go:122 - "successfuly" —
server/server_handler.go:135 - "fallsback" —
main.go:105(help text; overlaps docs task 23)
- status: closed
- priority: P3
- type: task
- created: 2026-06-10T10:24:37Z
- updated: 2026-06-12T11:39:13Z
(a) Legacy MD5 fingerprint prefix match: client/client.go:233 uses strings.HasPrefix(got, expect) — a truncated legacy fingerprint like a5:32 still "verifies" (matches 1 in 65k keys). Require the full 16-octet colon form before accepting.
(b) Unanchored authfile regexes: share/settings/user.go:24-33 uses MatchString — an entry 10.0.0.1:80 also authorizes 210.0.0.1:8080 and 10.0.0.1:8000; dots match any char. Anchoring by default is breaking, so at minimum document loudly with anchored examples — including that an empty-string entry (UserAllowAll) matches everything — and consider an opt-in flag or a startup warning for unanchored patterns.
(The socks UserAddr() == ":" quirk is covered by the SOCKS ACL bug, task 4.)
- #383 — Can chisel server be configured to allow only certain ports?
- #543 — Using auth.json file with target verification
- status: closed
- priority: P4
- type: task
- created: 2026-06-10T10:24:37Z
- updated: 2026-06-12T11:44:17Z
- Makefile linux/windows targets set
CGO_ENABLED=1while goreleaser releases use 0 (static binaries are the point) — align to 0 - Makefile
deptarget uses deprecatedgo get -ufor tools; usego install tool@version.mkdir -pruns on every make parse - Old
//+buildtags (share/cos/signal.go,signal_windows.go,pprof.go) — switch to//go:build - Deprecated
net.Error.Temporary()atshare/tunnel/tunnel_in_proxy_udp.go:91 client/client_test.go:39uses log.Fatal instead of t.Fatal, and calls t.Fatal from the HTTP handler goroutine (illegal cross-goroutine use; hangs instead of failing)- Dead code:
share/cio/pipe.gopipeVis/vis const,share/cnet/http_server.golistenErr field, commented MeterRWC call (tunnel_out_ssh.go:61) - e2e setup sleeps 50ms for client readiness (
test/e2e/setup_test.go:116, acknowledged TODO) — add a readiness signal API to Client instead
- status: inprogress
- priority: P3
- type: idea
- created: 2026-06-10T10:24:37Z
- updated: 2026-06-12T11:21:39Z
PR #582 implements --authurl: the server POSTs {"username", "password"} to an HTTP service; a 200 response with a JSON array of address regexes grants access, anything else denies.
- Response caching/TTL
- Request timeout + fail-closed semantics
- Secret/header for authenticating chisel to the auth service
- Interaction with live-ACL semantics (see task 8 — resolving the user per channel-open would make webhook auth consistent too)
Review the PR rather than reimplementing.
- PR #582 — Add support for server --authurl parameter
- #476 — Feature request: support external authentication system
- #574 — Integration for OTP / Std Logging
- status: closed
- priority: P3
- type: idea
- created: 2026-06-10T10:24:37Z
- updated: 2026-06-12T11:21:11Z
- See connected clients and their IPs — #530
- Log all connection attempts incl. failed auth — #521
- Show authenticated user on connect — #501
- Active-connection count endpoint for autoscaling — #522, PR #408
- Prometheus /metrics — PR #407
- Connect/disconnect hooks — #410, PR #487
- Client identifier — #468
Structured Info-level log on session open/close with user, remote IP, declared remotes (the server currently logs sessions only at Debug level — server_handler.go); failed auth at Info (currently Debugf, server.go:208).
Optional next: a /metrics endpoint (sessions, tunnels, bytes — ConnCount and Meter plumbing already exist) gated behind a flag, reusing the health/version switch in handleClientHandler.
- status: inprogress
- priority: P3
- type: idea
- created: 2026-06-10T10:24:37Z
- updated: 2026-06-12T11:17:32Z
#589 proposes an opt-in SSE+POST transport: long-lived GET for server->client, POST per frame for client->server, reducing to a net.Conn behind the share/cnet seam (like conn_ws.go / conn_rwc.go), keeping the single multiplexed SSH session, stdlib only, websocket stays the default.
Long-standing demand from environments that strip Upgrade headers (#24, #375) and IDS-blocked deployments (#507, #432).
Evaluate building on webdial vs the standalone implementation offered in the issue; buffering-proxy pathologies; auth/path configurability (#566 custom URI pairs well).
- #589 — SSE transport for environments that block WebSocket upgrades
- status: inprogress
- priority: P4
- type: idea
- created: 2026-06-10T10:24:37Z
- updated: 2026-06-12T11:47:23Z
- Accept PROXY protocol on the chisel server listener so the real client IP survives an L4 load balancer — affects the logging/ACL-by-IP asks
- Optionally emit PROXY headers on reverse-remote connections so backend services behind the client see the original source address
Interacts with requestlog TrustProxy (server/server.go:176-178). Parsing must be optional and default-off (header injection risk on public listeners).
- status: inprogress
- priority: P4
- type: idea
- created: 2026-06-10T10:24:37Z
- updated: 2026-06-12T11:48:47Z
Support unix sockets as local listeners and/or remote dial targets (e.g. tunneling a docker.sock or postgres socket).
Main blocker is remote syntax — settings.Remote parsing is colon-delimited (share/settings/remote.go:43-133); something like unix:/path/to.sock on either side needs explicit escaping rules. The dial/listen split in tunnel_in_proxy.go / tunnel_out_ssh.go is already proto-keyed (tcp/udp), so adding unix as an L4Proto is mechanically straightforward.
- #399 — [UDS] add unix domain support to chisel
- status: inprogress
- priority: P4
- type: idea
- created: 2026-06-10T10:24:37Z
- updated: 2026-06-12T11:49:43Z
- --keygen-json (PR #460): emit {key, fingerprint} JSON for automation; pairs with #499 (key automation ask).
--keygen-json: emit{key, fingerprint}JSON for automation — PR #460; pairs with #499 (key automation)- Encrypted PKCS#8 private keys +
--tls-keypassfor mTLS keys — PR #565 - Avoid plaintext proxy password: read from terminal/stdin — PR #532
- TLS fingerprint pinning for the transport layer, complementing the SSH
--fingerprint— #577
Keep test/e2e/env_key_test.go green throughout — regression test for #570 / PR #571 (CHISEL_KEY env fix).
- status: inprogress
- priority: P4
- type: idea
- created: 2026-06-10T10:24:37Z
- updated: 2026-06-12T11:50:36Z
- Random/ephemeral local port (allow port 0 + report allocation) — #434, #410; isPort currently rejects 0 (
share/settings/remote.go:135-144) - Config-file driven client/server — #393; CHISEL_MODE-style env selection for docker — #436
- WebSocket compression / zstd — #529; gorilla supports permessage-deflate via EnableCompression
- IPv4/IPv6 dial preference flag — localhost resolving to ::1 surprises users when the service binds 127.0.0.1 only — #544, #479, #520
- Custom websocket endpoint path (--uri) — #566; pairs with the SSE transport idea (task 30)
- Win7 support — #576; infeasible on modern Go, document minimum OS in README instead
- status: inprogress
- priority: P4
- type: task
- created: 2026-06-12T10:40:33Z
- updated: 2026-06-12T11:52:45Z
The dependabot config produced 11 stale PRs that sat unmerged for years (closed 2026-06-12 in task 21). Decide between dependabot grouped monthly updates or renovate, then implement. Refs: #559 (renovate suggestion), #452 (confusing "fake pushes" from dependabot).