# Enterprise Controls & Managed Settings

Manage Droid defaults and hard policy controls for models, tools, safety, network access, retention, and telemetry.

Factory's enterprise story is built on a **single, predictable settings hierarchy**. Orgs express hard policy controls and shared defaults in managed settings, then let projects and users customize only where the policy allows it.

Enterprise Controls govern models, tools, safety policies, network restrictions, retention, plugin marketplaces, service accounts, and feature defaults across laptops, CI, VMs, and airgapped environments.

This page is the org-admin reference for the settings **hierarchy**, the **precedence** rules that decide who wins, the **merge semantics** for each data type, and the authoritative **org-managed settings schema**. For personal preferences a user manages in the Factory App, see [App settings](/factory-app/settings). For the CLI settings.json reference, see [Settings](/droid-cli/settings). For per-feature how-tos, see [Custom Models (BYOK)](/model-independence/byok), [MCP](/harness/mcp), [Sandbox](/autonomy-and-safety/sandbox), [Hooks](/harness/hooks), [Plugins](/harness/plugins), [Output styles](/droid-cli/output-styles), and [Autonomy Levels](/autonomy-and-safety/auto-run).

<LabeledDivider label='Resolution model' />

## The settings levels

Settings are authored in `.factory/` folders, using the **same schema** at every level. What changes between levels is precedence.

