> This page is for version v0.1.3.
> For other versions, use one of these documentation indexes:
> - Latest (v0.1.3) (default): https://docs.nvidia.com/openshell/latest/llms.txt
> - Dev: https://docs.nvidia.com/openshell/dev/llms.txt
> - v0.1.3: https://docs.nvidia.com/openshell/v0.1.3/llms.txt
> - v0.1.2: https://docs.nvidia.com/openshell/v0.1.2/llms.txt
> - v0.1.1: https://docs.nvidia.com/openshell/v0.1.1/llms.txt
> - v0.1.0: https://docs.nvidia.com/openshell/v0.1.0/llms.txt
> - v0.0.116: https://docs.nvidia.com/openshell/v0.0.116/llms.txt

> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.nvidia.com/openshell/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.nvidia.com/openshell/_mcp/server.

# OCSF JSON Export

> How to enable full OCSF JSON logging for SIEM integration, compliance, and structured analysis. Includes schema version downgrade for AWS Security Lake, Splunk, and CrowdStrike compatibility.

The [shorthand log format](/observability/logging) is optimized for humans and agents reading logs in real time. For machine consumption or SIEM integration, enable OCSF JSON output: one native JSON object per record. Collection is best-effort, not a guarantee of complete audit history.

## Gateway Output

Configure `[openshell.gateway.ocsf_log]` in `gateway.toml` to collect gateway-origin OCSF events into one JSONL file. This includes gateway-wide events such as TLS certificate reloads, even when no sandbox log stream applies. Collection preserves native OCSF fields and event IDs and does not depend on `RUST_LOG`.

