Commit Graph
1628 Commits
Author SHA1 Message Date
Viktor Liu 2253450b4f Merge remote-tracking branch 'origin/main' into embedded-vnc 2026-09-23 15:05:30 +02:00
Viktor Liu 9ede661bab Retrack the agent connection once it is wrapped with the granted peer address 2026-09-23 15:05:30 +02:00
Viktor Liu c26b17653a Accept named-pipe agent connections, carry the remote peer through the agent handshake, and treat pipe disconnects as probes 2026-09-23 14:03:59 +02:00
Viktor Liu b9264d2824 Serve the Windows VNC agent on a ProtectedPrefix named pipe and verify the server PID 2026-09-23 13:23:50 +02:00
Viktor Liu c02a181e43 Place the Windows VNC agent socket under SystemTemp, falling back to Windows\Temp where it does not exist 2026-09-23 12:55:13 +02:00
Viktor Liu e42fe6dfc0 Bound the Windows clipboard read by the block size instead of scanning for a NUL 2026-09-23 12:37:05 +02:00
Riccardo Manfrin cb7ca8ef3f [client,management] Skip route firewall rule computation when no firewall (#7624)
* [client,management] Skip route firewall rule computation when no firewall

A peer that runs with the firewall disabled has no ACL manager and no
firewall to program, so nothing ever reads RoutesFirewallRules: the only
consumers are acl.Manager, which is reached solely when e.acl is set, and
the legacy-management probe in updateNetworkMap, which is guarded by a
non-nil firewall.

Building those rules is the most expensive part of a sync on a peer that
routes many network resources. On a 15k-peer deployment a debug bundle
showed getPeerNetworkResourceFirewallRules accounting for 62% of the
allocations of Calculate, and Calculate for effectively all of the
allocations of handleSync, which was taking 3.2s on average and holding
the engine lock for the duration.

Let the caller ask Calculate to leave the rules out. The client passes
its existing DisableFirewall setting; the management server keeps the
default and still produces them.

RoutesFirewallRulesIsEmpty is set from the resulting empty list, so a
receiver that would otherwise infer legacy management from an empty rule
set does not misread the skip.

* [client,management] Cover the skip flag through the envelope

Review feedback on #7624.

The components test compared only the length of the peer firewall rules, so
a change to their content would have passed while the message claimed they
came out unchanged. Compare the slices.

The skip path was also only exercised by setting the field directly on the
components, which bypasses the envelope conversion where
RoutesFirewallRulesIsEmpty is derived. That bit is what keeps the client from
reading skipped rules as a legacy management server, so it gets a test that
goes through EnvelopeToNetworkMap with the flag set.

* [management] Give the router a peer ACL so the rule comparison bites

Review feedback on #7624.

peer-router-1 appears in no peer ACL in the shared fixture, so its
FirewallRules came out empty and the equality assertion compared two empty
slices — it would have passed even if the peer rules were dropped entirely.

Add a policy covering the router and require the baseline to be non-empty
before comparing.
2026-09-23 11:50:44 +02:00
Viktor Liu 8f158a0827 Reset X11 button state on reconnect, read the Shift+AltGr console table, sync remote Caps Lock with macOS, and restore the FreeBSD stride test 2026-09-23 10:18:13 +02:00
Viktor Liu 395d6521f2 Make the token-leak, VNC port-scoping and bidirectional-SSH tests able to fail 2026-09-23 08:57:38 +02:00
Viktor Liu d6f46b79a3 Count in-flight writes in the final metrics tick and scope the agent token constants to their platforms 2026-09-23 08:54:08 +02:00
Viktor Liu adcd8e3ec5 Report FBU metrics as untracked for service-mode proxied sessions instead of as zero 2026-09-23 08:51:31 +02:00
Viktor Liu e0fe7cccb7 Make the VNC server single-use and recognise crash-leftover desktop processes by their recorded command 2026-09-23 08:36:59 +02:00
Viktor Liu f1a5538840 Map uinput keysyms and typed text through the console's active keymap, and cover the keypad, lock and AltGr keysyms 2026-09-23 08:23:18 +02:00
Viktor Liu 3cc3da41c1 Open the FreeBSD framebuffer read-only and decode depth-24 as 32-bit storage, validate X11 byte order and visual masks, honour destination stride, and drop the cached frame on close 2026-09-23 08:16:15 +02:00
Viktor Liu 47c51fa3c4 Keep Windows input state per session, reject unmappable VkKeyScanA results, queue clipboard and paste reliably, and deselect the GDI bitmap before deleting it 2026-09-23 08:12:31 +02:00
Viktor Liu ff4d6928f7 Fix macOS input permissions, Caps Lock, scroll and layout-independent typing, reconnect the X11 injector, and release VNC resources when start fails 2026-09-23 08:07:11 +02:00
Viktor Liu 9b0a3d3b29 Hand the macOS agent token over stdin, refuse a pre-existing SAS event, bound session writes, and stop the service agent after handlers drain 2026-09-23 07:43:28 +02:00
Viktor Liu 574e68a9e6 Harden the xauth traversal walk against symlinks, keep the runtime dir readable, and enable SeIncreaseQuotaPrivilege for agent spawning 2026-09-22 21:09:53 +02:00
Viktor Liu d0e705bb25 Make the agent token-leak assertion able to fail 2026-09-22 21:06:07 +02:00
Viktor Liu 8f06a43d93 Fix gosec bounds in the byte swizzle, drop the now-unused releaseCapture, and make the SSH and approval-reuse tests actually exercise their paths 2026-09-22 21:03:30 +02:00
Viktor Liu 65bb3ae4f5 Initialize agent capture and input before publishing its socket, and request Screen Recording in direct macOS mode 2026-09-22 20:17:23 +02:00
Viktor Liu 90450bd83b Frame the VNC session metrics explicitly instead of inferring updates from payload bytes 2026-09-22 20:14:53 +02:00
Viktor Liu 88dbac029e Order macOS pointer events by the previous button state, scale the cursor position into framebuffer pixels, and escalate crash cleanup to SIGKILL 2026-09-22 20:10:15 +02:00
Viktor Liu a5c146ccda Restore Windows privileges on shutdown, refuse approval on a locked session, and stop sessions reading a recycled capture buffer 2026-09-22 20:05:54 +02:00
Viktor Liu b703eb4409 Support scroll wheel and the missing modifier keysyms on uinput, and keep X11 capture when XTest is absent 2026-09-22 19:58:14 +02:00
Viktor Liu d1d8c19fc7 Retry a transient cursor-source failure and stop reporting stale framebuffer geometry 2026-09-22 19:56:19 +02:00
Viktor Liu 67c786b4f4 Report the MDM VNC keys to the UI and bound the copyrect tile-hash map 2026-09-22 19:53:43 +02:00
Viktor Liu 24f832e032 Fix big-endian pixel swizzle, closed-capturer panic on FreeBSD, and the macOS agent socket dir symlink race 2026-09-22 19:50:21 +02:00
Viktor Liu 2f03ea4051 Copy authorization slices on update, drain queued input on close, and drop input when the desktop switch fails 2026-09-22 19:46:36 +02:00
Viktor Liu 921aa2b543 Refuse ambiguous X11 display selection, harden the xauth temp file, and reset the service-agent latch on restart 2026-09-22 19:34:11 +02:00
Viktor Liu 5cf52e7e12 Fix uk locale key parity, capture claim race and approval window reuse 2026-09-22 19:21:28 +02:00
Viktor Liu 31a27e575f Default the VNC approval prompt to deny and arm the accept actions 2026-09-22 14:53:41 +02:00
Viktor Liu c66714ce42 Evict a stalled packet capture without holding the daemon mutex 2026-09-22 14:53:41 +02:00
Viktor Liu 6c15fa1d84 Merge branch 'main' into embedded-vnc
# Conflicts:
#	client/ui/frontend/src/app.tsx
#	client/ui/frontend/src/modules/main/MainConnectionStatusSwitch.tsx
#	client/ui/i18n/locales/uk/common.json
#	go.sum
2026-09-22 14:53:34 +02:00
Viktor Liu f6109a3395 [client] Remove the empty GPO DNS policy store on Windows teardown (#7563) 2026-09-22 12:44:10 +02:00
Zoltan Papp bc0671fd21 [client] Fix peers not being notified when the relay connection drops (#7490)
* [relay] Signal relay disconnects through the conn context

AddCloseListener deduplicated listeners by comparing
reflect.ValueOf(callback).Pointer(). For a method value that pointer is
the address of the compiler-generated wrapper, not an identity bound to
the receiver, so every peer's w.onRelayClientDisconnected compared equal.

All peers on the home relay register under the same connectionURL key, so
only the first registration survived and the rest were silently dropped.
On a relay disconnect those peers were never notified: statusRelay stayed
connected and the reconnect guard never fired. The relayed net.Conn itself
was closed by closeAllConns, so nothing leaked, but the peer state machine
did not learn about it. Foreign relays had the same defect scoped to the
peers sharing that server.

Rather than fixing the deduplication, drop the peer-level listener registry
entirely. A relayed Conn now exposes Context(), cancelled when the
connection is torn down, with a cancellation cause naming the reason. This
is the same shape quic-go uses for its Conn and Stream types, and it
removes the whole class of problems around listener identity, lifetime and
deregistration: the signal belongs to the resource instead of a side table.

WorkerRelay watches that context in a goroutine whose lifetime matches the
connection. A watcher that wakes up for a superseded connection compares
the conn pointer against the current one and returns without touching the
state machine, so a fast relay reconnect cannot have a stale watcher tear
down the connection that replaced it.

Client.SetOnDisconnectListener stays: it is server-level and drives the
reconnect guard and foreign relay eviction, unrelated to peers.

handleRelayReady also checks the conn context, closing the race where the
relay dies between OpenConn and the readiness handoff and the peer would
otherwise build a WireGuard endpoint over a dead connection.

TestNotifierDoubleAdd covered the removed mechanism and is gone.
TestForeignAutoClose asserted nothing (both branches logged); it now waits
for the relay to leave the client map and fails if it does not.

* [relay] Fix build: return the concrete conn from Client.OpenConn

OpenConn now returns *Conn, but it still went through connContainer.netConn(),
which widens to net.Conn. The helper had one caller and only existed to produce
the interface value the signature no longer wants, so return container.conn
directly and drop it.

* [relay] Assert the local-close cancellation cause explicitly

The local-close test only rejected ErrServerDisconnected, so it would also
have passed for ErrPeerDisconnected or a bare context.Canceled. closeConn
cancels with net.ErrClosed, so assert that.

* [client] Ignore relay disconnects from superseded connections

The relayed conn watcher compared the conn pointer under relayLock, released
it, and only then tore the connection down. A new offer could install its
replacement in that window, so a watcher that validated the old pointer went
on to close the proxy of the connection that had already replaced it and
report the peer as disconnected while it was up.

Move the decision to where the teardown happens. Conn records which relayed
connection the current proxy was built from, and onRelayDisconnected takes the
connection the signal belongs to and drops it under conn.mu when it is no
longer the current one. Check and effect are now in the same critical section,
so the verdict cannot go stale before it is acted on.

This also covers the proxy read loops, whose disconnect listener took no
argument and had the same defect: it now names the connection it belongs to.
The WG timeout path keeps passing nil, since it deliberately tears down
whatever is current.

* [client] Bind the relayed conn reference to the proxy swap

relayedConnRef was set at the top of the readiness path, but wgProxyRelay only
changes at the end, in setRelayedProxy. The two failure returns in between —
newProxy and ConfigureWGEndpoint — left the reference pointing at a connection
that never became active while the old proxy was still installed. A disconnect
of that old, live relay would then be dismissed as belonging to a superseded
connection and never cleaned up.

Set the reference in setRelayedProxy, next to the proxy it belongs to. Both
success paths go through it and neither failure path does, so no failure branch
has to remember to roll anything back.
2026-09-21 17:00:37 +02:00
Zoltan Papp e70ec07320 Read the session deadline under the status read lock (#7550)
GetSessionExpiresAt took the exclusive lock for a plain field read, so
every caller queued behind writers and behind each other. The Android
SessionMonitor polls it from the main thread, and in the captured ANR
that is exactly where the main thread was blocked while hundreds of
peer-list callbacks held or waited on the same mutex.

d.mux is already an RWMutex and the other getters use RLock; this brings
the deadline read in line with them.
2026-09-15 12:05:57 +02:00
Zoltan Papp 2d28f9002a [client] Fix the Windows tray deadlock on re-entrant window creation (#7449)
* [client] Fix the Windows tray deadlock on re-entrant window creation

The Wails systray runs the left-click handler synchronously inside the
tray window procedure, and creating a window on a running app pumps a
nested Win32 message loop while WebView2 initialises. ensureWindow held
the non-reentrant createMu across that creation, so the second button-up
of a double click re-entered ShowWindow from the pump and blocked the
main thread on its own lock. A goroutine holding createMu while the main
thread pumped, and the Open* dialogs holding mu across NewWithOptions,
Show, Hide and InvokeSync, exposed the same inversion.

WindowManager now serialises creation with a per-slot creating flag and
queues the callers' operations until the window exists, and no Wails call
runs while mu is held. The tray click and second-instance handlers call
ShowWindow off the message loop.

* [client] Serialize window operations while a slot is being created

Callers arriving after the window is published but before the creator
has drained the queue took the existing-window fast path and could run
ahead of older queued operations, so a newer SetURL could be overwritten
by an older one. withWindow now queues every caller while the creating
flag is set and clears the flag only once the queue is seen empty under
the lock.

A factory panic or a nil window left the creating flag set and the slot
dead; creation and drain now reset that state on early exit.

hideOtherWindows records the windows it hid only when no restore ran
in between, tracked by a generation counter, and re-shows them otherwise,
so a restore racing the hide cannot strand hidden windows.

* [misc] Run the client/ui subpackage tests in CI

The three test workflows filtered the package list with a `/client/ui`
prefix match, which dropped the subpackages along with the package that
cannot compile without a frontend build. `services`, `preferences`,
`i18n` and `authsession` all carry Go-side unit tests that never ran,
including the window manager re-entrancy regression test.

Anchor the pattern so only `client/ui` itself is excluded. The linux leg
keeps the prefix match on 386, where only the 64-bit gtk4/webkitgtk dev
packages are installed and the Wails application package would fail to
link, and the alpine container job keeps it for the same reason.

* [misc] Run the client/ui subpackage tests on a gtk4 4.10 runner

The previous commit let the subpackages into the linux client job, where
client/ui/services failed to build: the wails runtime's linux cgo layer
uses GtkFileDialog, which arrived in gtk4 4.10, and the job's ubuntu-22.04
runner ships 4.6.

Move them to their own job pinned to ubuntu-24.04 and restore the linux
client job's original exclusion, leaving the 386 and privileged legs on
the runner they have used since 2024. The new job needs no build cache,
sudo or privileged tag, so it stays a few seconds long.

Darwin and Windows keep the anchored pattern from the previous commit and
already run these tests green, including the window manager re-entrancy
regression test on the platform the deadlock was reported on.

* [client] Defer a window close that lands while the window is still being created

WindowManager publishes a dialog's slot only after the factory returns,
and on Windows the factory blocks in the WebView2 embed pump. A Close*
arriving in that gap found a nil slot and returned without doing
anything, so the dialog appeared afterwards for a flow that had already
been cancelled. The pre-fix Open* dialog functions held mu across the
whole creation, which blocked a concurrent Close* until the slot was
set; removing that lock hold reopened this gap.

Close* now goes through closeWindow: while the slot is being created it
records a closer in pendingClose, and finishCreation runs that closer
before any queued operation, so a window that is going away is never
shown and Wails never sees a Show on a destroyed window, which would
recreate it. Ops queued behind a close are dropped; windowOp carries no
factory, so they cannot be replayed into a new creation, and the
frontend callers reissue on the next state change.

The browser-login slot uses the same restoring closer from both
CloseBrowserLogin and CloseRenewFlow, since the popup's WindowClosing
hook only restores on a user close. Where two closers race one
creation the first registered wins, so a later caller cannot replace a
restoring closer with one that does not restore.
2026-09-14 15:37:46 +02:00
Brandon Hopkins ec0c36b0e7 [client] Add light mode with system, light, and dark theme options (#7344)
* desktop UI light mode

* Theme review fixes plus macOS window outline fix

* Windows runtime chrome re-theming plus apply serialization

* Windows chrome threading and theme event ordering fixes

* Darken toggle and setting sidebar text

* resolve theme appearance, apply on UI thread

* read theme once per window

* Re-assert Windows dark opt-in after SetTheme

* split app-wide GTK theming from per-window chrome

* Update Wails dependency and checksums

* KDE tray icon panel fix

* Five review fixes: theme ordering, cgo dedup, KDE panel resolution

* Path guard hardening, toggle contrast, windows comment

* non-vacuous escape tests

* Default view edits

* Polish settings nav, controls, borders, and disc

* Profiles settings boarder, modals, and buttons

* Additional edits based on feedback

* Switch colors away from slight blue hue

* Update missing lang

* Fix vertical tab active view
2026-09-11 08:25:10 -07:00
Zoltan Papp 794956a7a3 [client] Fix relay instance address race (#7498)
Read the relay instance URL and IP atomically to prevent reconnects from mixing values from different connections. Extend existing connection and offer/answer logs with relay URLs and IPs to help trace mismatched advertisements.
2026-09-11 16:21:10 +02:00
Riccardo Manfrin a419e770d9 [client, proxy] Make the buffer-pool retune reachable while a device is stalled (#7452)
* [client] Track the WireGuard device on the engine as a lock-free handle

Add an atomic handle on the wg device next to wgInterface, stored once the
interface is up and cleared when it is closed. Nothing reads it yet, so this
is a pure addition with no behavior change; it exists so the next commit can
reach the device without taking syncMsgMux.

* [client] Retune the WireGuard buffer pool without the engine lock

SetPerformance took syncMsgMux before reaching the device. That lock is held
by handleSync while it adds and removes peers, and peer removal is exactly
what blocks when a device's buffer pool is exhausted: Peer.Stop waits on a
keepalive timer callback that is itself parked in WaitPool.Get. Raising the
cap is the way out of that state, so the call must not queue behind the lock
the stall is holding.

Read the device through the atomic handle instead. Device.SetPreallocatedBuffersPerPool
takes the pool's own lock and broadcasts, so the waiters wake up.

* [proxy] Extract the buffer-cap apply loop out of the perf handler

Pure move: the loop over the registered clients becomes applyBufferCap, with
the same sequential behavior and the same return values. Split out so the next
commit can change how it iterates without the diff also carrying the move.

* [proxy] Bound the perf endpoint so one wedged client cannot hold it

The apply loop was sequential and unbounded. embed.Client.SetPerformance goes
through the client lock, which Start holds for the whole of a startup, so a
single account that is busy or wedged delayed the new buffer cap for every
other account on the node -- on the endpoint whose whole purpose is to
un-wedge a node.

Apply to all clients concurrently and give the whole call a 5s budget.
Accounts that do not answer in time are reported in "failed" instead of
blocking the response.

* [client] Drop the device handle before closing the interface

close() cleared the atomic handle only after wgInterface.Close() returned, so a
concurrent SetPerformance could still load it, retune a device that is being
torn down, and report the change as applied for an engine that has stopped.
Clear it first, so the window closes before the teardown begins.

Reported by cubic on PR #7452.

* [proxy] Put the per-client retune behind a field

Pure refactor: applyBufferCap calls h.setPerformance instead of the client
method directly, and NewHandler wires it to setClientPerformance. Same call,
same behavior; the seam is what lets the next two commits be tested without a
live embedded client.

* [proxy] Do not report a finished retune as timed out

When the deadline fires, select chooses at random among the ready cases, so a
result already sitting in the buffered channel could be skipped and its account
reported as timed out even though the cap had been applied. Drain what is
buffered before declaring the rest pending.

Reported by cubic on PR #7452.

* [proxy] Keep one retune per account in flight

The 5s budget bounds how long the endpoint waits, not the work: SetPerformance
goes through the embedded client's lock, and on a wedged account Stop holds that
lock forever, so every retry left one more goroutine parked there.

Route each account through a single worker. A request that finds one already
running takes its result if it has landed, and otherwise reports the account
under "in_flight" instead of starting a second attempt. One stuck account now
costs one goroutine, no matter how often the endpoint is called.

Reported by CodeRabbit and cubic on PR #7452.

* [proxy] Make the retune budget a var

Pure refactor: perfApplyTimeout becomes a var so a test can shorten it instead
of waiting five seconds. Same value, same behavior in production.

* [proxy] Extract the buffered-result drain

Pure refactor: the loop that empties the results channel when the deadline
fires becomes collectBuffered. Same behavior; split out so it can be tested
on its own, which the inline version could not be without racing the deadline.

* [proxy] Cover the retune single-flight and the deadline drain

TestApplyBufferCapSingleFlightPerAccount fails without the worker registry:
five calls against a client stuck in its own lock start five blocked workers
instead of one.

TestCollectBufferedCountsResultsReadyAtTheDeadline pins the drain helper's
contract - buffered results counted, errors recorded, only unanswered accounts
left pending. It drives collectBuffered directly: through applyBufferCap the
two select cases race by construction, so an end-to-end version of it would
pass on the unfixed code about half the time.

* [proxy] Keep the worker alongside each pending account

Pure refactor: the pending set becomes a map to the account's worker instead of
an empty struct. Same membership and same behavior; the next commit needs the
worker to resolve an account whose result has not reached the channel yet.

* [proxy] Publish a retune result before releasing its slot

The worker sent its result last, after taking perfMu to remove itself from the
registry. That lock is taken once per account by every caller walking the fleet,
so a worker that finished on time could queue behind an apply over thousands of
accounts and land after the deadline. Send first, deregister after.

Reported by cubic on PR #7452.

* [proxy] Read the worker, not the clock, for a finished retune

Publishing earlier only narrows the window: a client that answers just before
the deadline can still be reported as timed out. At the deadline the workers
themselves are authoritative - a closed done channel means the retune finished
and w.err carries its outcome, ordered by the close. Consult them instead of
declaring every pending account timed out, and keep the timeout label for the
ones actually still running.

Reported by cubic on PR #7452.

* [proxy] Cover the finished-worker resolution at the deadline

Fails on the previous behavior with "applied = 0, want 1": every pending
account was labelled a timeout, including the one whose retune had already
completed.
2026-09-11 09:38:22 +02:00
Nicolas Frati 2f48dbea6a [client] Add a release-wired rootless UBI image variant (#7469)
* [client] Add a release-wired rootless UBI image variant

* [client] Add ARM64 to the rootless UBI image

* [client] Express license output validation as a guard
2026-09-10 21:41:59 +02:00
Zoltan PappandClaude Opus 5 9615d2ab16 [client] Report the remote jobs key in the MDM UI snapshot (#7485)
* [client] Report the remote jobs key in the MDM UI snapshot

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* [client] Align the remote jobs snapshot key with the policy key

The snapshot field carried the JSON tag remoteJobsAllowed while the policy
key is allowRemoteJobs. GetConfigResponse.mDMManagedFields reports the raw
policy keys, and applyMDMRestrictions matches them against the struct's JSON
tags, so the field never turned true for a policy that set the key.

Every other field in Fields already uses its policy key as the JSON tag; this
was the only divergence.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-10 12:06:30 +02:00
Nicolas Frati 15a684248c [client] Support arbitrary UIDs in rootless image (#7440)
* [client] Support arbitrary UIDs in rootless image

* [client] Keep rootless executables root-owned

* [client] Harden arbitrary UID image validation

* [client] Preserve executable access in rootless image

Keep the binary and entrypoint executable when deployments override the runtime group. Retain root ownership so non-root users cannot modify either file.

* [client] Verify rootless state reuse with a stable UID

Persisted profiles remain scoped to the creating UID. Verify same-UID container recreation without broadening application permissions, and document the Kubernetes volume permission behavior observed on OpenShift. Remove unused synthetic-user home metadata.

* [client] Separate image changes from invoking user fix

Keep this PR limited to resolving unmapped non-root invoking users. Move container permissions and their smoke test to a dependent image branch so they can be reviewed separately.

* [client] Restore invoking process user test

Retain coverage for successful current-user lookup without sudo. Numeric-identity fallback tests do not cover this existing behavior.
2026-09-10 12:02:19 +02:00
Viktor Liu 3c32e9eb60 Merge remote-tracking branch 'origin/main' into embedded-vnc 2026-09-09 13:16:47 +02:00
Viktor Liu f89806e55d Convert the VNC MDM test to the explicit policy helper 2026-09-09 12:52:43 +02:00
Viktor Liu d101f6cc46 [client] Redirect DNS port 53 with UDP and TCP DNAT instead of the eBPF forwarder (#7439) 2026-09-09 11:32:11 +02:00
Viktor Liu 1157c55680 Let the browser VNC proxy carry plain RFB to a third-party server 2026-09-09 09:48:32 +02:00
Viktor Liu f372ed09fc Merge branch 'main' into embedded-vnc 2026-09-09 09:30:51 +02:00
Viktor Liu c622075088 Carry the VNC authorization across a listener rebind 2026-09-09 09:23:58 +02:00