Skip to content

Merging and Precedence

When multiple scopes or policies contribute flags, arguments, launch profiles, and routing, the final launch configuration is produced by two steps: obtaining the single Active Policy document for the machine and merging scopes within that document.

What You'll Learn

  • Where each platform gets its policy document, and what it runs on when none is delivered
  • How scopes merge inside the Active Policy (Launch Profile > User Configuration > Policy > Global)
  • How flags, arguments, mounts, networking, and routing resolve conflicts
  • How authorization tie-breaks affect routing and handlers

Policy sources and delivery

Exactly one policy document is active on a machine at a time. There is no cross-source merging: all merges and tie-breaks below happen inside that one document. Where the document comes from differs by platform; how it is evaluated does not.

Windows

The Sandbox Manager service and the Launcher resolve the Active Policy in this order, taking the first that yields a decodable document:

  1. Server downloadGET /api/policy from the Turbo server, with a SHA-256 ETag so an unchanged policy is not re-downloaded, and a Turbo-Policy-Contract header negotiating the strict document contract. The downloaded document is written to the machine-wide path below. A download that fails validation never replaces the stored file.
  2. Machine-wide file%ProgramData%\Turbo\Launcher\policy.json, whether written by the sync above or deployed directly by SCCM, Group Policy or Intune (see Deployment).
  3. In-memory last-known-good — the last document that loaded successfully in this process, kept when a newer candidate is refused.
  4. Embedded default — a built-in allow-all document (one allow rule over targetPath **, schemaVersion 2). It is a bootstrap convenience, not a secure baseline: a Windows machine with no delivered policy is unfiltered, and the Launcher announces that once per process.

The refresh cadence is 5 minutes (configuration.policyUpdateIntervalMinutes adjusts it) plus a directory watcher on the policy folder, so a deployed file applies within seconds. There is no per-user policy file; per-user input is limited to User Configurations validated against the enterprise policy's templates.

macOS

The Turbo daemon (sandboxmanager) resolves the Active Policy in this order:

  1. Managed preferences — a policy document delivered by MDM as a managed-preferences plist.
  2. Machine-wide file/Library/Application Support/Turbo/policy.json.
  3. Deny-all — with neither source present the daemon runs on an empty policy and nothing is launchable.

The daemon polls the sources' digests every 5 seconds and applies a change as soon as it decodes. A source is trusted only when its path is root-owned and not world-writable; an untrusted higher-precedence source fails closed rather than being demoted to the next one. The daemon does not download policy from a server. The macOS launcher never reads a file itself: it receives the daemon's decoded policy bytes over XPC, so launcher and daemon always evaluate the same document.

Empty policy versus no policy

  • A document with no apps (or an empty list) authorizes nothing, on every platform. The evaluator is the same everywhere.
  • No policy delivered at all differs only in the source of last resort: Windows falls back to the embedded allow-all default; macOS falls back to deny-all. Enterprise postures must deliver a policy on both.

Scope Precedence and Merge Order (Active Policy)

