feature: managed settings. - #10589
Draft
camilasan wants to merge 47 commits into
Draft
Conversation
camilasan
force-pushed
the
feature/5497/mdm
branch
from
August 19, 2026 15:27
e0beb6d to
24b18b8
Compare
camilasan
force-pushed
the
feature/5497/mdm
branch
from
September 7, 2026 09:41
24b18b8 to
db6b6be
Compare
Move phase, version and legacy discovery out of ConfigFile, Application, AccountManager, AccountState and FolderMan into src/libsync/settings. Assisted-by: Claude Code:claude-opus-4-8 Signed-off-by: Camila Ayres <hello@camilasan.com>
Cover the phase lifecycle, version comparison and legacy discovery. Assisted-by: Claude Code:claude-opus-4-8 Signed-off-by: Camila Ayres <hello@camilasan.com>
Add MIGRATION.md next to the sources. Assisted-by: Claude Code:claude-opus-4-8 Signed-off-by: Camila Ayres <hello@camilasan.com>
camilasan
force-pushed
the
feature/5497/mdm
branch
3 times, most recently
from
September 7, 2026 13:35
6e21f30 to
99cd928
Compare
Resolves the effective value of a setting from a set of injectable sources and returns metadata (winning source, default or locked). Precedence: locked policy, then user config, then the highest default, then the builtin settings. Assisted-by: Claude Code:claude-opus-4-8 Signed-off-by: Camila Ayres <hello@camilasan.com>
Native registry, plist and conf adapters plus buildDeviceSources, replicating the app name selection of ConfigFile::getValue and getPolicySetting. The OS adapters are not unit tested on the Linux build; only the factory is smoke tested. Assisted-by: Claude Code:claude-opus-4-8 Signed-off-by: Camila Ayres <hello@camilasan.com>
…ngs resolver skipUpdateCheck and autoUpdateCheck now go through ManagedSettings with the device sources and the user config, preserving the precedence policy over user over OS default over builtin. The user source carries the connection group so a locked policy still overrides a group scoped user value. Assisted-by: Claude Code:claude-opus-4-8 Signed-off-by: Camila Ayres <hello@camilasan.com>
Assisted-by: Claude Code:claude-opus-4-8 Signed-off-by: Camila Ayres <hello@camilasan.com>
Add ForcedPreferenceSource, a locked platform policy source that contributes a value only when an administrator forces the key. MacForcedPreferenceSource implements it with CFPreferencesAppValueIsForced and CFPreferencesCopyAppValue, so host and per user managed preferences are honored. buildDeviceSources now uses it on macOS instead of reading the managed preferences plist as a file. Assisted-by: Claude Code:claude-opus-4-8 Signed-off-by: Camila Ayres <hello@camilasan.com>
Add ServerManagedSettings, parsing and an allow list sanitizer for the values the server delivers through the support.desktopClient capability. Only allow listed keys pass, and only server lockable keys stay locked. ServerSettingsSource and buildServerSources expose them as ServerDefault (priority 30) and ServerLocked (priority 100), so device policy still wins. Assisted-by: Claude Code:claude-opus-4-8 Signed-off-by: Camila Ayres <hello@camilasan.com>
Add the end to end resolution flow above ManagedSettings and the server delivery flow above ServerManagedSettings, in the same in header style as Migration. Assisted-by: Claude Code:claude-opus-4-8 Signed-off-by: Camila Ayres <hello@camilasan.com>
An admin enforces settings, so LockState, enforceable, the ServerLocked source kind, and the wire and config keys become EnforcementState, enforceable, ServerEnforced, and enforced. ForcedPreferenceSource keeps Apple's forced term. Also shorten comments. Assisted-by: Claude Code:claude-opus-4-8 Signed-off-by: Camila Ayres <hello@camilasan.com>
getConfig resolves a setting name across the enforcement hierarchy and returns the value with its source and enforcement. getConfigBool/Int/String, setConfig (refused when the value is enforced), isEnforced and sourceOf build on it. skipUpdateCheck and autoUpdateCheck now use it; resolveManagedBool is removed. Also document managed config getConfig gateway design. Assisted-by: Claude Code:claude-opus-4-8 Signed-off-by: Camila Ayres <hello@camilasan.com>
getConfig parsed the server settings JSON from the config file on every call. ManagedConfig caches the parsed settings, keyed by config path and guarded by a read write lock, refreshed on write. ConfigFile serverManagedSettings and setServerManagedSettings delegate to it. Reads stay live for the user and device layers. Assisted-by: Claude Code:claude-opus-4-8 Signed-off-by: Camila Ayres <hello@camilasan.com>
When autoUpdateCheck is enforced by device policy or the server, the checkbox is disabled and its tooltip names who manages it. Populating it does not write the value back, and the toggle saves through setConfig, which refuses an enforced value. skipUpdateCheck still hides the whole updater section. Assisted-by: Claude Code:claude-opus-4-8 Signed-off-by: Camila Ayres <hello@camilasan.com>
A trusted server can suggest but not enforce skipUpdateCheck, autoUpdateCheck and the proxy keys; only device policy enforces them, so a server cannot disable updates or reroute traffic. sanitizeServerManagedSettings drops them from a server enforced payload. Virtual files and folder limits stay server enforceable. Assisted-by: Claude Code:claude-opus-4-8 Signed-off-by: Camila Ayres <hello@camilasan.com>
Assisted-by: Claude Code:claude-opus-4-8 Signed-off-by: Camila Ayres <hello@camilasan.com>
camilasan
force-pushed
the
feature/5497/mdm
branch
from
September 7, 2026 14:03
99cd928 to
47b4cdf
Compare
Test targets compile with QT_NO_KEYWORDS, so the slots keyword does not resolve. Use Q_SLOTS. Assisted-by: Claude Code:claude-opus-4-8 Signed-off-by: Camila Ayres <hello@camilasan.com>
Signed-off-by: Camila Ayres <hello@camilasan.com>
The source priority numbers already encode the full precedence, so the separate tier ranking was redundant. Rank on priority alone and drop tierOf. Assisted-by: Claude Code:claude-opus-4-8 Signed-off-by: Camila Ayres <hello@camilasan.com>
Assisted-by: Claude Code:claude-opus-4-8 Signed-off-by: Camila Ayres <hello@camilasan.com>
Signed-off-by: Camila Ayres <hello@camilasan.com>
Signed-off-by: Camila Ayres <hello@camilasan.com>
updateServerManagedSettings ran only on account add and remove, so a policy delivered or changed after login stayed stale. Connect it to capabilitiesChanged so the merged settings are re-persisted. Assisted-by: Claude Code:claude-opus-4-8 Signed-off-by: Camila Ayres <hello@camilasan.com>
setSkipUpdateCheck and setAutoUpdateCheck wrote QSettings directly, bypassing the enforcement guard. Delegate to setConfig so an enforced value is never overwritten. Assisted-by: Claude Code:claude-opus-4-8 Signed-off-by: Camila Ayres <hello@camilasan.com>
Match the guard the rest of the repo uses. Q_OS_MAC also covers iOS. Assisted-by: Claude Code:claude-opus-4-8 Signed-off-by: Camila Ayres <hello@camilasan.com>
Signed-off-by: Camila Ayres <hello@camilasan.com>
Repopulating the update checkbox called setChecked, which fired toggled and wrote the resolved value back as a user override, so a server default got frozen as a user value. Connect to clicked, which fires only on user action. Assisted-by: Claude Code:claude-opus-4-8 Signed-off-by: Camila Ayres <hello@camilasan.com>
The merged set left schemaVersion at 0. Carry the highest version from the subscribed accounts. Assisted-by: Claude Code:claude-opus-4-8 Signed-off-by: Camila Ayres <hello@camilasan.com>
Signed-off-by: Camila Ayres <hello@camilasan.com>
Signed-off-by: Camila Ayres <hello@camilasan.com>
Drop a negative newBigFolderSizeLimit before it reaches the resolver. Assisted-by: Claude Code:claude-opus-4-8 Signed-off-by: Camila Ayres <hello@camilasan.com>
Assisted-by: Claude Code:claude-opus-4-8 Signed-off-by: Camila Ayres <hello@camilasan.com>
Route the folder limit getters and setters through getConfig and setConfig so device policy and server values take effect and an enforced value is not overwritten. getConfig also reads legacy top level values at a lower priority. Assisted-by: Claude Code:claude-opus-4-8 Signed-off-by: Camila Ayres <hello@camilasan.com>
Disable each enforced control in the advanced settings and show a managed label. Assisted-by: Claude Code:claude-opus-4-8 Signed-off-by: Camila Ayres <hello@camilasan.com>
Assisted-by: Claude Code:claude-opus-4-8 Signed-off-by: Camila Ayres <hello@camilasan.com>
Resolve proxy type, host and port together through getConfig and apply the managed value at account load and in the global Qt proxy. Only managed fields replace values and an enforced write is refused. Assisted-by: Claude Code:claude-opus-4-8 Signed-off-by: Camila Ayres <hello@camilasan.com>
Disable the network proxy editor and show a managed label when the proxy is enforced by device policy. Assisted-by: Claude Code:claude-opus-4-8 Signed-off-by: Camila Ayres <hello@camilasan.com>
Assisted-by: Claude Code:claude-opus-4-8 Signed-off-by: Camila Ayres <hello@camilasan.com>
Resolve virtualFilesMode through getConfig so a server or device policy can set or enforce it. Assisted-by: Claude Code:claude-opus-4-8 Signed-off-by: Camila Ayres <hello@camilasan.com>
Preselect or force the virtual files choice in the add folder wizard and the account setup wizard from the managed policy, and disable it when enforced. Assisted-by: Claude Code:claude-opus-4-8 Signed-off-by: Camila Ayres <hello@camilasan.com>
Assisted-by: Claude Code:claude-opus-4-8 Signed-off-by: Camila Ayres <hello@camilasan.com>
Refresh the principle, scope and testing sections to match the shipped folder limit, proxy and virtual files wiring. Assisted-by: Claude Code:claude-opus-4-8 Signed-off-by: Camila Ayres <hello@camilasan.com>
Drop an invalid virtualFilesMode server value in sanitize and add the key to the managed settings schema. Assisted-by: Claude Code:claude-opus-4-8 Signed-off-by: Camila Ayres <hello@camilasan.com>
Assisted-by: Claude Code:claude-opus-4-8 Signed-off-by: Camila Ayres <hello@camilasan.com>
Add tests for virtualFilesMode sanitize validation, schema, and managedVirtualFilesMode resolution, plus proxy per field merge and the global proxy honoring a managed default. Update existing placeholder mode strings to valid values now that sanitize validates virtualFilesMode. Assisted-by: Claude Code:claude-opus-4-8 Signed-off-by: Camila Ayres <hello@camilasan.com>
camilasan
force-pushed
the
feature/5497/mdm
branch
from
September 9, 2026 06:39
47b4cdf to
51888af
Compare
… optional Add a section on how the client behaves without an enterprise subscription and reframe the remaining scope items as optional, since the wired keys are complete. Update the testing section to the tests that landed. Assisted-by: Claude Code:claude-opus-4-8 Signed-off-by: Camila Ayres <hello@camilasan.com>
…ippets Add a schema section covering SettingSpec, the enforceable gate, the builtin default type coercion and the synthesized fallback, and a sources section covering SettingSource, the device and server source factories and how getConfig builds the stack and resolves. Assisted-by: Claude Code:claude-opus-4-8 Signed-off-by: Camila Ayres <hello@camilasan.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Resolves
#5497
This PR is branched off #9191.
Managed settings for the desktop client
Adds a managed settings subsystem so an administrator can set or enforce client settings from three inputs, resolved through one precedence hierarchy: device policy (Windows registry, macOS managed preferences), server delivered values (via the support app capability), and the user config.
Design and resolution flow are documented in
src/libsync/settings/README.md.What it does
ManagedSettingsresolver that ranks sources by enforcement state and priority, converts the resolved value to the type declared in the schema, and reports the winning source plus whether it is enforced.Software\PoliciesandSoftwarehives, macOS forced preferences viaCFPreferencesAppValueIsForced, Linux conf.support.desktopClientfrom the capabilities, sanitizes it against a client allow list, caches the parsed values in a thread safeManagedConfigservice, and persists them for offline use.ConfigFile::getConfigplus thegetConfig<T>template, so callers resolve through the hierarchy instead of reading the config directly.setConfigrefuses to overwrite an enforced value.Security
Scope
Update settings (
skipUpdateCheck,autoUpdateCheck) are wired end to end. The remaining allow listed keys (virtual files mode, proxy, folder limits) are accepted and cached but not yet consumed by their accessors; that is a tracked follow up.Tests
Unit tests cover resolution precedence, type conversion, the getConfig gateway,
setConfigrefusal when enforced, server caching and persistence, and allow list sanitization including the dropped server enforced update and proxy keys.Server side
Delivery depends on the support app change (separate repo and PR) exposing
support.desktopClientin the capabilities, gated on an enterprise subscription.Checklist
AI (if applicable)