The gateway writes OCSF events to a local JSONL file. Give each replica its own path and configure the shipper to follow renamed rotation segments. See [OCSF JSONL configuration](/how-it-works/gateways/configuration#ocsf-jsonl-output) for queue bounds, retention, loss metrics, and best-effort delivery limits.

This output is separate from the supervisor stderr and file output described below. Enabling one does not enable the other. The gateway JSONL sink does not archive supervisor log lines pushed to the gateway; those lines carry OCSF shorthand text, not the full structured record.

Gateway records use the configured gateway name for both `device.uid` and `device.name`. Replicas of one installation share that identity; `device.hostname` identifies the emitting replica. Assign distinct gateway names to installations whose events are collected together. Reusing a name, including the default name, groups those installations under the same identity. Renaming a gateway changes its audit identity.

Without JSONL enabled, gateway OCSF events still appear as shorthand in console output, subject to the diagnostic log filter. Events associated with a sandbox also appear in its gateway log stream; gateway-wide events have no sandbox association.

## Enable Sandbox JSON Export

Use the `ocsf_json_enabled` setting to toggle JSON export. The setting can be applied globally, for all sandboxes, or per-sandbox.

Global:

```shell
openshell settings set --global --key ocsf_json_enabled --value true
```

Per-sandbox:

```shell
openshell settings set my-sandbox --key ocsf_json_enabled --value true
```

The setting takes effect on the next poll cycle, by default every 10 seconds. No sandbox restart is required.

To disable:

```shell
openshell settings set --global --key ocsf_json_enabled --value false
```

### Windows MXC gateways

MXC audit events originate in the Windows gateway rather than an in-sandbox
supervisor. Enable the MXC ETW consumer in `gateway.toml`, then explicitly opt
the gateway into durable JSONL output:

```toml
[openshell.drivers.mxc]
etw_audit = true
```

```powershell
$env:OPENSHELL_OCSF_JSON = "1"
# Optional; defaults to %PROGRAMDATA%\OpenShell\logs.
$env:OPENSHELL_OCSF_LOG_DIR = "D:\OpenShell\audit"
openshell-gateway --drivers mxc --config gateway.toml
```

The sink is available only when Windows selects the MXC driver. It rotates
daily and retains the three most recent files. Diagnostic `--log-level` values
do not suppress records in this explicitly enabled audit sink.

## Output Location

When `ocsf_json_enabled=true`, sandbox supervisors emit full OCSF JSON to stderr alongside shorthand output:

```text
2026-10-07T23:21:19.000Z OCSF NET:OPEN [MED] DENIED ...
2026-10-07T23:21:19.000Z OCSF-JSON {"class_uid":4001,...}
```

The example payload is abbreviated. Each actual `OCSF-JSON` line contains one complete compact JSON object. Configure your collector to match the `OCSF-JSON` token after the UTC timestamp and decode everything after the following space as JSON. Select these lines to avoid ingesting the shorthand copy of each event. The JSON preserves the original `metadata.uid` and follows `ocsf_schema_version`; with that setting unset, it uses the full native schema.

Collect stderr through the supervisor's platform log interface:

```shell
docker logs --follow <supervisor-container>
podman logs --follow <supervisor-container>
kubectl -n <sandbox-namespace> logs <supervisor-pod> -c supervisor --follow
```

JSON stderr output works independently of writable log storage and the diagnostic log filter. Disabling the setting stops new JSON records. The console writer uses a bounded queue of 1,024 complete lines shared by diagnostics, shorthand, and JSON; when the queue is full, new lines are dropped rather than blocking enforcement. Delivery is best-effort, and retention depends on your runtime and external collector. The gateway log API and `openshell logs` continue to return shorthand.

When file logging is available, supervisors also write plain JSONL to `/var/log/openshell-ocsf.YYYY-MM-DD.log` in their own filesystem, separate from the workload. `openshell sandbox exec` cannot read those files. File output requires a writable `/var/log`; a missing file sink does not disable JSON stderr output.

This file contains supervisor-originated events, not the [sandbox-runtime events](/observability/accessing-logs#main-process-output) emitted into workload container logs.

Supervisor files rotate daily with a configured three-file limit, but cleanup can remove JSONL history earlier. See [supervisor file retention](/observability/logging#log-file-location) for the limits. Docker and Podman store them on tmpfs; Kubernetes stores them in the supervisor pod's `emptyDir`. For durable retention, arrange external collection before rotation or teardown. See [supervisor file access](/observability/accessing-logs#direct-filesystem-access) for collection paths and best-effort limits.

Windows MXC gateway records use `%PROGRAMDATA%\OpenShell\logs` or `OPENSHELL_OCSF_LOG_DIR`. That sink also rotates daily and retains the three most recent files. The separate `[openshell.gateway.ocsf_log]` sink uses its configured path and retention.

## JSON Record Structure

Gateway-produced records use the `OpenShell Gateway` product identity; supervisor records use `OpenShell Sandbox Supervisor`. The producing device and the affected sandbox are separate identities, so a gateway event can still identify an affected sandbox through its container fields.

For gateway-produced records, `device.os.name` identifies the OS running the gateway process: Linux, Windows, or macOS. Linux supervisor records continue to report Linux, regardless of the gateway's OS. A native macOS gateway therefore reports macOS while its Linux sandboxes report Linux; a gateway running inside a Linux container reports Linux, even on a Mac host.

`metadata.uid` uniquely identifies an event and stays unchanged when that record is serialized again. Use `container.uid` to associate the event with its sandbox, not `metadata.uid`. Records without a sandbox association omit the container; unknown images are omitted rather than represented by an empty image name.

Each JSONL file line, or the payload after the stderr marker, is a complete OCSF v1.8.0 JSON object when no downgrade is configured. Here is an example of a network connection event:

```json
{
  "class_uid": 4001,
  "class_name": "Network Activity",
  "category_uid": 4,
  "category_name": "Network Activity",
  "activity_id": 1,
  "activity_name": "Open",
  "severity_id": 1,
  "severity": "Informational",
  "status_id": 1,
  "status": "Success",
  "time": 1775014138811,
  "message": "CONNECT allowed api.github.com:443",
  "metadata": {
    "product": {
      "name": "OpenShell Sandbox Supervisor",
      "vendor_name": "NVIDIA",
      "version": "0.3.0"
    },
    "version": "1.8.0"
  },
  "action_id": 1,
  "action": "Allowed",
  "disposition_id": 1,
  "disposition": "Allowed",
  "dst_endpoint": {
    "domain": "api.github.com",
    "port": 443
  },
  "src_endpoint": {
    "ip": "10.42.0.31",
    "port": 37494
  },
  "actor": {
    "process": {
      "name": "/usr/bin/curl",
      "pid": 57
    }
  },
  "firewall_rule": {
    "name": "github_api",
    "type": "opa"
  }
}
```

And a denied connection:

```json
{
  "class_uid": 4001,
  "class_name": "Network Activity",
  "activity_id": 1,
  "activity_name": "Open",
  "severity_id": 3,
  "severity": "Medium",
  "status_id": 2,
  "status": "Failure",
  "action_id": 2,
  "action": "Denied",
  "disposition_id": 2,
  "disposition": "Blocked",
  "status_detail": "no matching policy",
  "message": "CONNECT denied httpbin.org:443",
  "dst_endpoint": {
    "domain": "httpbin.org",
    "port": 443
  },
  "actor": {
    "process": {
      "name": "/usr/bin/curl",
      "pid": 63
    }
  },
  "firewall_rule": {
    "name": "-",
    "type": "opa"
  }
}
```

> **Note**
>
> The JSON examples above are formatted for readability. The actual JSONL file contains one JSON object per line with no whitespace formatting.

## OCSF Event Classes in JSON

The `class_uid` field identifies the event type:

| `class_uid` | Class                      | Shorthand prefix |
| ----------- | -------------------------- | ---------------- |
| 0           | Base Event                 | `EVENT`          |
| 4001        | Network Activity           | `NET:`           |
| 4002        | HTTP Activity              | `HTTP:`          |
| 4007        | SSH Activity               | `SSH:`           |
| 1007        | Process Activity           | `PROC:`          |
| 2004        | Detection Finding          | `FINDING:`       |
| 5019        | Device Config State Change | `CONFIG:`        |
| 6002        | Application Lifecycle      | `LIFECYCLE:`     |

HTTP Activity records include request or response details. Rejections that have
parsed a request, including early credential-binding failures, include safe
method-only request context and the generated response.

Connection-error records include the known peer or listening endpoint.

Policy configuration warnings use Device Config State Change.

Unix socket relay and relay-control notifications, and proxy or mediation
failures without an observed network endpoint, use Base Event (`class_uid: 0`).
Include these classes when filtering supervisor operational events. Relay-control
records that previously appeared as `NET:*` now appear as `EVENT`.

## SIEM Schema Version Compatibility

OpenShell emits OCSF v1.8.0 events internally, but many SIEMs only support older schema versions. The `ocsf_schema_version` setting tells the JSONL layer to downgrade events before writing, stripping fields and profiles that don't exist in the target version.

For gateway JSONL, set `schema_version` on the output destination. The target applies uniformly to every record written to that file and requires a gateway restart:

```toml
[openshell.gateway.ocsf_log]
path = "/var/log/openshell/gateway-ocsf.jsonl"
schema_version = "1.3"
```

The sandbox-scoped setting below controls the legacy supervisor-local JSONL file only.

Set the target version globally:

```shell
openshell settings set --global --key ocsf_schema_version --value "1.1"
```

Or per-sandbox:

```shell
openshell settings set my-sandbox --key ocsf_schema_version --value "1.3"
```

The setting takes effect on the next poll cycle, by default every 10 seconds. No sandbox restart is required.

Supported target versions are `1.1` and `1.3`. When set, the JSONL layer applies the following transformations:

* Strips fields added after the target version: `ai_model`, `container`, `observation_point_id`
* Removes unknown profile names from `metadata.profiles`: `ai_operation`, `container`
* Rewrites `metadata.version` to match the target version

The shorthand log output is unaffected. The internal event model stays at v1.8.0; only the serialized JSONL is transformed.

When unset or empty, no downgrade is applied and events are written at the current schema version.

| SIEM               | Required OCSF Version | `ocsf_schema_version` Value |
| ------------------ | --------------------- | --------------------------- |
| AWS Security Lake  | v1.1.0                | `1.1`                       |
| Splunk CIM Add-On  | v1.1-v1.3             | `1.1` or `1.3`              |
| CrowdStrike FDR    | v1.5.0                | Not yet supported           |
| Datadog Cloud SIEM | v1.5.0 (selectable)   | Not yet supported           |

> **Warning**
>
> Downgrading is lossy. The `ai_model` field and `ai_operation` profile are stripped for v1.1 and v1.3 targets, so AI model attribution is not present in downgraded events. If your audit workflow requires knowing which model handled a request, use the full schema version (leave `ocsf_schema_version` unset). OpenShell supports OCSF v1.8.0 natively; upgrade your SIEM's OCSF schema if you need AI attribution alongside backward-compatible ingestion.

Downgraded events include a `downgraded_from` marker in the `unmapped` object so auditors can distinguish "no model was involved" from "model attribution was stripped":

```json
{
  "unmapped": {
    "downgraded_from": "1.8.0"
  }
}
```

The core OCSF event structure (class UIDs, activity IDs, network and HTTP fields) is identical across v1.1 through v1.8. The stripped fields are all profile-gated additions.

## Integration with External Tools

The JSONL file can be shipped to any tool that accepts OCSF-formatted data:

| Tool                 | Integration Path                                                                                                                                                  |
| -------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Splunk               | Use the [Splunk OCSF Add-on](https://splunkbase.splunk.com/app/6943) to ingest OCSF JSONL files. Set `ocsf_schema_version` to `1.3` for CIM Add-On compatibility. |
| Amazon Security Lake | OCSF is the native schema for Security Lake. Set `ocsf_schema_version` to `1.1` for v1.1.0 compatibility.                                                         |
| CrowdStrike FDR      | Ship JSONL files to Falcon Data Replicator. v1.5 downgrade target is not yet supported.                                                                           |
| Elastic              | Use Filebeat to ship JSONL files with the OCSF field mappings.                                                                                                    |
| Custom pipelines     | Parse the JSONL file with `jq`, Python, or any JSON-capable tool.                                                                                                 |

After [copying supervisor files](/observability/accessing-logs#direct-filesystem-access) into `./supervisor-logs`, use `jq` to extract all denied connections:

```shell
jq -c 'select(.action == "Denied")' ./supervisor-logs/openshell-ocsf.*.log
```

## Relationship to Shorthand Logs

Sandbox events identify the sandbox environment with `device.type_id: 99` (Other) and `device.type: "Sandbox"`. The operating system is reported separately in `device.os.name`; the device type does not classify the underlying host or compute backend.

The shorthand format in `openshell.YYYY-MM-DD.log` and the JSON format in `openshell-ocsf.YYYY-MM-DD.log` are derived from the same OCSF events. The shorthand is a human-readable projection; the JSON is the full structured record when no schema downgrade is configured. When `ocsf_schema_version` is set, the JSON export is a lossy projection of the internal event model. Both formats are generated at the same time from the same event data.

Shorthand stderr output follows the diagnostic log filter; its local file is available when file logging starts successfully. JSON stderr and file export are opt-in through `ocsf_json_enabled`. File export requires the supervisor's JSONL sink to start successfully; stderr export is independent of that sink.

## Next Steps

* Learn how to [read the shorthand format](/observability/logging) for real-time monitoring.
* Refer to the [OCSF specification](https://schema.ocsf.io/) for the full schema reference.