Once the Active Policy is selected, configuration merges from least to most specific:

  1. Global Configuration
  2. Application Policy
  3. User Configuration (per-user customizations validated against the Active Policy's templates — Windows only)
  4. Launch Profile

The following diagram summarizes scope precedence within the Active Policy:

Mermaid Diagram

Evaluation order

  1. Start with Global Configuration (configuration.launch.runtime and global mounts).
  2. Merge the matching enabled action: "allow" application policies together with any matching enabled action: "modify" policies — the contributors — in contributor order (below). Apply their modifications and collect their launch profiles (profiles) and capabilities. A modify policy contributes to this merge only; it never authorizes the app (see below).
  3. Apply User Configuration (Windows only).
  4. If a Launch Profile is selected, apply its modifications last on top of the merged result.

Contributor order. Allow and modify rules are interleaved, not grouped: sort every contributor by priority ascending, and order equal priorities by id descending (ordinal, case-insensitive). Each contributor's modifications are layered over the previous one's, so the rule applied last wins a conflict — the highest priority, and among equals the smallest id. That is the same rule that picks the deciding allow (highest priority, then smallest id), so the deciding rule always has the last word over its peers. Identical on the Windows client, the macOS daemon and the macOS launcher.

User Configuration is a Windows-only scope; the macOS readers have no per-user file.

Merge Strategies by Data Type

ElementMerge StrategyConflict Resolution
MountsAdditiveid (or destination when id is missing) last-wins by scope; policy ties follow contributor order.
ArgumentsConcatenationreplace overrides; otherwise prepend/append follow scope order.
Keyed flagsLast-winsScope precedence; policy ties follow contributor order. Windows only.
Switch flagsDe-duplicationDeduplicate by exact string; ordered by scope. Windows only.
NetworkingMixedRules are additive; arrays replace; booleans last-wins; ports replace per scope.
File routingAdditiveHighest rule priority, then rule id; default: true within a rule wins first.

Inside the Policy scope the consistent tie-break is contributor order: higher priority wins, and among equal priorities the smallest id wins. priority defaults to 500 when omitted.

Security and Authorization (Deny vs Allow)

Security rule: Deny overrides allow
During authorization, if any matching enabled policy has action: "deny", the launch is blocked and the app is treated as unauthorized, regardless of how many action: "allow" policies match.

Authorization evaluation occurs before visibility, modifications, capabilities, and routing resolution.

  • Collect all enabled application policies that match the discovered executable. Matching evaluates matchNone first (any hit vetoes the rule), then matchAll (AND), matchAny (OR), or the targetPath shorthand. A rule with no positive form matches nothing.
  • If any matching policy has action: "deny", authorization is denied — at every site, whatever the deny's visibilityConstraints say. The app is not visible and cannot be used by routing.
  • Otherwise, if any matching policy has action: "allow", the app is authorized. The deciding allow is the matching allow with the highest priority, ties broken by the smallest id. Modifications and launch profile merges proceed among the matching allow and modify policies in contributor order; a higher-priority modify overrides a lower one and the allow, but the deciding allow remains the rule the launch is attributed to, and its visibility is the one applied.
  • If no allow policies match, the app is not authorized — even if one or more action: "modify" policies match. modify cannot grant access; it only enriches a launch some allow policy has already permitted.

Site scoping

visibilityConstraints.siteRefs scopes allow and modify rules to the selected site: an allow or modify rule whose siteRefs does not include the selected site is not a contributor and does not authorize or enrich the app there. A deny is never scoped by siteRefs — it applies at every site. Sites exist on the Windows client and the macOS launcher; the macOS daemon has no site concept and accepts-ignores siteRefs.

Visibility is then decided by the deciding allow:

  1. Authorization (allow/deny) must pass first.
  2. The deciding allow's visibility applies; "hidden" hides the app whatever other matching rules say.
  3. When Sites are active and no site is selected, site-scoped rules are ineligible until one is (secure by default).

Site scoping does not affect file association routing — a hidden app can still serve as a handler.

Runtime settings and VM flags

Raw flags (turboFlags, configuration.launch.flags) are a deprecated Windows-only channel; the macOS readers accept and ignore them. Prefer the structured keys.

Flag key normalization

  • A flag’s key is the portion after leading dashes up to the first "=" or whitespace.
    • --clipboard-size=1000 → key clipboard-size
    • --remote-sandbox → key remote-sandbox

Rules

  • Keyed flags: Last-wins per key by scope precedence (Launch Profile > Policy > Global). Within the Policy scope, contributor order applies (highest priority wins, then smallest id).
  • Unkeyed flags (pure switches): De-duplicate by exact string; final order is Profile → Policies (in contributor order) → Global.

Example (flags)

  • Global: --clipboard-size=1000
  • Policy A (priority 200): --clipboard-size=500
  • Profile: none
  • Result: --clipboard-size=500 (Policy overrides Global). If the Launch Profile sets --clipboard-size=200, it wins.

For authoring runtime settings and examples, see: Schema Reference

Arguments

Fields

  • prepend: Insert before base shortcut arguments.
  • append: Insert after base shortcut arguments.
  • replace: Replace the entire argument string.

Rules

  • replace overrides both prepend and append within the same scope.
  • Overall scope precedence is Launch Profile > Policy > Global.
  • When no replace exists at any scope:
    • Prepend order: Global.prepend → Policies.prepend (in contributor order) → Launch Profile.prepend → base shortcut args
    • Append order: base shortcut args → Global.append → Policies.append (in contributor order) → Launch Profile.append

arguments is applied by the Windows client; the macOS readers accept and ignore it.

Example (args concatenation, no replace)

  • Global.append: --enable=sandboxnetwork
  • Policy A.append: --incognito
  • Launch Profile.append: --hide-crash-restore-bubble
  • Result: [base] --enable=sandboxnetwork --incognito --hide-crash-restore-bubble

For authoring arguments and examples, see: Application Arguments

Mounts precedence and merging

When mounts are defined at multiple scopes, final mounts are produced by deterministic precedence and merging:

  • Scope precedence (most specific wins): Launch Profile > User Configuration (Windows) > Policy (App) > Global
  • Additive merge: Mounts are additive across scopes.
  • Conflict resolution:
    • Identity is the mount's id; when id is absent, destination is the identity. Conflicts on the same identity resolve last-wins by scope precedence (Launch Profile overrides Policy overrides Global).
    • Within the Policy scope, ties follow contributor order, consistent with flags/args.
  • Shape: { id?, source, destination, access: readOnly | readWrite, createIfMissing?, siteRefs? } on both platforms; access defaults to readWrite. The macOS daemon serves mounts natively; the Windows client maps them onto its --mount flag.

Visual merge & conflict resolution (mounts)

The following diagram summarizes how mounts are merged across scopes and how conflicts are resolved:

Mermaid Diagram

See authoring details and examples: Mounts & Shared Storage

Networking precedence and proxy routing selection

One proxyRouting shape, applied per platform: the macOS daemon enforces capabilities and evaluates each rule's destination match per connection; the Windows client selects a proxy per site from siteRefs and treats direct and deny as "no proxy" (it has no per-connection enforcement). See Networking.

Scope precedence (most specific wins)

  • Launch Profile > Policy (App) > Global.

Capabilities merging

  • Booleans (outboundAllowed, inboundAllowed, dnsAllowed): last-wins by scope.
  • Arrays (protocols, dnsServers, dnsSearchSuffixes): last-wins replacement by scope for determinism.
  • Ports (allow/deny):
    • Within a scope: deny overrides allow at evaluation time.
    • Across scopes: last-wins replacement of the entire ports object (Policy or Launch Profile can replace Global).
  • egressDefaultAction: last-wins by scope.

Proxy catalog (proxies[])

  • Defined only at Global scope: configuration.network.proxies.
  • App/profile scopes reference proxies by proxyRef; they cannot define proxies.

Proxy routing rules

  • Rules are additive across scopes (Global + Policy + Launch Profile).
  • For each connection (macOS daemon):
    1. Filter to enabled rules whose destination match passes (cidrs, ipRanges, hosts, optional ports/protocol). A rule without match matches every destination.
    2. Choose the rule with highest priority (descending); ties break by rule id (alphabetical). priority defaults to 500.
    3. Apply action:
      • proxy: route via proxyRef. If the proxy endpoint is unreachable the connection fails unless the rule names a failover (direct or deny); proxyCandidates lists the only alternates health may pick.
      • direct: connect without proxy.
      • deny: block the connection (short-circuits).
  • Per site (Windows client): among enabled rules whose siteRefs include the selected site (or have none), the highest-priority proxy rule's proxyRef is the proxy for every launch at that site; direct and deny mean no proxy.

See authoring details:

Launch Profiles

Combining launch profiles

  • Launch profiles from all contributors (matching allow and modify policies) are collected.
  • Conflicts by profile id (compared ordinal, case-insensitive) resolve to the profile from the highest-priority contributor; ties break by contributor id ascending. Identical on every reader.
  • Launch profile modifications apply after all base modifications have been merged.

UI behavior

  • The context menu shows Run (base), then one item per launch profile (displayName), followed by diagnostic/reset entries.

See full launch profile details and examples: Launch Profiles

Routing selection and tie-breaks

Terminology

  • fileTypes: central registry that determines what a file “is” (e.g., pdf, word-docs).
  • fileAssociations: central UX that defines which handlers exist per verb (preview/open/edit/print), which are default, and which appear in Open With.
  • apps[].capabilities: per-app declarations of supported fileTypes and verbs with invocation details; capabilities do not determine UI by themselves.

Resolution pipeline (per file and verb)

  1. Determine fileType:
    • Match the file against fileTypes[].match. If multiple match, prefer a more specific match when determinable (for example, a single-extension type over a broader multi-extension type that also includes the same extension); otherwise tie-break by fileType id alphabetical.
    • Extension matching is case-insensitive.
  2. Collect routing rules:
    • Gather enabled fileAssociations rules where fileTypeRef equals the chosen fileType id.
  3. Aggregate handlers for the requested verb from those routing rules.
  4. Choose default handler:
    • Any handler with default: true.
    • Else highest routing rule priority (descending).
    • Else stable alphabetical tie-break by routing rule id (fallback to rule displayName when id is missing).
  5. Open With:
    • All handlers where showInOpenWith: true.
  6. Security gating:
    • Handlers referencing apps via appRef or targetPath require an enabled action: "allow" app policy that matches the executable; otherwise the handler is ignored.

Print

  • The Print action is available only when at least one routing rule provides verbs.print for the selected fileType.
  • If none provide verbs.print, Print is not offered.

Tie-break ordering within Policy scope

When multiple enabled allow and modify policies match the same executable:

  • The deciding allow is the allow with the highest priority; ties go to the smallest id (ordinal, case-insensitive).
  • Modifications are layered in contributor order — ascending priority, equal priorities by id descending — so the rule that would win selection is applied last and wins conflicts.
  • Launch profile conflicts go to the highest-priority contributor, ties by id ascending.
  • priority defaults to 500 on every reader when omitted.

Tie-breaks for file policies and routing rules

  • File policies (list/import/export/open/delete): Conflicts within the active policy resolve by rule priority (descending), then rule id alphabetical (fallback to displayName when id is missing).
  • File association rules: Primary selection uses explicit default: true within a rule and then rule priority. When a tie-break is needed across rules, use rule id alphabetical (fallback to displayName when id is missing).

UI vs device responsibilities

  • UI-level (routing): Print handlers defined in fileAssociations.verbs.print resolve like other verbs. Selection order is rule priority (descending), then rule id alphabetical; within a rule, default: true wins, else array order. If no applicable rule provides verbs.print, the Print action is not offered for those files.
  • Device-level (runtime): Printer availability inside applications is enforced by the runtime using environment-specific configuration carried as opaque strings via flags. These settings do not change routing selection; they control whether printers are available during app execution.

Precedence for device-level settings carried via runtime settings

  • Scope precedence: Launch Profile > Policy > Global
  • Runtime behavior: Scalars are last-wins by scope; arrays use last-wins replacement. See: Schema Reference

Security gating for routing

Routing entries reference applications by appRef or targetPath. A referenced app must be authorized:

  • There must be at least one enabled application policy with action: "allow" that matches the executable (including high-assurance matchers if present).
  • Otherwise that routing entry is ignored.

For routing semantics and examples, see: File Types & Routing

Troubleshooting precedence

  • “Flag didn’t take effect”: Check for a more specific scope overriding it (Profile usually wins). For keyed flags, verify the final key value.
  • “Arguments missing”: Look for replace at any scope; it overrides prepend/append. If none, verify the scope concatenation order.
  • “Wrong launch profile won”: Ensure unique launch profile id, or adjust policy priority/id to control tie-breaks.
  • “Unexpected file handler”: Confirm the chosen fileType, then inspect routing rule priority, default: true, and rule ids; also ensure security gating did not filter out another candidate.

Templates and precedence

Configuration templates and user configurations are Windows-only; the macOS readers accept and ignore configurationTemplates.

  • Active Policy: Templates are defined and enforced inside the Active Policy. See: Deployment and Policy sources and delivery.
  • Authorization: The presence of templates does not bypass authorization. An app must still be discoverable and allowed by at least one enabled app policy with action: "allow". Templates constrain user-provided inputs (environment variables and mounts).
  • Merge interactions: Templates do not contribute runtime settings or arguments directly. Standard merges for runtime/arguments remain unchanged (Launch Profile > User Configuration > Policy > Global).
  • App linkage: Apps opt into templates via apps[].configurationTemplates. Multiple apps can reference the same template to share a constrained configuration surface.
  • Validation timing: Environment variables referenced inside patterns are resolved at validation time. Mount constraints in templates are applied to user configurations at validation time.

Next steps