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

# Air-Gapped Devin On-Disk Formats

> Stable file locations for the air-gapped Devin Desktop and CLI on macOS, Linux, and Windows, plus the audit-log JSONL schema and examples for SIEM ingestion.

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

The air-gapped CLI and Desktop keep all of their state in a small set of files with documented, stable formats. This page lists where each file lives, which ones you author, and the exact schema of the audit log so you can forward it to your SIEM.

***

## File locations

### Files you author

These are the machine-wide inputs. Each has a default path and an environment variable that overrides it; the variable must be set in the environment the agent starts from (see [API key environment variables](/federal/air-gapped/models-file#api-key-environment-variables) for how to do that for Desktop).

| File | Default path | Override | Format | Documented on |
| - | - | - | - | - |
| License | `/etc/devin/license.devin-onprem-license` | `DEVIN_LICENSE_FILE` | Signed token, single line | [License](/federal/air-gapped/license) |
| Models file | `/etc/devin/models.json` | `DEVIN_MODELS_FILE` | JSON, `"version": 0` | [Models file](/federal/air-gapped/models-file) |
| Auth file (optional, enables SSO) | `/etc/devin/auth.json` | `DEVIN_AUTH_FILE` | JSON, `"version": 0` | [SSO](/federal/air-gapped/sso) |
| CA bundle (optional) | none | `DEVIN_CA_BUNDLE` | PEM | [Network and certificates](/federal/air-gapped/network-and-certificates) |
| Machine-wide policy (optional) | Linux `/etc/devin/system.json`, macOS `/Library/Application Support/Devin/system.json` | none | JSON | [CLI system configuration](/cli/enterprise/system-config) |

The defaults are the same on macOS and Linux. On Windows the license, models file, and auth file default to `C:\ProgramData\Devin\` (`license.devin-onprem-license`, `models.json`, `auth.json`), the machine-wide policy file is `C:\ProgramData\Devin\system.json`, and the override variables are the same; [lock the folder down](/federal/air-gapped/install#windows-lock-down-the-configuration-folder) before placing files in it. The CLI reads these files and never writes to them, so you can manage them with your configuration-management tooling and make them read-only.

### Per-user state written by the CLI

The CLI follows the XDG base-directory layout on **both** macOS and Linux (it does not use `~/Library` for its own state). `XDG_CONFIG_HOME` and `XDG_DATA_HOME` are honored when set.

| Path | Contents |
| - | - |
| `~/.config/devin/config.json` | User configuration shared with the standard Devin CLI (permissions, `proxy`, and so on). Never contains credentials. See the [CLI config file reference](/cli/reference/configuration/config-file). |
| `~/.config/devin/mcp_config.json` | Locally configured MCP servers. |
| `~/.local/share/devin/sso_tokens.toml` | SSO token cache written by `devin auth login`, owner-only permissions (`0600`). Deleted by `devin auth logout`. |
| `~/.local/share/devin/cli-federal/sessions.db` | Session history (SQLite). Contains conversation content; treat it as sensitive. |
| `~/.local/share/devin/cli-federal/audit/` | Audit log directory: `audit.jsonl` plus rotated `audit-<timestamp>.jsonl` files and the `.audit.lock` advisory lock. Schema below. |

`cli-federal` is the product subdirectory used by the air-gapped build; it keeps this state separate from any standard Devin CLI install on the same machine, which uses `cli`.

On Windows the CLI does not read `XDG_*` variables. It keeps the same files under `%APPDATA%\devin\`, and its cache under `%LOCALAPPDATA%\devin\`:

| Path (Windows) | Contents |
| - | - |
| `%APPDATA%\devin\config.json` | User configuration, as above. |
| `%APPDATA%\devin\mcp_config.json` | Locally configured MCP servers. |
| `%APPDATA%\devin\sso_tokens.toml` | SSO token cache. |
| `%APPDATA%\devin\cli-federal\sessions.db` | Session history (SQLite). |
| `%APPDATA%\devin\cli-federal\audit\` | Audit log directory. |

### Desktop-specific state

The Desktop application is isolated from any other Devin Desktop or Windsurf install on the same machine:

| Path | Contents |
| - | - |
| `~/.devin-airgap-config/` | An isolated `XDG_CONFIG_HOME` that Desktop passes to the bundled agent. Inside it, `devin/config.json` is a symlink to the real `~/.config/devin/config.json`, so the CLI and the bundled agent share one user config. Nothing else from `~/.config/devin/` (notably `mcp_config.json`) is visible to the agent inside Desktop. |
| `~/.devin-airgap/` | Desktop extensions directory. |
| `~/.codeium/windsurf-airgap/` | Language-server state. |
| Linux `~/.config/Devin - Airgap/`, macOS `~/Library/Application Support/Devin - Airgap/` | Desktop user data: `User/settings.json` (including `devin.acp.agentEnv`), window state. |

The symlink is created at Desktop startup only if nothing exists at `~/.devin-airgap-config/devin/config.json`. If a regular file is already there, Desktop leaves it alone and the bundled agent uses that file instead of the shared one; the **Devin ACP** output channel logs a `WARNING: … is a regular file, not a symlink` line. Delete the file and restart Desktop to restore the link.

On Windows there is no symlink. Desktop's bundled agent and the CLI share `%APPDATA%\devin\config.json` directly, and Desktop does not load the user's `%APPDATA%\devin\mcp_config.json`. Desktop user data is in `%APPDATA%\Devin - Airgap\`.

Only `~/.devin-airgap-config/` and `~/.devin-airgap/` are safe to delete for a clean-slate reinstall. `~/.config/devin/` and `~/.local/share/devin/` also belong to the standalone CLI.

## Audit log

The air-gapped CLI writes a local, append-only, structured audit log. It is always on (there is no toggle), does not exist in any other Devin build, and is written to the workstation for your own review and SIEM ingestion.

### Location and rotation

| Property | Value |
| - | - |
| Active file | `~/.local/share/devin/cli-federal/audit/audit.jsonl` (Windows: `%APPDATA%\devin\cli-federal\audit\audit.jsonl`) |
| Format | JSON Lines: one self-contained JSON object per line, UTF-8 |
| Permissions | Directory and files are created owner-only (`0600`) |
| Rotation | When `audit.jsonl` reaches 10 MiB it is renamed to `audit-<UTC timestamp>.jsonl` (for example `audit-20260127T234500123Z.jsonl`) and a fresh file is started |
| Retention | At most 10 rotated files are kept; the oldest are deleted first |
| Concurrency | Rotation is guarded by the `.audit.lock` advisory lock, so concurrent CLI processes never double-rotate |

Ship rotated files to your SIEM before they age out if you need unbounded retention. Because the CLI only ever appends and never rewrites existing lines, a standard file-tailing collector is sufficient; see [Forward the audit log to your SIEM](#forward-the-audit-log-to-your-siem).

### Redaction guarantees

Events carry paths and metadata only. The log never contains prompts, model responses, code, file contents, secret values, API keys, or full shell command lines: for shell tools only the program name (the first token) is recorded.

### Envelope (schema version 1)

Every line carries this envelope; per-event fields are flattened alongside it.

| Field | Type | Description |
| - | - | - |
| `schema_version` | number | Currently `1`. Bumped only on breaking changes. |
| `ts` | string | RFC 3339 UTC timestamp with milliseconds. |
| `pid` | number | Process ID of the emitting CLI process. |
| `event` | string | Event type; determines the remaining fields. |

Configure your parser to ignore unknown fields and unknown `event` values: new fields and event types may be added without a `schema_version` bump.

### Event types

#### `session_start`

Emitted when an agent session begins (new or resumed root session).

| Field | Type | Description |
| - | - | - |
| `session_id` | string | CLI session ID. Use it to join every other event of the session. |
| `source` | string | How the session started (for example `startup`). |
| `cli_version` | string | Released CLI version, as printed by `devin --version`. |
| `actor` | object | Identity attribution; see [`actor` object](#actor-object). |

```json theme={null}
{"schema_version":1,"ts":"2026-01-27T23:45:00.123Z","pid":4242,"event":"session_start","session_id":"a1b2","source":"startup","cli_version":"1.2.3","actor":{"os_user":"jsmith","git_user_name":"J. Smith","git_user_email":"jsmith@example.com","sso_issuer":"https://sso.example.com/realms/corp","sso_subject":"6f1c…","sso_user":"jsmith","sso_groups":["/devin-users"]}}
```

#### `session_end`

Emitted when the session ends or the agent shuts down.

| Field | Type | Description |
| - | - | - |
| `session_id` | string | CLI session ID. |
| `reason` | string | `prompt_input_exit`, `clear`, `resume`, `logout`, or `other`. |

```json theme={null}
{"schema_version":1,"ts":"2026-01-27T23:59:59.000Z","pid":4242,"event":"session_end","session_id":"a1b2","reason":"prompt_input_exit"}
```

Every `session_start` is paired with exactly one `session_end` for the same `session_id` on clean exit. The CLI runs the agent in a child process that it terminates on teardown, so the parent process records the child session's `session_end` (reason `other`) when the session finishes. A session killed by `SIGKILL` or power loss cannot write a final event; treat a `session_start` with no matching `session_end` as an abnormal termination.

#### `tool_call`

Emitted after each tool the agent executes.

| Field | Type | Description |
| - | - | - |
| `session_id` | string | CLI session ID. |
| `tool_name` | string | Tool name (for example `read`, `write`, `exec`). |
| `tool_id` | string | Unique ID of this tool invocation. |
| `success` | boolean | Whether the tool reported success. |
| `program` | string, optional | For shell tools: the program name only, never arguments. |
| `paths` | string\[], optional | Filesystem paths the tool was scoped to. Omitted when empty. |

```json theme={null}
{"schema_version":1,"ts":"2026-01-27T23:45:07.001Z","pid":4242,"event":"tool_call","session_id":"a1b2","tool_name":"exec","tool_id":"t_01","success":true,"program":"cargo"}
```

#### `file_read` and `file_write`

Emitted in addition to `tool_call`, once per path in the tool's resolved permission scope. They describe the access the tool was *scoped to*, not verified I/O: a tool approved with read scope over several paths emits a `file_read` per path even if it touched only some of them, and the events are emitted whether or not the tool call succeeded.

| Field | Type | Description |
| - | - | - |
| `session_id` | string | CLI session ID. |
| `path` | string | Filesystem path. |

```json theme={null}
{"schema_version":1,"ts":"2026-01-27T23:45:09.410Z","pid":4242,"event":"file_write","session_id":"a1b2","path":"/home/jsmith/repo/src/main.rs"}
```

#### `model_call`

Emitted after each inference request to a model endpoint from your models file.

| Field | Type | Description |
| - | - | - |
| `session_id` | string | CLI session ID. |
| `model` | string | Model name from the models file. |
| `endpoint` | string, optional | Base URL of the configured endpoint. |
| `input_tokens` | number, optional | Total input tokens, including cache reads. |
| `output_tokens` | number, optional | Output tokens. |
| `cache_read_tokens` | number, optional | Cache-read tokens, when the endpoint reports them. |
| `cache_creation_tokens` | number, optional | Cache-write tokens, when reported. |

Token fields are omitted when the endpoint does not report usage.

```json theme={null}
{"schema_version":1,"ts":"2026-01-27T23:45:12.900Z","pid":4242,"event":"model_call","session_id":"a1b2","model":"llama-3-70b","endpoint":"https://models.internal:8000/v1","input_tokens":5120,"output_tokens":384}
```

#### `auth`

| Field | Type | Description |
| - | - | - |
| `action` | string | One of the actions below. |
| `actor` | object | Identity attribution; see [`actor` object](#actor-object). |
| `error_code` | string, optional | On `sso_login_denied` and `sso_revalidation_failed`: the `IDPnnn` code the user was shown. Omitted on successful actions. |

| `action` | Description |
| - | - |
| `identity_resolved` | Identity attribution at session start and after each `devin auth` command (so `status` names the login it validated and `logout` names none). |
| `license_validated` | The license file validated successfully at startup. |
| `license_installed` | A license file was written through Desktop's Settings page. Carries no path or file contents. |
| `sso_login` | SSO login completed (`devin auth login` or the Desktop sign-in). |
| `sso_login_denied` | SSO login refused after the IdP answered: the required group is missing (`IDP004`), or the returned ID token failed validation or was absent (`IDP005`). `actor` carries the identity the IdP vouched for when the token validated; nothing is cached. |
| `sso_refresh` | SSO tokens refreshed against the IdP. |
| `sso_logout` | SSO logout (`devin auth logout` or the Desktop sign-out). |
| `sso_revalidation_failed` | A running agent lost its SSO login and is shutting down; see [Revalidation in running sessions](/federal/air-gapped/sso#revalidation-in-running-sessions). |

```json theme={null}
{"schema_version":1,"ts":"2026-01-27T23:44:58.000Z","pid":4241,"event":"auth","action":"sso_login","actor":{"os_user":"jsmith","git_user_name":"J. Smith","git_user_email":"jsmith@example.com","sso_issuer":"https://sso.example.com/realms/corp","sso_subject":"6f1c…","sso_user":"jsmith","sso_groups":["/devin-users"]}}
{"schema_version":1,"ts":"2026-01-28T08:10:02.512Z","pid":5150,"event":"auth","action":"sso_login_denied","error_code":"IDP004","actor":{"os_user":"rjones","sso_issuer":"https://sso.example.com/realms/corp","sso_subject":"a90d…","sso_user":"rjones","sso_groups":["/contractors"]}}
{"schema_version":1,"ts":"2026-01-28T09:00:00.000Z","pid":4242,"event":"auth","action":"sso_revalidation_failed","error_code":"IDP003","actor":{"os_user":"jsmith","sso_issuer":"https://sso.example.com/realms/corp","sso_subject":"6f1c…","sso_user":"jsmith","sso_groups":["/devin-users"]}}
```

### `actor` object

Identity attribution is the OS user, the local git identity, and (when SSO is configured) the identity from the validated ID token. Fields are omitted when unavailable.

| Field | Type | Description |
| - | - | - |
| `os_user` | string, optional | `$USER`. |
| `git_user_name` | string, optional | `git config user.name`. |
| `git_user_email` | string, optional | `git config user.email`. |
| `sso_issuer` | string, optional | The IdP issuer URL the token was validated against. |
| `sso_subject` | string, optional | The ID token's `sub` claim. |
| `sso_user` | string, optional | The identity `devin auth status` prints (`preferred_username`, `email`, or `sub`). |
| `sso_groups` | string\[], optional | The token's group claims (`groups` by default, or the configured `groups_claim`). |

The SSO fields are present on every `auth` event that has a validated token at hand (`sso_login`, `sso_refresh`, `sso_logout`, `sso_login_denied`), on `sso_revalidation_failed` for the login the process validated (or, when it validated none, the login still in the token cache), and on `session_start` and `identity_resolved` for the login the running process validated at startup. `tool_call`, `file_read`, `file_write`, and `model_call` carry no `actor`; attribute them through `session_id` to their `session_start`.

> [!NOTE]
>
> `sso_groups` records each user's IdP group memberships on the local machine. The audit directory and files are owner-only; include them in your data-handling review if group names are sensitive in your environment.

### Forward the audit log to your SIEM

Devin does not send audit records anywhere. To aggregate usage across workstations, point the log collection agent your SIEM already uses (for example a Splunk universal forwarder or Elastic Agent) at the audit directory on each workstation. No Devin setting is involved.

Configure the agent's file input with these settings:

| Setting | Value |
| - | - |
| Files to collect | `audit*.jsonl` in the audit directory. This matches the active file and the rotated files. Do not collect `.audit.lock`. |
| Path for all users on a workstation | Linux: `/home/*/.local/share/devin/cli-federal/audit/audit*.jsonl`. macOS: `/Users/*/.local/share/devin/cli-federal/audit/audit*.jsonl`. These paths cover home directories under `/home` and `/Users` only; adjust them if home directories are elsewhere or your users set `XDG_DATA_HOME`. |
| Record format | One JSON object per line. No multi-line handling is needed. |
| Timestamp | The `ts` field: RFC 3339, UTC, millisecond precision. |
| Start position | Beginning of file, so records written before the agent was installed are collected. |
| Rotation | Rotation renames the active file and starts a new `audit.jsonl`. The agent must track files by identity and not by name, so that a renamed file is not read a second time. Check how your agent handles renamed files. |
| Unknown fields | Keep them. New fields and event types can be added without a `schema_version` change. |

> [!WARNING]
>
> The audit directory and files are readable only by the user who owns them, and Devin resets these permissions each time it opens the log. Run the collection agent as root or as the user. Do not change the file permissions to give another account access; the change does not persist.

Each user's workstation keeps the active file plus at most 10 rotated files of 10 MiB each, about 110 MiB in total. Records older than that are deleted, so a workstation that produces more than this between collections loses its oldest records.

To confirm collection is working, start and end a Devin session on a workstation, then search your SIEM for a `session_start` and a `session_end` record with the same `session_id`.

#### Example: Splunk universal forwarder

Deploy these two files in an app on each forwarder (for example `$SPLUNK_HOME/etc/apps/devin_audit/local/`). Use only the `monitor` stanza that matches the workstation's operating system, and replace `devin` with the index you want to use.

```ini inputs.conf theme={null}
[monitor:///home/*/.local/share/devin/cli-federal/audit/audit*.jsonl]
sourcetype = devin:airgap:audit
index = devin
disabled = 0

[monitor:///Users/*/.local/share/devin/cli-federal/audit/audit*.jsonl]
sourcetype = devin:airgap:audit
index = devin
disabled = 0
```

```ini props.conf theme={null}
[devin:airgap:audit]
INDEXED_EXTRACTIONS = json
KV_MODE = none
SHOULD_LINEMERGE = false
TIMESTAMP_FIELDS = ts
TIME_FORMAT = %Y-%m-%dT%H:%M:%S.%3NZ
TZ = UTC
```

`props.conf` must be on the forwarder, not only on the indexers, because `INDEXED_EXTRACTIONS` is applied where the file is read. Leave `crcSalt` unset: the forwarder then recognizes a rotated file by its content and does not read it again under its new name. Every directory in the wildcard path must be readable by the account the forwarder runs as; a forwarder installed as a non-root service account cannot read the owner-only audit directories.

Fields arrive as indexed fields with their JSON names, so `actor.sso_user`, `model`, and `input_tokens` are searchable directly:

```text theme={null}
index=devin sourcetype="devin:airgap:audit" event=model_call | stats sum(input_tokens) sum(output_tokens) by model
```

#### Example: Elastic (Filebeat or Elastic Agent)

Add this `filestream` input to `filebeat.yml`. Keep only the path for the workstation's operating system.

```yaml filebeat.yml theme={null}
filebeat.inputs:
  - type: filestream
    id: devin-airgap-audit
    paths:
      - /home/*/.local/share/devin/cli-federal/audit/audit*.jsonl
      - /Users/*/.local/share/devin/cli-federal/audit/audit*.jsonl
    prospector.scanner.fingerprint.length: 64
    parsers:
      - ndjson:
          target: devin
          add_error_key: true
    processors:
      - timestamp:
          field: devin.ts
          layouts:
            - "2006-01-02T15:04:05.999Z"
```

The `ndjson` parser puts every record under a `devin.` prefix (`devin.event`, `devin.session_id`, `devin.actor.sso_user`). Do not set `target: ""`: the record's `event` field is a string, which conflicts with the `event` object in the Elastic Common Schema and makes Elasticsearch reject the documents. The `timestamp` processor copies `ts` into `@timestamp`; Elastic marks this processor as beta, so if you prefer, drop it and set `@timestamp` from `devin.ts` in an ingest pipeline instead. The `id` must be unique among your `filestream` inputs. Filebeat's default file identity is a fingerprint of the start of the file, which follows a file across the rename that rotation performs, so no rotation setting is needed. The fingerprint length is lowered from the default 1024 bytes to 64 because Filebeat 9.0 to 9.4 ignores a file until it is at least that long, which would skip a workstation with only a few records. The first 64 bytes of an audit file contain the first record's `ts` and `pid`, which keeps fingerprints distinct. Changing the length later makes Filebeat read every file again.

With Elastic Agent, add the Custom Logs (Filestream) integration to the agent policy, enter the same paths, and put the `parsers` and `processors` blocks above in the integration's custom configuration field.

> [!NOTE]
>
> Cognition has not run these examples against a Splunk or Elastic deployment. Validate them in your environment, and check the vendor documentation for your installed version before deploying them to workstations.

Useful fields to index or search on:

| Question | Fields |
| - | - |
| Who used Devin, and when | `event` = `session_start`, with `actor.sso_user` (or `actor.os_user`) and `ts` |
| Which version is deployed | `cli_version` on `session_start` |
| Token usage by model | `event` = `model_call`, with `model`, `input_tokens`, `output_tokens` |
| Failed tool calls | `event` = `tool_call`, with `success` = `false` |
| Refused sign-ins | `event` = `auth`, with `action` = `sso_login_denied` and `error_code` |

Only `session_start` and `auth` records name the user. To attribute `tool_call`, `file_read`, `file_write`, and `model_call` records to a user, join them to the `session_start` record with the same `session_id`.

## Diagnostics report

`devin diagnostics` writes a redacted, plain-text report you can review before sending it to Cognition support. It contains only versions, configuration shape (whether each file exists and parses), model names, whether each API-key variable is set, and audit-log statistics; it never contains prompts, code, file contents, secret values, endpoint URLs, or environment-variable values.

```sh theme={null}
devin diagnostics
```

```text theme={null}
Diagnostics written to devin-diagnostics-20260127T234500Z.txt
The report is plain text and stays on this machine; review it before sharing.
```

Use `--output <path>` to choose the file name. The report is written owner-only (`0600`). See [Troubleshooting](/federal/air-gapped/troubleshooting) for what to include in a support request.


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