| Level | Where it lives | Who owns it |
| :---- | :------------- | :---------- |
| **Org** | Managed-settings endpoint, an org `.factory/` bundle, or a [system-managed `settings.json`](#system-managed-settings-file) | Org administrators |
| **Folder** | `<git-root>/.../<subfolder>/.factory/` | Repo maintainers (monorepo subtrees) |
| **Project** | `<git-root>/.factory/` | Repo maintainers |
| **User** | `~/.factory/` | Individual developers |

Each `.factory/` folder can contain:

- `settings.json`: general settings (models, safety, preferences, telemetry).
- `hooks.json`: hook definitions.
- `mcp.json`: MCP server configurations.
- `droids/`, `commands/`, `skills/`, `output-styles/`: droid, command, skill, and output style definitions.

At resolution time the four authored levels are placed into a longer **build order** that also includes a command-line overlay, remote dynamic config, and hardcoded defaults. The full order, highest priority first, is:

| Priority | Level | Source |
| :------- | :---- | :----- |
| 1 | **Org** (and org plugins) | Remote managed settings or system-managed `settings.json` |
| 2 | **Runtime** | The `--settings` overlay passed on the command line |
| 3 | **Folder** | Nested `.factory/` directories in a monorepo subtree |
| 4 | **Project** | `<git-root>/.factory/` |
| 5 | **User** | `~/.factory/` |
| 6 | **Dynamic** | Remote dynamic config |
| 7 | **BuiltIn** | Hardcoded Droid defaults |

For **hard controls**, the highest level present wins, so **Org is authoritative**: lower levels can extend a policy where the schema allows accumulation, but they can never weaken or remove it. For **session defaults**, the order inverts and the most-local level wins. The next section explains why.

---

## How precedence works: hard controls vs session defaults

This is the single most important distinction on this page. Managed settings resolve through **two precedence systems** at once, and they run in opposite directions. For the user-facing perspective on how personal preferences interact with these controls, see [App settings](/factory-app/settings).

<ComparisonTable label='Hard controls vs session defaults'>

| | **Hard controls** | **Session defaults** |
| :--- | :--- | :--- |
| **Who wins** | Highest level present. Org (priority 1) is authoritative. | Most-local level. Runtime > Folder > Project > User > Org > Dynamic > BuiltIn. |
| **What lower levels can do** | Extend where the schema allows accumulation (union arrays, add new locked keys); never weaken, remove, or re-enable. | Override the value chosen by a less-local level, but only within the ceilings set by hard controls. |
| **Fields** | `modelPolicy`, `mcpPolicy`, `permissionRules` (and deprecated command lists), `sandbox`, managed `hooks`, `strictEnabledPlugins`, `strictKnownMarketplaces`, `subagentModelSettings`, plus scalars like `maxAutonomyLevel`, `subagentAutonomyLevel`, `cloudSessionSync`, `wikiCloudSync`, `voiceDictationEnabled`, `imageGenerationEnabled`, `imageGenerationAllowedModelIds`, `sessionRetentionDays`. | The keys inside `sessionDefaultSettings`: `model`, `reasoningEffort`, `interactionMode`, `autonomyLevel`, `autonomyMode`, `specModeModel`, `specModeReasoningEffort`; plus `missionOrchestratorModel`, `missionOrchestratorReasoningEffort`, `imageGenerationModel`, and the model and reasoning keys inside `missionModelSettings`. |

</ComparisonTable>

Session defaults rank levels so that **lower (more-local) wins**: Runtime is most authoritative, then Folder, Project, and User, with Org, Dynamic, and BuiltIn acting as fallbacks. So a user who sets a preferred `model` overrides the org's default `model`, and a `--settings` runtime overlay overrides even the user. The six mission model and reasoning fields follow the same order and resolve independently, so a local worker-model choice can override the org fallback while the org's validation-worker default still applies. These choices are still **capped by hard controls**: the org `modelPolicy` allowlist decides which models exist at all, and `maxAutonomyLevel` caps the effective `autonomyLevel` no matter who set it.

In short: orgs decide the **boundaries** (hard controls, top-down), and users pick their **preferred defaults inside those boundaries** (session defaults, bottom-up).

---

## Merge semantics

Within each precedence system, the actual merge depends on the data type. Most settings use the four merge modes below. Permission rules have a separate ID-based resolution step.

| Merge mode | How it works | Examples | Best for |
| :--------- | :----------- | :------- | :------- |
| Simple values, first wins | For scalar hard-control values (strings, numbers, booleans), the first level that sets the value wins. Lower levels cannot change or remove it. | `maxAutonomyLevel`, `cloudSessionSync`, `wikiCloudSync`, `voiceDictationEnabled`, `sessionRetentionDays` | Organization-wide ceilings, feature gates, and retention decisions. |
| Arrays, union | Array fields accumulate across levels. Org entries are always present; project, folder, and user levels can add more without removing or weakening higher-level entries. | Legacy command lists, enabled hooks | Shared lists that teams may extend; command-rule coexistence is described below. |
| Named arrays, locked entries | Entries merge by their identifier. Lower levels can add new identifiers but cannot modify or remove entries defined at a higher level. | `customModels`, keyed by `id` | Centrally managed catalogs that projects and users may extend. |
| Objects, locked keys | Keys defined at a higher level are locked. Lower levels can add new keys but cannot modify or delete existing ones. | MCP server definitions | Centralized control over critical configuration, with room for project or user additions. |

For example, if the org defines a `customModels` entry with the ID `custom:approved-model`,
projects and users can add entries with other IDs, but they cannot change or remove the
org-defined entry.

The scalar keys inside `sessionDefaultSettings` and the six mission model and reasoning fields are exceptions to "first wins": the **most-local** value for each field wins, not the first. See [How precedence works](#how-precedence-works-hard-controls-vs-session-defaults).

### Permission rule resolution

`permissionRules.rules` does not use ordinary array union. Eligible sources are ordered
Org, Runtime, Folder, Project, User. Higher-priority valid definitions claim repeated
rule IDs; different IDs accumulate. Within a folder's settings, `settings.local.json`
precedes `settings.json`. Project and folder rules require workspace trust. Plugins and
dynamic configuration cannot author native permission rules.

After IDs are resolved, matching blocks take precedence over requests for approval,
which take precedence over allows. An org allow does not cancel a project block with a
different ID. A disabled rule still suppresses lower-priority definitions with the same
ID; deleting it removes that suppression. An empty rules array does not clear inherited
rules.

Deprecated command lists still participate for backward compatibility. A native rule
can replace a custom legacy restriction from the same source when its matching
requirements are met, but restrictions from other sources remain effective. See
[coexistence and migration](/autonomy-and-safety/permission-rules#migrate-legacy-command-lists)
before changing an existing policy.

---

## System-managed settings file

For deployments where org policy must be applied **before any user signs in**, including managed laptops, CI runners that aren't authenticated to Factory, airgapped installs, or restricted-network installs, IT/MDM administrators can drop a `settings.json` at a hardcoded, well-known path on each machine:

| OS           | Path                                                  |
| ------------ | ----------------------------------------------------- |
| macOS        | `/Library/Application Support/Factory/settings.json`  |
| Linux & WSL  | `/etc/factory/settings.json`                          |
| Windows      | `C:\Program Files\Factory\settings.json`              |

The file uses the same [org-managed settings schema](#org-managed-settings-schema) as the rest of this page.

When this file is present it is the **authoritative source for org-level settings** on that machine. It short-circuits any API fetch or developer-mode environment overrides for org settings. The `factoryTier` signal is still queried from the Factory API on a best-effort basis (failures are non-fatal) so enterprise-gated behaviors keep working for managed deployments.

If the file is absent or unreadable, Droid falls back to the existing org-settings flow (Factory App / managed-settings endpoint). If the file is present but malformed or fails schema validation, org settings resolve to an empty policy and the failure is logged. A broken admin deployment is **not** silently bypassed.

This is the recommended way to ship base policy with an MDM image or installer; users cannot override or weaken it.

## Manage permission rules

Use the **Permission Rules** section of Enterprise Controls to configure organization
command policy. It replaces the deprecated command allowlist, denylist, and blocklist
settings, which remain respected for backward compatibility.

1. Select **Manage rules**, then add a rule or select an existing one.
2. Choose **Allow**, **Ask for approval**, or **Block**. Enter the ordered tokens under **Command starts with**, with alternatives where needed.
3. Add at least one **Should match** and one **Should not match** example. A passing example means the matching expectation passed, not that the command is allowed. Examples are never executed.
4. Select **Done** to return to the pending Enterprise Controls draft.
5. Use **Review Changes** and save to apply the policy. Closing the editor does not save changes.

The **JSON** view accepts a rules container, a rule array, or a full settings object.
It imports only the rules and preserves their IDs. Review organization scope before
saving. This differs from `droid rules check --file`, which rejects a full settings
wrapper.

Disabling a rule keeps its ID claim; deleting it can expose a lower-priority rule.
Invalid definitions and failing examples prevent managed-settings saves. For local
files, invalid rules can instead be skipped at runtime, so always run the checker.

See [Permission rules](/autonomy-and-safety/permission-rules) for the schema, matching
limits, and preview workflow. If this section is unavailable, contact [support@factory.com](mailto:support@factory.com)
before replacing existing managed lists.

<LabeledDivider label='Schema reference' />

## Org-managed settings schema

Below is the full schema for org-managed settings. These are configured by org admins through the <a href="https://app.factory.ai">Enterprise Controls</a> in the Factory App. Every property is optional; omit any field you do not need to set.

### Session defaults

<ResponseField name="sessionDefaultSettings" type="object">
  Default session preferences the org ships to all members. Every key here is a [session default](#how-precedence-works-hard-controls-vs-session-defaults): the most-local level wins, capped by hard controls. Users can override these with their own preferences (see [App settings](/factory-app/settings)); the values here act as the org-wide fallback.

  **Properties**

  <ResponseField name="model" type="string">
    Default model identifier. See [Models](/models) for the available IDs.
  </ResponseField>
  <ResponseField name="reasoningEffort" type="string">
    How much thinking the model does before responding. One of `none`, `dynamic`, `off`, `minimal`, `low`, `medium`, `high`, `xhigh`, `max`.
  </ResponseField>
  <ResponseField name="interactionMode" type="string">
    Droid interaction mode. One of `auto`, `spec`, `agi`.
  </ResponseField>
  <ResponseField name="autonomyLevel" type="string">
    Default autonomy level for the session. One of `off`, `low`, `medium`, `high`. Capped by `maxAutonomyLevel`.
  </ResponseField>
  <ResponseField name="specModeModel" type="string">
    Model to use when spec mode is active. See [Models](/models).
  </ResponseField>
  <ResponseField name="specModeReasoningEffort" type="string">
    Reasoning effort override for spec mode. Same values as `reasoningEffort`.
  </ResponseField>
  <ResponseField name="autonomyMode" type="string">
    **Deprecated.** Use `interactionMode` and `autonomyLevel` instead.
  </ResponseField>
</ResponseField>

### Mission defaults

Mission model and reasoning defaults use the same most-local precedence as the session defaults above. Each of the six fields resolves independently across Runtime, Folder, Project, User, Org, Dynamic, and BuiltIn levels. Org values therefore act as fallbacks that users and projects can override within the org's model policy.

<ResponseField name="missionOrchestratorModel" type="string">
  Default model for the mission orchestrator. See [Models](/models) for the available IDs.
</ResponseField>

<ResponseField name="missionOrchestratorReasoningEffort" type="string">
  Default reasoning effort for the mission orchestrator. Same values as `reasoningEffort`.
</ResponseField>

<ResponseField name="missionModelSettings" type="object">
  Default models and reasoning effort for mission workers and validation workers. The user-managed `skipScrutiny` and `skipUserTesting` settings are not accepted in org-managed settings.

  **Properties**

  <ResponseField name="workerModel" type="string">
    Default model for mission workers. See [Models](/models) for the available IDs.
  </ResponseField>
  <ResponseField name="workerReasoningEffort" type="string">
    Default reasoning effort for mission workers. Same values as `reasoningEffort`.
  </ResponseField>
  <ResponseField name="validationWorkerModel" type="string">
    Default model for mission validation workers. See [Models](/models) for the available IDs.
  </ResponseField>
  <ResponseField name="validationWorkerReasoningEffort" type="string">
    Default reasoning effort for mission validation workers. Same values as `reasoningEffort`.
  </ResponseField>
</ResponseField>

### Autonomy, safety, and commands

<ResponseField name="maxAutonomyLevel" type="string">
  Maximum autonomy level any user or project can set. One of `off`, `low`, `medium`, `high`. A hard ceiling that caps the `autonomyLevel` session default.
</ResponseField>

<ResponseField name="webSearchDisabled" type="boolean">
  When `true`, removes the built-in Web Search tool (`WebSearch`) from the CLI tool catalog, tool discovery, and Script tool access. Defaults to `false`; omitting it or setting it to `false` preserves availability subject to existing controls.

  Droid reads this field only from org-managed settings. It ignores user, project, and folder values, so lower-level settings can neither disable Web Search nor re-enable it against org policy. In Enterprise Controls, turn on **Built-in Network Tools → Disable Web Search** to set `webSearchDisabled` to `true`.

  This controls tool availability. `builtInToolAutonomyOverrides` controls approval requirements for available tools. Web Fetch (`FetchUrl`) is unchanged and remains subject to its separate autonomy, network, and airgap controls.
</ResponseField>

<ResponseField name="builtInToolAutonomyOverrides" type="object">
  Per-tool [autonomy level](/autonomy-and-safety/auto-run) required to run a built-in network tool without a confirmation prompt. Map of built-in tool name to `low`, `medium`, or `high`. A tool auto-runs only when the session's Autonomy Level is at or above its configured requirement; otherwise Droid asks for approval. Tools without an entry default to `low`. Enforced from org-managed settings only, so user and project settings cannot weaken it. Configured under **Built-in Network Tools** in Enterprise Controls.
  <ResponseField name="WebSearch" type="string">
    Autonomy level required to run the built-in Web Search tool. One of `low`, `medium`, `high`. Defaults to `low`.
  </ResponseField>
  <ResponseField name="FetchUrl" type="string">
    Autonomy level required to run the built-in Web Fetch tool. One of `low`, `medium`, `high`. Defaults to `low`.
  </ResponseField>
</ResponseField>

<ResponseField name="subagentAutonomyLevel" type="string">
  Autonomy level applied to subagents spawned by the Task tool. One of `inherit`, `off`, `low`, `medium`, `high`; defaults to `inherit`, which falls back to the parent session's autonomy level. An explicit level is clamped to `maxAutonomyLevel` so it can never exceed the enterprise cap.
</ResponseField>

<ResponseField name="subagentModelSettings" type="object">
  Complexity-to-model routing for [subagents](/harness/subagents). Applies when a subagent's droid uses `model: inherit` and the parent passes a complexity tier.
  <ResponseField name="lightModel" type="string">
    Model used for `light` complexity tasks. A model identifier, or omit to inherit the spawning session's model.
  </ResponseField>
  <ResponseField name="lightReasoningEffort" type="string">
    Reasoning effort for the light tier. Same values as `reasoningEffort`.
  </ResponseField>
  <ResponseField name="mediumModel" type="string">
    Model used for `medium` complexity tasks. A model identifier, or omit to inherit the spawning session's model.
  </ResponseField>
  <ResponseField name="mediumReasoningEffort" type="string">
    Reasoning effort for the medium tier. Same values as `reasoningEffort`.
  </ResponseField>
  <ResponseField name="heavyModel" type="string">
    Model used for `heavy` complexity tasks. A model identifier, or omit to inherit the spawning session's model.
  </ResponseField>
  <ResponseField name="heavyReasoningEffort" type="string">
    Reasoning effort for the heavy tier. Same values as `reasoningEffort`.
  </ResponseField>
</ResponseField>

<ResponseField name="enableDroidShield" type="boolean">
  Enable Droid Shield safety checks.
</ResponseField>

<ResponseField name="permissionRules" type="object">
  Recommended shell command policy. Requires `version: 1` and a `rules` array.
  Each rule requires an `id`, a `decision` (`allow`, `ask`, or `block`), an exact
  ordered `match.prefix`, and non-empty `tests.match` and `tests.noMatch` arrays.
  Optional fields are `reason` and `enabled`. See the
  [rule reference](/autonomy-and-safety/permission-rules#rule-fields) and
  [resolution semantics](#permission-rule-resolution).
</ResponseField>

<ResponseField name="commandAllowlist" type="string[]">
  **Deprecated.** Still respected for backward compatibility. Legacy command patterns
  that allow execution without command approval unless a restriction wins. Use
  `permissionRules` with `decision: "allow"` for new policy.
</ResponseField>

<ResponseField name="commandDenylist" type="string[]">
  **Deprecated.** Still respected for backward compatibility. Legacy command patterns
  that require confirmation in permission-checked execution. Use `permissionRules`
  with `decision: "ask"` for new policy, not `block`.
</ResponseField>

<ResponseField name="commandBlocklist" type="string[]">
  **Deprecated.** Still respected for backward compatibility. Legacy command patterns
  that produce a hard block. An effective block has no approval path, including under
  `--skip-permissions-unsafe`. Use `permissionRules` with `decision: "block"` for new
  policy. Review [migration semantics](/autonomy-and-safety/permission-rules#migrate-legacy-command-lists)
  before mixing rules and lists.
</ResponseField>

<ResponseField name="allowManagedHooksOnly" type="boolean">
  When `true`, only org-managed hooks and hooks from org-enabled plugins are loaded; hooks defined at the user and project levels are dropped. Defaults to `false`, where hooks from every level participate. See [Org-managed hooks](/harness/hooks#org-managed-hooks).
</ResponseField>

<ResponseField name="hooks" type="object">
  Org-managed hook definitions, keyed by [hook event](/harness/hooks#hook-events). Org-defined hooks are always loaded and cannot be removed by lower levels. For event semantics and authoring, see [Hooks](/harness/hooks).

  **Properties**

  <ResponseField name="EventName" type="object[]">
    One key per hook event: `PreToolUse`, `PostToolUse`, `Notification`, `UserPromptSubmit`, `Stop`, `SubagentStop`, `PreCompact`, `SessionStart`, `SessionEnd`. Each value is an array of matcher groups. Unknown event keys are tolerated but warned about at load time.

    **Matcher group properties**

    <ResponseField name="matcher" type="string">
      Tool name pattern to match (only applies to `PreToolUse` and `PostToolUse`).
    </ResponseField>
    <ResponseField name="commandRegex" type="string">
      Optional regular expression matched against the command.
    </ResponseField>
    <ResponseField name="hooks" type="object[]" required>
      Commands to run when the matcher matches.

      **Hook command properties**

      <ResponseField name="type" type="string" required>
        Must be `"command"`.
      </ResponseField>
      <ResponseField name="command" type="string" required>
        Shell command to execute.
      </ResponseField>
      <ResponseField name="timeout" type="number">
        Per-command timeout in seconds.
      </ResponseField>
    </ResponseField>
  </ResponseField>
</ResponseField>

<ResponseField name="hooksDisabled" type="boolean">
  Disable all hooks for the session.
</ResponseField>

<ResponseField name="showHookOutput" type="boolean">
  Show hook output in the session view.
</ResponseField>

<ResponseField name="sandbox" type="object">
  Built-in sandboxing for command execution and file access. For sandbox behavior and modes, see [Sandbox](/autonomy-and-safety/sandbox).

  **Properties**

  <ResponseField name="enabled" type="boolean">
    Whether sandboxing is enabled.
  </ResponseField>
  <ResponseField name="mode" type="string">
    Sandbox scope. One of `per-command` or `whole-process`.
  </ResponseField>
  <ResponseField name="filesystem" type="object">
    Filesystem access lists: `allowRead`, `allowWrite`, `denyRead`, `denyWrite` (each a string array of paths).
  </ResponseField>
  <ResponseField name="network" type="object">
    Network access: `allowedDomains`, `allowUnixSockets`, `allowAllUnixSockets`, `allowLocalBinding`, and `httpProxyPort` / `socksProxyPort` for proxying egress.
  </ResponseField>
</ResponseField>

### Models and BYOK

For BYOK and gateway setup, see [Custom Models (BYOK)](/model-independence/byok).

<ResponseField name="customModels" type="object[]">
  Org-provisioned custom model definitions.

  **Properties**

  <ResponseField name="model" type="string" required>
    Model name sent to the provider API.
  </ResponseField>
  <ResponseField name="id" type="string" required>
    Unique identifier for this custom model entry. Must start with `custom:` (e.g. `custom:my-internal-model-v1`).
  </ResponseField>
  <ResponseField name="index" type="integer" required>
    Display order index.
  </ResponseField>
  <ResponseField name="baseUrl" type="string" required>
    Base URL of the model API endpoint.
  </ResponseField>
  <ResponseField name="apiKey" type="string">
    API key for authenticating with the provider. Optional -- omit for keyless endpoints. Supports `${VAR_NAME}` environment variable references.

    <Warning>
      **Static API keys are blocked for org-managed models.** The managed-settings endpoint rejects any custom model with a static `apiKey` value. Use a `${VAR_NAME}` environment variable reference instead.
    </Warning>
  </ResponseField>
  <ResponseField name="authMode" type="string">
    Authentication mode for HTTP Anthropic models. Set to `bearer` to send the configured credential as `Authorization: Bearer` instead of `x-api-key`. Bearer mode requires `provider: "anthropic"` and isn't supported for Bedrock models. See [Anthropic bearer authentication](/model-independence/byok#anthropic-bearer-authentication).
  </ResponseField>
  <ResponseField name="provider" type="string" required>
    Provider type. One of `anthropic`, `openai`, `generic-chat-completion-api`, `bedrock-converse`, `factory`, `google`, `xai`, `voyage`.
  </ResponseField>
  <ResponseField name="displayName" type="string" required>
    Human-readable name shown in the model picker.
  </ResponseField>
  <ResponseField name="maxContextLimit" type="integer">
    Maximum context window size in tokens.
  </ResponseField>
  <ResponseField name="enableThinking" type="boolean">
    Enable extended thinking / chain-of-thought.
  </ResponseField>
  <ResponseField name="thinkingMaxTokens" type="integer">
    Maximum tokens for the thinking phase.
  </ResponseField>
  <ResponseField name="maxOutputTokens" type="integer">
    Maximum output tokens.
  </ResponseField>
  <ResponseField name="extraHeaders" type="object">
    Additional HTTP headers to send with each request. Map of header name to value. For org-managed models, only approved non-secret headers are visible to non-managers; new non-approved header names are rejected.
  </ResponseField>
  <ResponseField name="extraArgs" type="object">
    Additional provider-specific arguments passed to the API.
  </ResponseField>
  <ResponseField name="noImageSupport" type="boolean" required>
    Set to `true` if the model does not support image inputs.
  </ResponseField>
  <ResponseField name="bedrock" type="object">
    AWS Bedrock routing options for this model. See [AWS Bedrock](/model-independence/byok#aws-bedrock) for the full field list.

    **Properties**

    <ResponseField name="awsRegion" type="string">
      AWS region for Bedrock requests. Supports `${VAR_NAME}` interpolation, so one org-distributed entry resolves per environment.
    </ResponseField>
    <ResponseField name="awsProfile" type="string">
      AWS profile used to resolve credentials and, as a last resort, a region. Supports `${VAR_NAME}` interpolation.
    </ResponseField>
    <ResponseField name="bedrockBaseUrl" type="string">
      Explicit Bedrock endpoint override, for example a VPC endpoint. A full URL or a `${VAR_NAME}` reference that expands to one.
    </ResponseField>
    <ResponseField name="requestMetadata" type="object">
      Key-value tags attached to every Bedrock inference call for this model, as a map of name to string value, so Droid usage is attributable in your Bedrock model-invocation logs and cost reports. Keys and values support `${VAR_NAME}` interpolation. Configured per model rather than org-wide, and readable by every member, so it is attribution data and must not carry secrets. See [Request metadata](/model-independence/byok#request-metadata).
    </ResponseField>
  </ResponseField>
</ResponseField>

<ResponseField name="modelPolicy" type="object">
  Org-level model access control. Allowlists use higher-wins (a higher level's allowlist is the ceiling); `blockedModelIds` is a union across levels.

  **Properties**

  <ResponseField name="allowedModelIds" type="string[]">
    Model IDs that are explicitly allowed. See [Models](/models) for the available IDs.
  </ResponseField>
  <ResponseField name="blockedModelIds" type="string[]">
    Model IDs that are explicitly blocked. Combined across levels: once blocked at any level, always blocked.
  </ResponseField>
  <ResponseField name="allowCustomModels" type="boolean">
    Whether users may add their own custom models (user BYOK). Set to `false` to disable user-supplied keys entirely.
  </ResponseField>
  <ResponseField name="allowedBaseUrls" type="string[]">
    Base URLs that custom models are allowed to connect to. Use this to force all custom-model traffic through an approved LLM gateway.
  </ResponseField>
  <ResponseField name="allowAllFactoryModels" type="boolean">
    Allow all current and future Factory-hosted models except those in `blockedModelIds`. Blocking an individual model keeps that exception disabled while new models are allowed automatically.
  </ResponseField>
  <ResponseField name="isFastModelsAllowed" type="boolean">
    Whether fast model variants may be used.
  </ResponseField>
  <ResponseField name="requireExplicitOptInModelIds" type="string[]">
    Model IDs that require an explicit org opt-in before they can be used.
  </ResponseField>
  <ResponseField name="allowFactoryRouterByok" type="boolean">
    Whether the Factory Router may be used with bring-your-own-key credentials.
  </ResponseField>
</ResponseField>

<ResponseField name="userModelPolicies" type="object">
  Per-user model overrides. Map of user ID to policy object.

  **Properties**

  <ResponseField name="allowedModelIds" type="string[]">
    Model IDs this user is allowed to use.
  </ResponseField>
  <ResponseField name="blockedModelIds" type="string[]">
    Model IDs this user is blocked from using.
  </ResponseField>
</ResponseField>

### MCP

MCP access is allowlist-only. For server configuration, see [MCP](/harness/mcp).

<ResponseField name="mcpPolicy" type="object">
  Org-level MCP server access control.

  **Properties**

  <ResponseField name="enabled" type="boolean">
    Whether the MCP allowlist is enforced. Defaults to `false`.
  </ResponseField>
  <ResponseField name="allowlist" type="string[]">
    Allowed hostname or stdio command/argument matchers, not configured MCP server names. When enforcement is enabled, a server must match at least one entry; an empty or absent allowlist blocks all servers, even if a project or user configures them. There is no MCP blocklist.

    Remote `http` and `sse` servers are matched by hostname only. `*` matches zero or more characters, including dots: `example.com` includes the parent and its subdomains, while `*.example.com` includes only subdomains, at any depth. HTTP/HTTPS URL entries are reduced to their hostname; scheme, port, credentials, path, query string, and fragment do not restrict access. For example, `https://tools*.example.com/mcp` also allows `http://tools1.example.com:8080/other`.

    Stdio command and argument matching is unchanged and does not use hostname wildcard rules. See [MCP policy matching rules](/harness/mcp#enterprise-mcp-policy) and the [production and staging gateway example](/harness/mcp#example-allow-production-and-staging-gateways).
  </ResponseField>
</ResponseField>

<ResponseField name="mcpAutonomyOverrides" type="object">
  Per-server autonomy overrides. Map of MCP server name to an object with an optional `defaultLevel` (`low`, `medium`, `high`) and an optional `tools` map of tool name to level.
</ResponseField>

<ResponseField name="mcpAutonomyUrlOverrides" type="object[]">
  Autonomy overrides matched by URL. Each entry has a `urlPattern` and a `defaultLevel` (`low`, `medium`, `high`).
</ResponseField>

### Plugins and marketplaces

For setup and rollout, see [Internal Plugin Marketplaces](/enterprise/internal-plugin-marketplaces) and [Plugins](/harness/plugins).

<ResponseField name="enabledPlugins" type="object">
  Map of plugin name in `plugin@marketplace` format to `true` (enabled) or `false` (disabled). Plugins set to `true` are automatically installed on CLI startup once their marketplace is available. By default, entries merge additively across org, project, and user settings.
</ResponseField>

<ResponseField name="strictEnabledPlugins" type="boolean">
  When `true`, the `enabledPlugins` maps at this level and higher levels become the exhaustive plugin allowlist. Enables and manual installs at lower levels are rejected unless the plugin is listed with value `true`, and Factory's default plugins are disabled unless listed. Existing plugins remain installed and reactivate if the policy is removed. When omitted at every level, `enabledPlugins` remains additive. An explicit `false` at a higher level disables lower-level strict policies. Older CLI versions that do not recognize this setting do not enforce it.
</ResponseField>

<ResponseField name="extraKnownMarketplaces" type="object">
  Additional plugin marketplaces. Map of marketplace name to a source object. Marketplaces listed here are automatically cloned and registered on CLI startup.

  **Properties**

  <ResponseField name="source" type="object" required>
    Marketplace source, one of the following shapes, discriminated by the `source` field:

    **GitHub source**

    <ResponseField name="source" type="string" required>
      Must be `"github"`.
    </ResponseField>
    <ResponseField name="repo" type="string" required>
      GitHub repository in `owner/repo` format.
    </ResponseField>
    <ResponseField name="ref" type="string">
      Optional Git branch or tag to track (e.g. `"main"`, `"v1.2.0"`).
    </ResponseField>
    <ResponseField name="sha" type="string">
      Optional full 40-character commit SHA. When set, the marketplace is pinned to this exact commit.
    </ResponseField>

    **URL source**

    <ResponseField name="source" type="string" required>
      Must be `"url"`.
    </ResponseField>
    <ResponseField name="url" type="string" required>
      Git repository URL of the marketplace (e.g. `https://gitlab.com/company/plugins.git`).
    </ResponseField>
    <ResponseField name="ref" type="string">
      Optional Git branch or tag to track.
    </ResponseField>
    <ResponseField name="sha" type="string">
      Optional full 40-character commit SHA to pin to.
    </ResponseField>

    **Local source**

    <ResponseField name="source" type="string" required>
      Must be `"local"`.
    </ResponseField>
    <ResponseField name="path" type="string" required>
      Local filesystem path to the marketplace.
    </ResponseField>

    **Git subdirectory source**

    <ResponseField name="source" type="string" required>
      Must be `"git-subdir"`.
    </ResponseField>
    <ResponseField name="url" type="string" required>
      Git repository URL hosting the marketplace.
    </ResponseField>
    <ResponseField name="path" type="string" required>
      Subdirectory within the repository that contains the marketplace manifest.
    </ResponseField>
    <ResponseField name="ref" type="string">
      Optional Git branch or tag to track.
    </ResponseField>
    <ResponseField name="sha" type="string">
      Optional full 40-character commit SHA to pin to.
    </ResponseField>
  </ResponseField>
</ResponseField>

<ResponseField name="strictKnownMarketplaces" type="object[]">
  Marketplace sources that are strictly enforced. A higher-level list is the ceiling and is not broadened by lower levels. Organization-level `extraKnownMarketplaces` sources are included automatically and do not need to be repeated. Each entry is a source object directly (e.g. `{ "source": "github", "repo": "owner/repo" }`), not wrapped in a `source` key like `extraKnownMarketplaces` values.
</ResponseField>

### Network, sync, and retention

<ResponseField name="networkPolicy" type="object">
  IP restrictions for authenticated Factory web, CLI, and API requests.

  **Properties**

  <ResponseField name="allowedIps" type="string[]" required>
    IPv4 addresses or CIDR ranges that may access Factory web, CLI, and API key surfaces. Must contain at least one entry when set.
  </ResponseField>
</ResponseField>

<ResponseField name="cloudSessionSync" type="boolean">
  Whether sessions are synced to the cloud.
</ResponseField>

<ResponseField name="wikiCloudSync" type="boolean">
  Whether AutoWiki content can be stored in the Factory App and synced to GitHub wikis. Enabled by default; when disabled, all AutoWiki API endpoints are blocked and the CLI skips Cloud Sync and GitHub wiki sync. See [AutoWiki](/software-factory/wiki/overview#autowiki-cloud-sync).
</ResponseField>

<ResponseField name="sessionRetentionDays" type="integer">
  Number of days cloud sessions are retained. Between 14 and 365.
</ResponseField>

### Telemetry

<ResponseField name="telemetry" type="object">
  Pins your organization's OpenTelemetry sink for every member, so a developer's local `OTEL_*` variables cannot redirect the telemetry stream or switch it off. Read from the org level only: a `telemetry` block in project, folder, or user settings is ignored with a warning. Unlike most org fields, it is distributed to every member (the CLI reads it on each member's machine), so treat every value in it as readable by the whole organization. It does not configure Factory's own collector. See [Telemetry & Analytics](/enterprise/telemetry#org-managed-telemetry-settings).

  **Properties**

  <ResponseField name="enabled" type="boolean">
    Master switch for the customer telemetry pipeline. `true` keeps it on even where `OTEL_CUSTOMER_ENABLED=false`; `false` keeps it off even where `OTEL_CUSTOMER_ENABLED=true`. Omit it to fall back to `OTEL_CUSTOMER_ENABLED`. Airgapped deployments export to your collector only, and run the pipeline only where `endpoint` resolves to a usable collector.
  </ResponseField>
  <ResponseField name="endpoint" type="string">
    Base URL of your OTLP HTTP collector, taking precedence over `OTEL_TELEMETRY_ENDPOINT`. An `http`/`https` URL with no query string or fragment, or a `${VAR_NAME}` reference standing in for the whole URL.
  </ResponseField>
  <ResponseField name="headers" type="object">
    Static OTLP headers sent on every export, as a map of header name to value. Values may contain `${VAR_NAME}` references; prefer them over literal collector credentials. When `endpoint` is set, the `OTEL_*` header variables never apply, so omitting `headers` means the sink is called with none.
  </ResponseField>
  <ResponseField name="metricExportIntervalMs" type="integer">
    Metric flush interval in milliseconds, taking precedence over `OTEL_METRIC_EXPORT_INTERVAL`. Capped at `2147483647`.
  </ResponseField>
  <ResponseField name="logMessageContent" type="boolean">
    Whether message content (user and assistant message text, tool call inputs, and tool results) is exported. `true` exports it even where `OTEL_LOG_MESSAGE_CONTENT` is unset; `false` never exports it, overriding the variable. Omit it to let each machine decide with `OTEL_LOG_MESSAGE_CONTENT`. Content only ever reaches your own sink, never Factory's collector, and is never written when no sink is configured. `granularity: "aggregate"` overrides this field.
  </ResponseField>
  <ResponseField name="granularity" type="string">
    Whether exported data carries per-individual identity: `user` (default) or `aggregate`. Under `aggregate`, direct and indirect user identifiers are stripped from every datapoint on both sinks and message content is never written. Resolved most-restrictive-wins against `OTEL_TELEMETRY_GRANULARITY`, so either source asking for `aggregate` yields `aggregate`. See [Data granularity](/enterprise/telemetry/privacy#data-granularity).
  </ResponseField>
  <ResponseField name="format" type="string">
    Which convention your collector receives: `legacy` (default) or `genai`. The two replace each other rather than stack. The org value wins over `OTEL_TELEMETRY_FORMAT`, which applies only where this is unset; an unrecognized value resolves to `legacy`. Factory's own collector is unaffected. See [Export formats](/enterprise/telemetry/data-reference#export-formats).
  </ResponseField>
</ResponseField>

### Feature controls

<ResponseField name="voiceDictationEnabled" type="boolean">
  Whether voice dictation is available as an input method to members of the organization.
</ResponseField>

<ResponseField name="imageGenerationEnabled" type="boolean">
  Whether Droid may generate and edit images with image-generation models through the built-in `GenerateImage` tool. Defaults to `true`; set it to `false` to opt the organization out.

  Image-generation models are hidden from model pickers and are not governed by `modelPolicy` allow and block lists. Use this toggle with `imageGenerationAllowedModelIds` and `imageGenerationModel` to control them. When `false`, the CLI stops offering the `GenerateImage` tool and the LLM proxy rejects image-model requests with `403`. Prem deployments read the same value from org-managed settings, so the control applies to hosted and on-premises inference alike. In Enterprise Controls, this is **Session Features → Image Generation**.
</ResponseField>

<ResponseField name="imageGenerationAllowedModelIds" type="string[]">
  Allowlist of image-generation model IDs that the `GenerateImage` tool may use and the LLM proxy accepts. This is a hard control: the org value is authoritative, and lower levels cannot broaden it. When absent, every image model is allowed. An empty list allows none, which disables image generation like `imageGenerationEnabled: false`. Users can only select, and Droid can only fall back to, models on this list. In Enterprise Controls, this is **Allowed image models**. See [Image generation](/droid-cli/image-generation#choose-a-model) for the model IDs.
</ResponseField>

<ResponseField name="imageGenerationModel" type="string">
  Default image-generation model that the `GenerateImage` tool tries first. This is a session default: the org value seeds users' settings, and a user, project, or folder value overrides it. The value must be an allowed image model. When absent, Droid uses Factory's default order. In Enterprise Controls, this is **Default image model**.
</ResponseField>

### Personas

For configuration through Enterprise Controls, see [Personas](/enterprise/personas).

<ResponseField name="personaSettings" type="object">
  Controls which work personas members can choose during onboarding and what each persona recommends. Enterprise organizations only.

  **Properties**

  <ResponseField name="enabledRoles" type="string[]">
    Personas offered during onboarding: any of `engineering`, `product`, `design`, `finance`, `marketing`, `sales`, `operations`, `something-else`. Must contain at least one unique entry. Omit it to offer every persona.
  </ResponseField>
  <ResponseField name="personaConnectors" type="object">
    Connectors recommended to members of a persona during activation. Map of persona to an array of up to 20 unique connector slugs from Factory's connector catalog. Recommendations only surface for connectors currently enabled for the organization; personas without an entry fall back to generic suggestions.
  </ResponseField>
</ResponseField>

### Droid Computers

<ResponseField name="managedComputersEnabled" type="boolean">
  Whether Factory-managed Droid Computers are enabled for the organization.
</ResponseField>

<ResponseField name="managedComputersAllowedEmails" type="string[]">
  Emails permitted to use managed Droid Computers.
</ResponseField>

<ResponseField name="byomComputersEnabled" type="boolean">
  Whether bring-your-own-machine Droid Computers are enabled.
</ResponseField>

<ResponseField name="byomComputersAllowedEmails" type="string[]">
  Emails permitted to use bring-your-own-machine computers.
</ResponseField>

### Missions

<ResponseField name="missionPolicy" type="object">
  Org-level access control for missions.

  **Properties**

  <ResponseField name="restrictedAccess" type="boolean">
    Whether mission access is restricted. Defaults to `false`.
  </ResponseField>
  <ResponseField name="allowedUserIds" type="string[]">
    User IDs allowed to use missions when access is restricted.
  </ResponseField>
</ResponseField>

### Org administration

<ResponseField name="includeCoAuthoredByDroid" type="boolean">
  Include a `Co-authored-by: Droid` trailer in git commits.
</ResponseField>

<ResponseField name="ideAutoConnect" type="boolean">
  Automatically connect to the IDE on session start.
</ResponseField>

<ResponseField name="disableAutoUpdate" type="boolean">
  Disable automatic Droid CLI updates and in-app desktop updates (useful for managed or pinned deployments).
</ResponseField>

<ResponseField name="restrictMemberVisibility" type="boolean">
  Restrict whether members can see other members of the organization.
</ResponseField>

<ResponseField name="restrictApiKeyCreationToManagers" type="boolean">
  When `true`, only Owners and Managers can create API keys.
</ResponseField>

<ResponseField name="advancedAnalyticsEnabled" type="boolean">
  Whether advanced analytics are enabled for the organization.
</ResponseField>

<ResponseField name="disableWeeklyUsageSummary" type="boolean">
  Controls the weekly email to organization Managers and Owners that lists users and active service accounts at or above 80% of their monthly credit cap. The summary is sent when this setting is omitted or `false`; set it to `true` to stop the summary.
</ResponseField>

<ResponseField name="factoryRouterGuidance" type="string">
  Free-text guidance for the Factory Router.
</ResponseField>

<ResponseField name="factoryRouterRules" type="object[]">
  Routing rules for the Factory Router. Each rule has an optional `when` condition and a `guidance` string.
</ResponseField>

<ResponseField name="env" type="object">
  Environment variables injected into every Droid session at startup, as a map of name to string value. Only the organization-managed block is honored: a block at any other level, including a `--settings` runtime overlay, is ignored without a warning, so neither an untrusted repository nor a command-line flag can inject variables. Values may reference existing variables with `${VAR_NAME}` syntax, resolved against the environment as it was before the block was applied rather than against other keys in the same block; a reference to a variable the machine does not set resolves to an empty string. A variable already set in the shell is never overwritten, so a developer can always override an injected default.
</ResponseField>

### Supported model IDs

The identifiers used in `allowedModelIds`, `blockedModelIds`, and `requireExplicitOptInModelIds` are the model IDs listed on [Models](/models), which is the single source of truth for available models across providers. Unrecognized IDs are silently dropped rather than failing validation, so policies stay valid across model updates.

<LabeledDivider label='In practice' />

## Level responsibilities

The same schema applies at every level; the typical division of labor is:

{/* sweep-allow: term-bullets */}

- **Org** publishes the authoritative policy: allowed models and gateways, BYOK rules, global command allow/deny/block lists, org-standard droids, commands, hooks, and plugin marketplaces, plus retention, cloud sync, network policy, and feature gates. This bundle is distributed to every environment Droid runs in.
- **Project and folder** `.factory/` directories (checked into version control) specialize org policy for a codebase: project-specific droids and gateways within the allowed set, hooks that know the repo's tests and deploys, and tighter controls for high-risk repositories. Folder-level directories handle monorepos where subsystems differ.
- **User** `~/.factory/` holds personal session defaults where the active policy allows it: a preferred model from the allowed set, preferred reasoning effort, interaction mode, and autonomy level. See [App settings](/factory-app/settings) for the personal preferences surface.

Because hard controls remain authoritative, users cannot re-enable disallowed models or tools, loosen command lists, or raise autonomy above an enforced ceiling.

---

## Example: enforcing a model policy

Suppose your org wants to allow only approved enterprise models, disallow user-supplied API keys, and force all prompts through a particular LLM gateway. You would:

1. Define the allowed models and gateway endpoints in `modelPolicy` and `customModels`.
2. Set `modelPolicy.allowCustomModels` to `false` to disable user BYOK entirely.
3. Use `modelPolicy.allowedBaseUrls` to require approved gateway endpoints for custom models.

Projects and users can still choose **which of the approved models** to use, but cannot break these guarantees.

## Example: environment-specific autonomy

Consider an org that wants high autonomy in CI and sandboxed containers but limited autonomy on developer laptops. You could:

1. At org level, set `maxAutonomyLevel` to `high`.
2. In project settings, define environment-aware hooks that inspect environment tags (for example `environment.type=local|ci|sandbox`) and downgrade or block autonomy above `medium` on laptops.
3. Optionally, define stricter folder-level policies for particularly sensitive repos.

Users can only choose safer personal defaults within the allowed space.

## Example: disable built-in web search

For a connected on-premises org that cannot allow Factory-hosted web search, add this to org-managed settings without enabling full airgap mode:

```json
{
  "webSearchDisabled": true
}
```

This disables the CLI's built-in Web Search tool; it does not block all network egress or all provider access.

## Example: full org-managed settings

The `mcpPolicy` below allows `docs.example.com` and its subdomains, plus subdomains of `mcp-gateway.example.com`. For example, `https://docs.example.com/mcp` and `https://ticketing.mcp-gateway.example.com/mcp` pass the policy; `https://mcp-gateway.example.com/mcp` does not, because `*.` excludes the parent hostname. These are hostname matchers, not the names used in `mcp.json`.

The command rules use exact argument prefixes, not wildcards or operation-wide
prohibitions. Review [matching limits](/autonomy-and-safety/permission-rules#match-command-arguments)
and test the command forms your team uses.

```json
{
  "sessionDefaultSettings": {
    "model": "<model-id-from-/models>",
    "reasoningEffort": "high",
    "interactionMode": "auto",
    "autonomyLevel": "medium",
    "specModeModel": "<model-id-from-/models>",
    "specModeReasoningEffort": "medium"
  },
  "missionOrchestratorModel": "<model-id-from-/models>",
  "missionOrchestratorReasoningEffort": "high",
  "missionModelSettings": {
    "workerModel": "<model-id-from-/models>",
    "workerReasoningEffort": "medium",
    "validationWorkerModel": "<model-id-from-/models>",
    "validationWorkerReasoningEffort": "high"
  },
  "maxAutonomyLevel": "high",
  "builtInToolAutonomyOverrides": {
    "WebSearch": "high",
    "FetchUrl": "medium"
  },
  "subagentAutonomyLevel": "medium",
  "cloudSessionSync": true,
  "voiceDictationEnabled": true,
  "imageGenerationEnabled": true,
  "includeCoAuthoredByDroid": true,
  "enableDroidShield": true,
  "ideAutoConnect": true,
  "permissionRules": {
    "version": 1,
    "rules": [
      {
        "id": "org/git-push",
        "decision": "ask",
        "match": { "prefix": ["git", "push"] },
        "tests": {
          "match": ["git push origin main"],
          "noMatch": ["git status"]
        }
      },
      {
        "id": "org/npm-publish",
        "decision": "block",
        "match": { "prefix": ["npm", "publish"] },
        "tests": {
          "match": ["npm publish"],
          "noMatch": ["npm pack"]
        }
      }
    ]
  },
  "allowManagedHooksOnly": true,
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Execute",
        "hooks": [
          {
            "type": "command",
            "command": "\"$FACTORY_PROJECT_DIR\"/.factory/hooks/audit-command.sh",
            "timeout": 30
          }
        ]
      }
    ]
  },
  "customModels": [
    {
      "model": "my-internal-model",
      "id": "custom:my-internal-model-v1",
      "index": 0,
      "baseUrl": "https://llm-gateway.internal.example.com/v1",
      "apiKey": "${INTERNAL_MODEL_API_KEY}",
      "provider": "generic-chat-completion-api",
      "displayName": "Internal Model v1",
      "maxContextLimit": 128000,
      "enableThinking": true,
      "thinkingMaxTokens": 8192,
      "maxOutputTokens": 16384,
      "extraHeaders": {
        "X-Team": "platform"
      },
      "extraArgs": {},
      "noImageSupport": false
    }
  ],
  "modelPolicy": {
    "allowedModelIds": ["<model-id-from-/models>"],
    "blockedModelIds": [],
    "allowCustomModels": false,
    "allowedBaseUrls": ["https://llm-gateway.internal.example.com/v1"],
    "allowAllFactoryModels": false
  },
  "mcpPolicy": {
    "enabled": true,
    "allowlist": ["https://docs.example.com", "*.mcp-gateway.example.com"]
  },
  "networkPolicy": {
    "allowedIps": ["10.0.0.0/8", "192.0.2.0/24"]
  },
  "sandbox": {
    "enabled": true,
    "mode": "per-command",
    "filesystem": {
      "denyRead": ["~/.ssh", "~/.aws"],
      "denyWrite": ["/etc"]
    },
    "network": {
      "allowedDomains": ["api.github.com", "registry.npmjs.org"]
    }
  },
  "restrictApiKeyCreationToManagers": true,
  "sessionRetentionDays": 90,
  "advancedAnalyticsEnabled": true,
  "disableWeeklyUsageSummary": false,
  "userModelPolicies": {
    "user-abc-123": {
      "allowedModelIds": ["<model-id-from-/models>"],
      "blockedModelIds": []
    }
  },
  "enabledPlugins": {
    "my-org-plugin@internal-marketplace": true,
    "experimental-plugin@internal-marketplace": false
  },
  "strictEnabledPlugins": true,
  "extraKnownMarketplaces": {
    "internal-marketplace": {
      "source": {
        "source": "github",
        "repo": "my-org/factory-plugins"
      }
    }
  },
  "strictKnownMarketplaces": []
}
```

<LabeledDivider label='Going further' />

## Models, gateways, MCP, and tooling

The hierarchy governs which models and external systems Droid can reach. The org-level controls are the schema fields above; each area also has a dedicated page:

{/* sweep-allow: term-bullets */}

- **Models and BYOK**: `modelPolicy` (`allowedModelIds`, `blockedModelIds`, `allowCustomModels`, `allowedBaseUrls`) and `customModels`. See [Models](/models) for IDs and [Custom Models (BYOK)](/model-independence/byok) for gateway and provider setup, including cloud platforms (Bedrock, Vertex, Azure OpenAI) and self-hosted endpoints.
- **MCP servers**: `mcpPolicy` (allowlist-only), plus `mcpAutonomyOverrides` and `mcpAutonomyUrlOverrides` for per-server or per-URL autonomy. See [MCP](/harness/mcp).
- **Droids, commands, and hooks**: published in the org `.factory` bundle; set `allowManagedHooksOnly` to `true` to ignore project- and user-defined hooks. See [Hooks](/harness/hooks) and [Agent Safety & Controls](/enterprise/llm-safety-and-agent-controls).

---

## Putting it all together

Enterprise Controls underpin everything described in the other enterprise pages:

- [Identity & Access](/enterprise/identity-and-access): who can change which level of settings.
- [Data Flows & Privacy](/enterprise/privacy-and-data-flows): where data and telemetry are allowed to go.
- [Deployment Patterns](/enterprise/network-and-deployment): which environments Droid can run in and how it connects.
- [Agent Safety & Controls](/enterprise/llm-safety-and-agent-controls): policies for commands, tools, and Droid Shield.
- [Compliance & Audit](/enterprise/compliance-audit-and-monitoring): guarantees and telemetry used to prove compliance.

By expressing policy once at the right level, you can run Droid across cloud, hybrid, and airgapped environments **without per-machine drift or one-off configuration**.

<RelatedLinks>
  <RelatedLink href="/enterprise/llm-safety-and-agent-controls" title="Agent Safety & Controls">
    See how command lists, sandbox, and hooks work in practice.
  </RelatedLink>
  <RelatedLink href="/enterprise/telemetry" title="Telemetry & Analytics">
    Measure adoption and cost with OTEL export and the Analytics API.
  </RelatedLink>
</RelatedLinks>
