Commit Graph
1482 Commits
Author SHA1 Message Date
Theodor S. Midtlien 58398c29de Add test for already owned profile during migration 2026-09-16 16:00:25 +02:00
Theodor S. Midtlien 9637cb37e5 Recover from dup active profiles that cannot be resolved with username. 2026-09-16 16:00:25 +02:00
Theodor Midtlienandcubic-dev-ai[bot] 9d991e9e43 Update client/internal/profilemanager/service.go
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
2026-09-16 16:00:25 +02:00
Theodor S. Midtlien bc8f50dbda Move known check to inside stamp owner 2026-09-16 16:00:25 +02:00
Theodor S. Midtlien 9854108c86 Fix config concurrent reload during OwnsProfile check 2026-09-16 16:00:25 +02:00
Theodor S. Midtlien f851942ab4 Fix OwnsProfile fallback to active profile 2026-09-16 16:00:25 +02:00
Theodor S. Midtlien baa4f0537c Fix stale server config after stamp 2026-09-16 16:00:25 +02:00
Theodor S. Midtlien 716cb79f8f Use restricted write for config json 2026-09-16 16:00:25 +02:00
Theodor S. Midtlien 2748291525 Only default profile fail open 2026-09-16 16:00:25 +02:00
Theodor S. Midtlien db459b38ec Add one-shot migration 2026-09-16 16:00:25 +02:00
Theodor S. Midtlien fe97bd5b4e Add profile stamping as active migration 2026-09-16 16:00:25 +02:00
Theodor S. Midtlien 1ac303c77e Fix status and list profiles 2026-09-16 16:00:25 +02:00
Theodor S. Midtlien c7451e39ad (WIP) Migrate active_profile 2026-09-16 16:00:25 +02:00
Theodor S. Midtlien a56d40be3c (WIP) List profiles based on ownership by Identity 2026-09-16 16:00:25 +02:00
Theodor S. Midtlien 80b919e854 Implement OwnsProfile on Server 2026-09-16 16:00:25 +02:00
Viktor Liu 15ed6f8f15 Add an optional --allow-group flag restricting the daemon sockets (#7478) 2026-09-16 13:30:10 +02:00
Theodor Midtlienandcubic-dev-ai[bot] abb94ad2d2 Update client/internal/debug/debug.go
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
2026-09-16 10:37:59 +02:00
Theodor S. Midtlien 258a20c433 Fix empty active state read 2026-09-16 10:37:59 +02:00
Theodor Midtlienandcubic-dev-ai[bot] fa3689c079 Update client/internal/debug/debug.go
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
2026-09-16 10:37:59 +02:00
Theodor S. Midtlien 331c490326 Fix typo 2026-09-16 10:37:59 +02:00
Theodor S. Midtlien 2d5ee900d0 Replace RequireFlowInitiator with AuthzLevel and clear flow on switch profile 2026-09-16 10:37:59 +02:00
Theodor S. Midtlien 7705645492 Remove double error log 2026-09-16 10:37:59 +02:00
Theodor S. Midtlien 1538d5f11c Add profile owners to debug bundle 2026-09-16 10:37:59 +02:00
Theodor S. Midtlien 5f7d869426 Fix status and list profiles 2026-09-16 10:37:59 +02:00
Theodor S. Midtlien 45e8bd8af4 Let unimplemented RequireFlowInitiator pass 2026-09-16 10:37:59 +02:00
Theodor S. Midtlien b620173f51 Clarify the Rule comment 2026-09-16 10:37:59 +02:00
Theodor S. Midtlien a585626950 Remove Authorization context and fix bugs in rule and targetProfile 2026-09-16 10:37:59 +02:00
Theodor S. Midtlien fc93bfc561 Add method interceptor tests to ensure that authz is configured for every RPC 2026-09-16 10:37:59 +02:00
Theodor S. Midtlien bbf28a40ed Add authorization framework for gRPC methods 2026-09-16 10:37:59 +02:00
Theodor S. Midtlien cacdb50c0e Add known mark to identity, fix credentials comparison bugs 2026-09-16 10:37:59 +02:00
Theodor S. Midtlien 15003fc495 Add owners to list profile ipc 2026-09-16 10:37:59 +02:00
Theodor S. Midtlien a9fb48d80b Let active profile owner be the source of truth for the session holder 2026-09-16 10:37:59 +02:00
Theodor S. Midtlien 3a2f6df038 Fix debug test 2026-09-16 10:37:59 +02:00
Theodor S. Midtlien 04c00a9a19 Improve owner config parsing 2026-09-16 10:37:59 +02:00
Theodor S. Midtlien abc77e6d92 Use list of owners 2026-09-16 10:37:59 +02:00
Theodor S. Midtlien 3ee80d99bf Fix wasm build 2026-09-16 10:37:59 +02:00
Theodor S. Midtlien 723deda478 Add nolint for unused functions 2026-09-16 10:37:59 +02:00
Theodor S. Midtlien 45b30ef83d Fix ServiceManager.AddProfile signature 2026-09-16 10:37:58 +02:00
Theodor S. Midtlien 77cfc4ac4c Refactor gatePolicy to gateRule 2026-09-16 10:37:32 +02:00
Theodor S. Midtlien 126f3a640b Use sessionholder pointer 2026-09-16 10:37:32 +02:00
Theodor S. Midtlien 6a983eee34 Add WIP profile ownership 2026-09-16 10:37:31 +02:00
Theodor S. Midtlien f4f47db14c WIP: add session ownership 2026-09-16 10:36:43 +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