> ## Documentation Index
> Fetch the complete documentation index at: https://docs.devin.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Air-Gapped Devin Models File

> Schema reference and annotated example for the models.json file that points air-gapped Devin at your OpenAI-compatible model endpoints, plus MDL codes.

> [!NOTE]
>
> This documentation is for the federal deployments of Devin. [Back to Devin Docs](/get-started/devin-intro)

Air-gapped Devin takes its model configuration from a single JSON file that you author. The file is the source of model endpoints, and inference goes to the OpenAI-compatible endpoints it lists.

## Location

| Precedence | Source |
| - | - |
| 1 | `$DEVIN_MODELS_FILE` (absolute path) |
| 2 | `/etc/devin/models.json` (Windows: `C:\ProgramData\Devin\models.json`) |

The CLI, `devin models validate`, and the Desktop **Models** settings section all resolve the file the same way. The system path is typically root-owned; use `DEVIN_MODELS_FILE` for per-user overrides. On Windows, [lock down `C:\ProgramData\Devin\`](/federal/air-gapped/install#windows-lock-down-the-configuration-folder) before placing the file there.

## Complete example

```json theme={null}
{
  "schema_version": 0,
  "models": [
    {
      "name": "llama-3.3-70b",
      "display_name": "Llama 3.3 70B (vLLM)",
      "base_url": "https://models.internal:8000/v1",
      "api_key": { "env": "VLLM_API_KEY" },
      "api": "chat_completions",
      "capabilities": ["parallel_tool_calls"],
      "max_context_tokens": 131072,
      "max_output_tokens": 8192,
      "default": true
    },
    {
      "name": "qwen3-vl-32b",
      "display_name": "Qwen3 VL 32B (vision)",
      "base_url": "https://gateway.internal/openai/v1",
      "api_key": { "env": "GATEWAY_API_KEY" },
      "api": "chat_completions",
      "capabilities": ["vision", "reasoning", "parallel_tool_calls"],
      "max_context_tokens": 65536,
      "max_output_tokens": 16384
    },
    {
      "name": "gpt-oss-120b",
      "display_name": "gpt-oss 120B (Responses API)",
      "base_url": "https://responses.internal:8080/v1",
      "api_key": { "env": "RESPONSES_API_KEY" },
      "api": "responses",
      "capabilities": ["reasoning"],
      "max_context_tokens": 131072
    },
    {
      "name": "llama3.1:8b",
      "display_name": "Llama 3.1 8B (Ollama)",
      "base_url": "http://localhost:11434/v1",
      "api_key": { "env": "OLLAMA_API_KEY" },
      "max_context_tokens": 32768
    }
  ]
}
```

The example shows every field, both API surfaces (`chat_completions` and `responses`), and a loopback `http://` endpoint. Copy it to `/etc/devin/models.json` (`C:\ProgramData\Devin\models.json` on Windows) and edit.

## Schema reference (version 0)

Top level:

| Field | Required | Meaning |
| - | - | - |
| `schema_version` | yes | Must be `0`. |
| `models` | yes | Non-empty array of model entries. |

Each entry in `models`:

| Field | Required | Meaning |
| - | - | - |
| `name` | yes | Unique model slug. Users select it with `--model` on the CLI or `/model` in a session. |
| `display_name` | no | Human-readable label shown in menus. Defaults to `name`. |
| `base_url` | yes | Base URL of the OpenAI-compatible endpoint. Must be `https://`; plain `http://` is accepted only for loopback hosts (`localhost`, `127.0.0.0/8`, `[::1]`), for example a vLLM or Ollama server on the same machine. |
| `api_key.env` | yes | **Name** of the environment variable that holds the API key. The key value never appears in the file, in diagnostics, or in the Desktop settings UI. |
| `api` | no | `chat_completions` (default) or `responses`. |
| `capabilities` | no | Any of `vision`, `reasoning`, `parallel_tool_calls`. |
| `max_context_tokens` | yes | Context window in tokens; must be nonzero. |
| `max_output_tokens` | no | Output cap in tokens; must be nonzero and must not exceed `max_context_tokens`. |
| `default` | no | Marks the default model. At most one entry may set it. |

The schema is strict: **unknown fields are rejected**, so a typo fails validation instead of being silently ignored. There is no top-level endpoint block and no inline API key.

## API key environment variables

`api_key.env` is an indirection. The agent reads the key from its **own process environment** at startup. The variable must be **set** for every listed model, even for endpoints that ignore the key (such as Ollama or an unauthenticated vLLM); it may be empty. Otherwise the agent fails to start with `model '<name>': API key environment variable '<VAR>' is not set`.

* **CLI:** export the variable in the shell, or in the systemd/launchd unit, that runs `devin`.
* **Desktop:** the bundled agent inherits the **Desktop app's** environment, not your terminal's. A variable exported in `~/.zshrc` is normally not visible to an app launched from the Dock, Finder, or Spotlight. Provide it in one of these ways:
  * **Recommended:** the `devin.acp.agentEnv` setting (**Settings → Devin Local → Environment Variables**, or in `settings.json`):

    ```json theme={null}
    "devin.acp.agentEnv": {
      "devin-cli": { "VLLM_API_KEY": "...", "OLLAMA_API_KEY": "ollama" }
    }
    ```

    Values are merged into the agent's environment when it is spawned. Restart the agent or the app after changing them. Use the same setting for `DEVIN_MODELS_FILE` if the file is not at the default path.
  * **Launch environment:** on macOS, `open` does not pass your shell's environment to the app. Either run `launchctl setenv VAR value` to make a variable visible to Dock and Finder launches until the next login, or start the app from a shell that has the variables exported by running the executable under `/Applications/Devin - Airgap.app/Contents/MacOS/` directly. On Linux set the variables in the `.desktop` launcher or the systemd user environment.

Confirm from the same environment the agent runs in:

```sh theme={null}
devin airgap doctor
```

```text theme={null}
…
pass  model 'llama-3.3-70b': API key env var  VLLM_API_KEY is set
FAIL  model 'qwen3-vl-32b': API key env var   CFG002: environment variable 'GATEWAY_API_KEY' (referenced by `api_key.env`) is not set
…
```

## Validate the file

```sh theme={null}
devin models validate
```

```text theme={null}
/etc/devin/models.json: OK
```

Options:

| Option | Meaning |
| - | - |
| `--file <path>` | Validate a file other than the resolved default. |
| `--format text` (default) or `--format json` | Output format. |

On problems, every diagnostic is printed with its code and the model it applies to, and the command exits nonzero:

```sh theme={null}
devin models validate --file ./models.json
```

```text theme={null}
./models.json: 2 problem(s) found
  MDL005 [model 'llama-3.3-70b']: duplicate model name 'llama-3.3-70b'
  MDL010 [model 'qwen3-vl-32b']: model 'qwen3-vl-32b': `base_url` must be an https:// URL (plain http:// is only allowed for loopback addresses: localhost, 127.0.0.0/8, or [::1])
```

The JSON form returns `{"path": …, "valid": false, "diagnostics": [...]}` for automation. `devin models validate` runs without a valid license, so you can prepare the file before the license is installed.

## Apply changes

Model backends initialize at process start. After editing the file, restart the CLI, or restart the bundled agent in Desktop, to load the new configuration.

If the file is missing or invalid, the standalone CLI refuses to start and prints the error. Desktop's bundled agent starts **degraded** with no models: sessions cannot run inference, and the **Models** settings section shows the `MDL` diagnostics so you can fix the file in place. Desktop validates before saving, never writes an invalid file, writes atomically, and refuses (`MDL014`) to replace a file owned by another user, so a root-owned `/etc/devin/models.json` can only be changed by an administrator on disk. On Windows, when the models file is owned by an administrator, a standard user cannot save model settings from Desktop (the save is refused with `MDL014`); an administrator can.

## Error codes

| Code | Meaning | What to do |
| - | - | - |
| `MDL001` | File is not valid JSON for schema v0, including unknown fields | Fix the JSON; remove fields not in the schema table above |
| `MDL002` | Unsupported `schema_version` | Set `"schema_version": 0` |
| `MDL003` | `models` is empty | Add at least one model entry |
| `MDL004` | A model has an empty `name` | Give every entry a unique `name` |
| `MDL005` | Duplicate model `name` | Rename one of the entries |
| `MDL006` | Empty `base_url` | Set the endpoint URL |
| `MDL007` | Empty `api_key.env` | Name the environment variable that holds the key |
| `MDL008` | `max_context_tokens` is zero | Set the model's context window |
| `MDL009` | More than one model marked `default` | Leave `default: true` on exactly one entry |
| `MDL010` | `base_url` is not an `https://` URL with a host (`http://` only for loopback) | Use `https://`, or a loopback address for a same-host server |
| `MDL011` | `max_output_tokens` exceeds `max_context_tokens` | Lower `max_output_tokens` |
| `MDL012` | File exists but could not be read (permissions or invalid UTF-8) | Fix permissions for the user running Devin; save the file as UTF-8 |
| `MDL013` | `max_output_tokens` is zero | Remove the field or set a positive value |
| `MDL014` | File is owned by another user, so Desktop refuses to replace it | Edit the file on disk as its owner, or point `DEVIN_MODELS_FILE` at a file you own |

Endpoint reachability and inference problems found by `devin airgap doctor` are reported as `NET` and `CFG` codes; see [Error codes](/federal/air-gapped/error-codes).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.